[mapguide-commits] r9757 - in sandbox/jng/vanilla_swig: . Bindings Bindings/src Bindings/src/Bindings Bindings/src/Bindings/Common Bindings/src/Bindings/Common/DotNet Bindings/src/Bindings/Common/Java Bindings/src/Bindings/Common/Php Bindings/src/Bindings/DotNet Bindings/src/Bindings/Java Bindings/src/Bindings/Php Bindings/src/Bindings/mapadmin Bindings/src/Bindings/mapviewerphp Bindings/src/IMake Bindings/src/Managed Bindings/src/Managed/DotNet Bindings/src/Managed/DotNet/MapGuideDotNetApi Bindings/src/Managed/DotNet/MapGuideDotNetApi/Properties Bindings/src/Managed/DotNet/MapGuideDotNetApi/build Bindings/src/Managed/DotNet/MapGuideDotNetApi/build/net461 Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/ubuntu.14.04-x64 Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/ubuntu.14.04-x64/native Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x64 Bindings/src/Managed/D otNet/MapGuideDotNetApi/runtimes/win-x64/native Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x86 Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x86/native Bindings/src/Managed/Java Bindings/src/Managed/Java/org Bindings/src/Managed/Java/org/osgeo Bindings/src/Managed/Java/org/osgeo/mapguide Bindings/src/Test Bindings/src/Test/DotNet Bindings/src/Test/DotNet/src Bindings/src/Test/DotNet/src/TestCommon Bindings/src/Test/DotNet/src/TestCommon/ExternalTests Bindings/src/Test/DotNet/src/TestCommon/FeatureService Bindings/src/Test/DotNet/src/TestCommon/MapLayer Bindings/src/Test/DotNet/src/TestCommon/Properties Bindings/src/Test/DotNet/src/TestCommon/ResourceService Bindings/src/Test/DotNet/src/TestCommonFull Bindings/src/Test/DotNet/src/TestCommonFull/Properties Bindings/src/Test/DotNet/src/TestMapGuideApi Bindings/src/Test/DotNet/src/TestMapGuideApi/DrawingService Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests Bindings/src/Test/DotNet/src/TestMapGui deApi/MappingService Bindings/src/Test/DotNet/src/TestMapGuideApi/Properties Bindings/src/Test/DotNet/src/TestMapGuideApi/RenderingService Bindings/src/Test/DotNet/src/TestMapGuideApi/ServerAdmin Bindings/src/Test/DotNet/src/TestMapGuideApi/SiteService Bindings/src/Test/DotNet/src/TestMapGuideApi/WebLayout Bindings/src/Test/DotNet/src/TestMapGuideApiFull Bindings/src/Test/DotNet/src/TestMapGuideApiFull/Properties Bindings/src/Test/DotNet/src/TestMisc Bindings/src/Test/DotNet/src/TestMiscFull Bindings/src/Test/DotNet/src/TestMiscFull/Properties Bindings/src/Test/DotNet/src/TestRunner Bindings/src/Test/DotNet/src/TestRunner/Properties Bindings/src/Test/DotNet/src/TestRunnerFull Bindings/src/Test/DotNet/src/TestRunnerFull/Properties Bindings/src/Test/Java Bindings/src/Test/Java/extlib Bindings/src/Test/Java/src Bindings/src/Test/Java/src/org Bindings/src/Test/Java/src/org/osgeo Bindings/src/Test/Java/src/org/osgeo/mapguide Bindings/src/Test/Java/src/org/osgeo/mapguide/test Bindings/src /Test/Java/src/org/osgeo/mapguide/test/common Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource Bindings/src/Test/Php Bindings/src/Test/Php/MapAgentShim Bindings/src/Tools Bindings/src/Tools/ClassMapGen Bindings/src/Tools/ClassMapGen/Data Bindings/src/Tools/ClassMapGen/Data/Templates Bindings/src/Tools/Common Bindings/src/Tools/PhpPostProcess Bindings/src/Tools/StampVer Bindings/src/Tools/SwigPrepare

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Fri Nov 6 08:04:15 PST 2020


Author: jng
Date: 2020-11-06 08:04:14 -0800 (Fri, 06 Nov 2020)
New Revision: 9757

Added:
   sandbox/jng/vanilla_swig/Bindings/
   sandbox/jng/vanilla_swig/Bindings/src/
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Bindings.sln
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/CMakeLists.txt
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/coreclr_compat.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/custom.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/string.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/sugar.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/exception.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/extensions.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/monkey_patch.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/exception.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/monkey_patch.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/pointer.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/refcount.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/CMakeLists.txt
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/DotNetUnmanagedApi.rc
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/DotNetUnmanagedApi.vcxproj
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/InitializeWebTier.cpp
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/Makefile
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/README.md
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/dotnetcore.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/CMakeLists.txt
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/COPYRIGHT
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/InitializeWebTier.cpp
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/JavaApi.rc
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/JavaApi.vcxproj
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/Makefile.am
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/README.md
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/catchall.code
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/getclassid.code
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/java.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/CMakeLists.txt
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/COPYRIGHT
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/InitializeWebTier.cpp
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/Makefile.am
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpApi.rc
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpApi.vcxproj
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpClassMap.cpp
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpLocalizer.cpp
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/README.md
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/catchall.code
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/getclassid.code
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/php.i
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/mapadmin/
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/mapadmin/README.txt
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/mapviewerphp/
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/mapviewerphp/README.txt
   sandbox/jng/vanilla_swig/Bindings/src/CMakeLists.txt
   sandbox/jng/vanilla_swig/Bindings/src/IMake/
   sandbox/jng/vanilla_swig/Bindings/src/IMake/CMakeLists.txt
   sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.cpp
   sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.sln
   sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.vcxproj
   sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.xsd
   sandbox/jng/vanilla_swig/Bindings/src/IMake/Makefile
   sandbox/jng/vanilla_swig/Bindings/src/IMake/SimpleXmlParser.cpp
   sandbox/jng/vanilla_swig/Bindings/src/IMake/SimpleXmlParser.h
   sandbox/jng/vanilla_swig/Bindings/src/IMake/stdafx.cpp
   sandbox/jng/vanilla_swig/Bindings/src/IMake/stdafx.h
   sandbox/jng/vanilla_swig/Bindings/src/Managed/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/MapGuideDotNetApi.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/Properties/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/Properties/AssemblyInfo.cs
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/build/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/build/net461/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/build/net461/MapGuideDotNetApi.targets
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/EntryPoint.cs
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/ManagedException.cs
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/MgClassMap.cs
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/MgObjectFactory.cs
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/MgStringCollection.cs
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/Streams.cs
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/ubuntu.14.04-x64/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/ubuntu.14.04-x64/native/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/ubuntu.14.04-x64/native/readme.txt
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x64/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x64/native/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x64/native/readme.txt
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x86/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x86/native/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x86/native/readme.txt
   sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/
   sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/AppThrowable.java
   sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/MgBase64.java
   sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/MgLocalizer.java
   sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/ObjectFactory.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/DotNet.sln
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/FullFramework.sln
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ApiTypes.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Assert.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/CommonTests.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/CommonUtility.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/ByteReaderTest.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/CollectionTests.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/ConvenienceTests.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/PropertiesTest.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/FeatureService/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/FeatureService/FeatureServiceOperationExecutor.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/FeatureService/Operations.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ITestExecutor.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ITestExecutorCollection.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ITestLogger.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/MapLayer/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/MapLayer/MapLayerOperationExecutor.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/MapLayer/Operations.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/PlatformApiTestExecutor.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/PlatformResources.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Properties/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Properties/AssemblyInfo.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ResourceService/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ResourceService/Operations.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ResourceService/ResourceServiceOperationExecutor.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Sqlite.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestCommon.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestDataRoot.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestExecutionRun.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestExecutorBase.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestResult.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/UnitTestException.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommonFull/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommonFull/Properties/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommonFull/Properties/AssemblyInfo.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommonFull/TestCommonFull.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/DrawingService/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/DrawingService/DrawingServiceOperationExecutor.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/DrawingService/Operations.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/CollectionTests.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/MapTests.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/RenderingServiceTests.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/ResourceServiceTests.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MapGuideResources.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MapGuideTestExecutorCollection.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MapGuideTests.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MappingService/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MappingService/MappingServiceOperationExecutor.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/Properties/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/Properties/AssemblyInfo.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/RenderingService/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/RenderingService/RenderingServiceOperationExecutor.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ServerAdmin/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ServerAdmin/Operations.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ServerAdmin/ServerAdminOperationExecutor.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/SiteService/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/SiteService/Operations.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/SiteService/SiteServiceOperationExecutor.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/TestMapGuideApi.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/WebLayout/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/WebLayout/Operations.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/WebLayout/WebLayoutOperationExecutor.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApiFull/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApiFull/Properties/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApiFull/Properties/AssemblyInfo.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApiFull/TestMapGuideApiFull.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/Program.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/TestMisc.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMiscFull/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMiscFull/Properties/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMiscFull/Properties/AssemblyInfo.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMiscFull/TestMiscFull.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Program.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Properties/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Properties/AssemblyInfo.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Properties/launchSettings.json
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/TestRunner.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/App.config
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/Properties/
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/Properties/AssemblyInfo.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/TestRunnerFull.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/README.txt
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/build.xml
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java-win32-x64.dll
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java-win32-x86.dll
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java.jar
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/ApiTypes.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/CommonTests.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/Console.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/DebugSettings.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/MapGuideTestExecutorCollection.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/MapGuideTests.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/PlatformFactory.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/Program.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/Assert.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/AssertException.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/CommonUtility.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/IExternalTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/IPlatformFactory.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/ITestExecutor.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/ITestExecutorCollection.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/ITestLogger.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/MissingTestExecutorException.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/MutableInteger.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/PlatformApiTestExecutor.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestExecutionRun.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestExecutorBase.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestExecutorCollectionBase.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestLoggerFile.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestResult.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/UnitTestException.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/XmlCompareResult.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/ReadOnlyLayerCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/RenderingServiceTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/ClearLog.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/DeleteLog.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/DeletePackage.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/EnumeratePackages.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetLog.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetLogByDate.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetPackageLog.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetPackageStatus.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/LoadPackage.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/Offline.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/Online.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/RenameLog.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/ServerAdminOperationExecutor.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/DescribeDrawing.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/DrawingServiceOperationExecutor.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/EnumerateDrawingLayers.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/EnumerateDrawingSectionResources.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/EnumerateDrawingSections.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawing.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawingLayer.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawingSection.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawingSectionResource.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/AddGroup.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/AddServer.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/AddUser.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/CreateSession.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/DeleteGroups.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/DeleteUsers.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/DestroySession.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateGroups.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateGroups2.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateRoles.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateRoles2.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateServers.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateUsers.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GetUserForSession.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GrantGroupMembershipsToUsers.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GrantRoleMembershipsToGroups.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GrantRoleMembershipsToUsers.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RemoveServer.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RevokeGroupMembershipsFromUsers.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RevokeRoleMembershipsFromGroups.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RevokeRoleMembershipsFromUsers.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/SiteServiceOperationExecutor.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/UpdateGroup.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/UpdateServer.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/UpdateUser.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_BackDescription.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_BackTooltip.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ForwardDescription.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ForwardTooltip.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetCenter.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetInformationPaneWidth.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetInitialTaskUrl.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetMapDefinition.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetScale.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetTaskPaneWidth.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetTitle.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_HomeDescription.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_HomeTooltip.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowContextMenu.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowLegend.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowProperties.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowStatusbar.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowTaskbar.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowTaskpane.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowToolbar.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TasksDescription.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TasksName.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TasksTooltip.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TestUiItem.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WebLayoutOperationExecutor.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IApplySession.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IMapCreator.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IMapGuideSession.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/ISessionCreator.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IWebLayoutCreator.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/BatchPropertyCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderMemoryConstructorTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderStringConstructorTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderTestData.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ClassDefinitionCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CoordinateCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurvePolygonCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurveRingCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurveSegmentCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurveStringCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/FeatureCommandCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/FeatureSchemaCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/GeomBuild.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/GeometryCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/IntCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LayerCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LayerGroupCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LineStringCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LinearRingCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/MapCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/PropertyCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/PropertyDefinitionCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/StringCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/StringPropertyCollectionTest.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/DescribeFeatureSchema.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/ExecuteSqlQuery.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/FeatureServiceOperationExecutor.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetClasses.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetConnectionPropertyValues.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetFeatureProviders.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetLongTransactions.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetProviderCapabilities.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetSchemas.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetSpatialContexts.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/SelectAggregates.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/SelectFeatures.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/SetLongTransaction.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/TestConnection.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/AddLayer.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/AddLayerGroup.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetCoordinateSystem.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetDataExtent.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetDisplayInLegend.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetGroups.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerDefinition.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerFeatureClass.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerFeatureSource.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerVisibility.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayers.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLegendLabel.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetMapExtent.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetMapName.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetViewCenter.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetViewScale.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/HideGroup.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/IsLayerVisible.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/LayerExists.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/MapLayerOperationExecutor.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/RemoveGroup.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/ShowGroup.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/ApplyResourcePackage.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/ChangeResourceOwner.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/CopyResource.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/DeleteResource.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/DeleteResourceData.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/EnumerateResourceData.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/EnumerateResourceReferences.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/EnumerateResources.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetRepositoryContent.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetRepositoryHeader.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetResourceContent.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetResourceData.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetResourceHeader.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/InheritPermissionsFrom.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/MoveResource.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/RenameResourceData.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/ResourceServiceOperationExecutor.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/SetResource.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/SetResourceData.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/UpdateRepository.java
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ApiEntUnitTests.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ApiUnitTests.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/DrawingServiceAPI.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/DrawingServiceHttpRequests.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ExecuteOperation.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/FeatureServiceAPI.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/FeatureServiceHttpRequests.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HtmlPrinter.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HttpEntUnitTests.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HttpRequest.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HttpUnitTests.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/LocaleTest.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MapAgentShim/
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MapAgentShim/index.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MapLayerAPI.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MappingServiceAPI.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MappingServiceHttpRequests.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Menu.html
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/NewSdf.MapDefinition
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/NewSdfInt.LayerDefinition
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/NewSdfString.LayerDefinition
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/PhpUnitTests.zpj
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ResourceServiceAPI.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ResourceServiceHttpRequests.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Result.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Run.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/RunTests.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/SQLiteEngine.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ServerAdminAPI.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/SiteServiceAPI.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/SiteServiceHttpRequests.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Test1.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/UpdateDumpFiles.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Utils.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Validate.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ValidateUtils.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/WebLayoutAPI.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/WfsHttpRequests.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/WmsHttpRequests.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/createSdf.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/createSdfUnicode.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/index.html
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/master.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/setResourceData.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/sortXml.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/sqlite_constants.php
   sandbox/jng/vanilla_swig/Bindings/src/Test/Php/testAwSelection.php
   sandbox/jng/vanilla_swig/Bindings/src/Tools/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/CMakeLists.txt
   sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/ClassMapGen.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/README.txt
   sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/dotnet.txt
   sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/java.txt
   sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/php.txt
   sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/classmap_master.json
   sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Program.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/Common/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/Common/helpers.h
   sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/CMakeLists.txt
   sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/PhpPostProcess.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/Program.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/main.cpp
   sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/CMakeLists.txt
   sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/Program.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/StampVer.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/main.cpp
   sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/CMakeLists.txt
   sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/Program.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/SwigPrepare.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/main.cpp
   sandbox/jng/vanilla_swig/Bindings/src/Tools/Tools.sln
Log:
Add current vanilla SWIG binding source from GitHub

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Bindings.sln
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Bindings.sln	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Bindings.sln	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,161 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26430.13
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DotNetUnmanagedApi", "DotNet\DotNetUnmanagedApi.vcxproj", "{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}"
+	ProjectSection(ProjectDependencies) = postProject
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C} = {B601F04C-0D42-4AFC-A092-B31185E2EA8C}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IMake", "..\IMake\IMake.vcxproj", "{B601F04C-0D42-4AFC-A092-B31185E2EA8C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8B6FBCB3-F11C-4AB9-AEE5-6A272E31EA7F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".net API", ".net API", "{A9257857-9844-4CEA-AF02-A7D8AB8F46AB}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{99132700-5CAD-4540-9939-7502C1A448EA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PHP API", "PHP API", "{323AEA7C-774B-49D8-8D34-7A766288C549}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PhpApi", "Php\PhpApi.vcxproj", "{696D2664-D17F-4357-8A19-2B5DC4B29962}"
+	ProjectSection(ProjectDependencies) = postProject
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C} = {B601F04C-0D42-4AFC-A092-B31185E2EA8C}
+	EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Java API", "Java API", "{664329B8-67BD-4D38-AE4B-A2920B038C09}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JavaApi", "Java\JavaApi.vcxproj", "{C02F44CB-D916-428D-A1A5-04E225A2A4EC}"
+	ProjectSection(ProjectDependencies) = postProject
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C} = {B601F04C-0D42-4AFC-A092-B31185E2EA8C}
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug_VC11|x64 = Debug_VC11|x64
+		Debug_VC11|x86 = Debug_VC11|x86
+		Debug_VC14|x64 = Debug_VC14|x64
+		Debug_VC14|x86 = Debug_VC14|x86
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Release_VC11|x64 = Release_VC11|x64
+		Release_VC11|x86 = Release_VC11|x86
+		Release_VC14|x64 = Release_VC14|x64
+		Release_VC14|x86 = Release_VC14|x86
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Debug_VC11|x64.ActiveCfg = Debug_VC11|x64
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Debug_VC11|x64.Build.0 = Debug_VC11|x64
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Debug_VC11|x86.ActiveCfg = Debug_VC11|Win32
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Debug_VC11|x86.Build.0 = Debug_VC11|Win32
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Debug_VC14|x64.ActiveCfg = Debug_VC14|x64
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Debug_VC14|x64.Build.0 = Debug_VC14|x64
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Debug_VC14|x86.ActiveCfg = Debug_VC14|Win32
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Debug_VC14|x86.Build.0 = Debug_VC14|Win32
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Debug|x64.ActiveCfg = Debug_VC14|x64
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Debug|x64.Build.0 = Debug_VC14|x64
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Debug|x86.ActiveCfg = Debug_VC14|Win32
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Debug|x86.Build.0 = Debug_VC14|Win32
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Release_VC11|x64.ActiveCfg = Release_VC11|x64
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Release_VC11|x64.Build.0 = Release_VC11|x64
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Release_VC11|x86.ActiveCfg = Release_VC11|Win32
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Release_VC11|x86.Build.0 = Release_VC11|Win32
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Release_VC14|x64.ActiveCfg = Release_VC14|x64
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Release_VC14|x64.Build.0 = Release_VC14|x64
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Release_VC14|x86.ActiveCfg = Release_VC14|Win32
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Release_VC14|x86.Build.0 = Release_VC14|Win32
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Release|x64.ActiveCfg = Release_VC14|x64
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Release|x64.Build.0 = Release_VC14|x64
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Release|x86.ActiveCfg = Release_VC14|Win32
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}.Release|x86.Build.0 = Release_VC14|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug_VC11|x64.ActiveCfg = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug_VC11|x64.Build.0 = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug_VC11|x64.Deploy.0 = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug_VC11|x86.ActiveCfg = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug_VC11|x86.Build.0 = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug_VC14|x64.ActiveCfg = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug_VC14|x64.Build.0 = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug_VC14|x64.Deploy.0 = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug_VC14|x86.ActiveCfg = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug_VC14|x86.Build.0 = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug|x64.ActiveCfg = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug|x86.ActiveCfg = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug|x86.Build.0 = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release_VC11|x64.ActiveCfg = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release_VC11|x64.Build.0 = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release_VC11|x64.Deploy.0 = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release_VC11|x86.ActiveCfg = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release_VC11|x86.Build.0 = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release_VC14|x64.ActiveCfg = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release_VC14|x64.Build.0 = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release_VC14|x64.Deploy.0 = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release_VC14|x86.ActiveCfg = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release_VC14|x86.Build.0 = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release_VC14|x86.Deploy.0 = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release|x64.ActiveCfg = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release|x86.ActiveCfg = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release|x86.Build.0 = Release|Win32
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Debug_VC11|x64.ActiveCfg = Debug|x64
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Debug_VC11|x64.Build.0 = Debug|x64
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Debug_VC11|x86.ActiveCfg = Debug|Win32
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Debug_VC11|x86.Build.0 = Debug|Win32
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Debug_VC14|x64.ActiveCfg = Debug|x64
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Debug_VC14|x64.Build.0 = Debug|x64
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Debug_VC14|x86.ActiveCfg = Debug|Win32
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Debug_VC14|x86.Build.0 = Debug|Win32
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Debug|x64.ActiveCfg = Debug|x64
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Debug|x64.Build.0 = Debug|x64
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Debug|x86.ActiveCfg = Debug|Win32
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Debug|x86.Build.0 = Debug|Win32
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Release_VC11|x64.ActiveCfg = Release|x64
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Release_VC11|x64.Build.0 = Release|x64
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Release_VC11|x86.ActiveCfg = Release|Win32
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Release_VC11|x86.Build.0 = Release|Win32
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Release_VC14|x64.ActiveCfg = Release|x64
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Release_VC14|x64.Build.0 = Release|x64
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Release_VC14|x86.ActiveCfg = Release|Win32
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Release_VC14|x86.Build.0 = Release|Win32
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Release|x64.ActiveCfg = Release|x64
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Release|x64.Build.0 = Release|x64
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Release|x86.ActiveCfg = Release|Win32
+		{696D2664-D17F-4357-8A19-2B5DC4B29962}.Release|x86.Build.0 = Release|Win32
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Debug_VC11|x64.ActiveCfg = Debug|x64
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Debug_VC11|x64.Build.0 = Debug|x64
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Debug_VC11|x86.ActiveCfg = Debug|Win32
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Debug_VC11|x86.Build.0 = Debug|Win32
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Debug_VC14|x64.ActiveCfg = Debug|x64
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Debug_VC14|x64.Build.0 = Debug|x64
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Debug_VC14|x86.ActiveCfg = Debug|Win32
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Debug_VC14|x86.Build.0 = Debug|Win32
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Debug|x64.ActiveCfg = Debug|x64
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Debug|x64.Build.0 = Debug|x64
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Debug|x86.ActiveCfg = Debug|Win32
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Debug|x86.Build.0 = Debug|Win32
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Release_VC11|x64.ActiveCfg = Release|x64
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Release_VC11|x64.Build.0 = Release|x64
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Release_VC11|x86.ActiveCfg = Release|Win32
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Release_VC11|x86.Build.0 = Release|Win32
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Release_VC14|x64.ActiveCfg = Release|x64
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Release_VC14|x64.Build.0 = Release|x64
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Release_VC14|x86.ActiveCfg = Release|Win32
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Release_VC14|x86.Build.0 = Release|Win32
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Release|x64.ActiveCfg = Release|x64
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Release|x64.Build.0 = Release|x64
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Release|x86.ActiveCfg = Release|Win32
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC}.Release|x86.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E} = {A9257857-9844-4CEA-AF02-A7D8AB8F46AB}
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C} = {99132700-5CAD-4540-9939-7502C1A448EA}
+		{696D2664-D17F-4357-8A19-2B5DC4B29962} = {323AEA7C-774B-49D8-8D34-7A766288C549}
+		{C02F44CB-D916-428D-A1A5-04E225A2A4EC} = {664329B8-67BD-4D38-AE4B-A2920B038C09}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {733BCCC2-0431-4AB7-ABB6-AAE81AB54C48}
+	EndGlobalSection
+EndGlobal

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/CMakeLists.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/CMakeLists.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/CMakeLists.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,12 @@
+if (WITH_DOTNET)
+    add_subdirectory(DotNet)
+endif (WITH_DOTNET)
+if (WITH_JAVA)
+    add_subdirectory(Java)
+endif (WITH_JAVA)
+if (WITH_PHP)
+    add_subdirectory(Php)
+endif (WITH_PHP)
+# Needed for api binding generation
+file(COPY "Common" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+file(COPY "MapGuideApi" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/coreclr_compat.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/coreclr_compat.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/coreclr_compat.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,243 @@
+/**
+ * coreclr_compat.i
+ *
+ * SWIG typemaps and macros for CoreCLR support
+ *
+ * NOTE: SWIG must be run with SWIG_CSHARP_NO_EXCEPTION_HELPER and SWIG_CSHARP_NO_STRING_HELPER defined
+ */
+
+/**
+ * .net reverse string marshaling
+ *
+ * For reasons unknown, the default SWIG string helper produces garbage from our returned strings. This is probably
+ * due to our APIs returning STL strings and lifetime issues that make it incompatible.
+ *
+ * We know the impl from our modified version works so this is that implementation
+ */
+%insert(runtime) %{
+/* Callback for returning strings to C# without leaking memory */
+typedef void * (SWIGSTDCALL* SWIG_CSharpMgStringHelperCallback)(int);
+static SWIG_CSharpMgStringHelperCallback mg_string_callback = NULL;
+%}
+
+%pragma(csharp) imclasscode=%{
+    class MgStringHelper 
+    {
+        public delegate global::System.IntPtr MgStringDelegate(int len);
+        
+        static MgStringDelegate stringDelegate = new MgStringDelegate(CreateString);
+
+        [global::System.Runtime.InteropServices.DllImport("$dllimport", EntryPoint="MgRegisterStringCallback_$module")]
+        public static extern void MgRegisterStringCallback_$module(MgStringDelegate stringDelegate);
+
+        static global::System.IntPtr CreateString(int len)
+        {
+            return global::System.Runtime.InteropServices.Marshal.AllocCoTaskMem(len);
+        }
+
+        static MgStringHelper()
+        {
+            MgRegisterStringCallback_$module(stringDelegate);
+        }
+    }
+
+    static MgStringHelper stringHelper = new MgStringHelper();
+%}
+
+%insert(runtime) %{
+#ifdef __cplusplus
+extern "C" 
+#endif
+SWIGEXPORT void SWIGSTDCALL MgRegisterStringCallback_$module(SWIG_CSharpMgStringHelperCallback callback)
+{
+    mg_string_callback = callback;
+}
+%}
+
+/**
+ * .net Exception hierarchy
+ *
+ * By default MgException inherits from MgSerializable. For it to be throwable in .net, we have to break
+ * the inheritance chain and have MgException derive from our ManagedException base exception class instead
+ *
+ * Breaking the MgSerializable inheritance chain is inconsequential as the methods provided by MgSerializable
+ * are not available for managed code consumption
+ */
+%typemap(csbase, replace="1") MgException "ManagedException"
+
+/**
+ * Exception propagation support
+ */
+%insert(runtime) %{
+
+typedef void (SWIGSTDCALL* SWIG_CSharpMgExceptionCallback_t)(const void*, const char*); 
+
+static SWIG_CSharpMgExceptionCallback_t mg_exception_callback = NULL;
+
+#ifdef __cplusplus
+extern "C" 
+#endif
+SWIGEXPORT void SWIGSTDCALL SWIGRegisterMgExceptionCallback_$module(SWIG_CSharpMgExceptionCallback_t callback)
+{
+    mg_exception_callback = callback;
+}
+%}
+
+%pragma(csharp) imclasscode=%{
+    protected class MgExceptionHelper {
+        public delegate void MgExceptionDelegate(global::System.IntPtr exPtr, string className);
+        
+        static MgExceptionDelegate mgExceptionDelegate = new MgExceptionDelegate(SetPendingMgException);
+        
+        [global::System.Runtime.InteropServices.DllImport("$dllimport", EntryPoint="SWIGRegisterMgExceptionCallback_$module")]
+        static extern void SWIGRegisterMgExceptionCallback_$module(MgExceptionDelegate mgDelegate);
+        
+        static void SetPendingMgException(global::System.IntPtr exPtr, string className)
+        {
+            //IMPORTANT: It is imperative that nothing throws here as while such behavior is acceptable on Windows, in
+            //CoreCLR on Linux such a throw is treated as a native throw and will crash the running application with SIGABRT
+            //
+            //See: https://github.com/dotnet/coreclr/issues/2263
+            //
+            //SWIG by default will use SWIGPendingException to "stash" exceptions to be rethrown later on
+            //we will use the same mechanism
+            global::System.Exception ex = OSGeo.MapGuide.MgObjectFactory.CreateObject<global::System.Exception>(exPtr);
+            if (ex != null)
+                SWIGPendingException.Set(ex);
+            else //Shouldn't get here
+                SWIGPendingException.Set(new global::System.Exception("Attempted to construct a .net proxy of " + className + ", but instance is not an exception type"));
+        }
+        
+        static MgExceptionHelper()
+        {
+            SWIGRegisterMgExceptionCallback_$module(mgExceptionDelegate);
+        }
+    }
+    
+    protected static MgExceptionHelper mgExceptionHelper = new MgExceptionHelper();
+    
+    internal class SWIGPendingException
+    {
+        [global::System.ThreadStatic]
+        private static global::System.Exception pendingException = null;
+        private static int numExceptionsPending = 0;
+
+        public static bool Pending
+        {
+            get
+            {
+                bool pending = false;
+                if (numExceptionsPending > 0)
+                if (pendingException != null)
+                pending = true;
+                return pending;
+            } 
+        }
+
+        public static void Set(global::System.Exception e)
+        {
+            if (pendingException != null)
+                throw new global::System.Exception("FATAL: An earlier pending exception from unmanaged code was missed and thus not thrown (" + pendingException.ToString() + ")", e);
+            pendingException = e;
+            lock(typeof($imclassname))
+            {
+                numExceptionsPending++;
+            }
+        }
+
+        public static global::System.Exception Retrieve()
+        {
+            global::System.Exception e = null;
+            if (numExceptionsPending > 0) 
+            {
+                if (pendingException != null) 
+                {
+                    e = pendingException;
+                    pendingException = null;
+                    lock(typeof($imclassname))
+                    {
+                        numExceptionsPending--;
+                    }
+                }
+            }
+            return e;
+        }
+    }
+%}
+
+// Exception support
+%exception {
+    MG_TRY()
+        $action
+    MG_CATCH(L"$wrapname")
+    if (mgException != NULL) {
+        //AddRef the exception as this is a Ptr<> it will auto-unref when leaving the method
+        (*mgException).AddRef();
+        char* exClassName = mgException->GetMultiByteClassName();
+        mg_exception_callback(mgException.p, exClassName);
+    }
+}
+
+/**
+ * .net polymorphism support
+ *
+ * We leverage the fact that every object type derives from MgObject which can describe its own class name
+ * We use this information to determine what .net proxy class to create when any method returns a Mg* class
+ */
+
+//Insert the necessary helper functions on the native side to assist
+//
+//NOTE: extern "C" not required as SWIG will already take care of that
+%insert(header) %{
+
+#ifdef __cplusplus
+extern "C"
+#endif
+SWIGEXPORT int SWIGSTDCALL GetClassId(void* ptrObj)
+{
+    return ((MgObject*)ptrObj)->GetClassId();
+}
+
+#ifdef __cplusplus
+extern "C"
+#endif
+SWIGEXPORT void* SWIGSTDCALL GetClassName(void* ptrObj)
+{
+    void* result = NULL;
+    STRING clsName = ((MgObject*)ptrObj)->GetClassName();
+    result = mg_string_callback((int)(clsName.length()+1)*sizeof(wchar_t));
+#ifdef _WIN32
+    wcscpy((wchar_t*)result, clsName.c_str());
+#else
+    xstring u16String;
+    UnicodeString::UTF32toUTF16((const LCh*) clsName.c_str(), u16String);
+    result = mg_string_callback((int)(u16String.length()+1)*sizeof(LCh));
+    XMLString::copyString((XMLCh*)result, u16String.c_str());
+#endif
+    return result;
+}
+
+%}
+
+//Override the default typemap.
+%typemap(csout, excode=SWIGEXCODE) SWIGTYPE * 
+{
+    var objPtr = $imcall;$excode
+    if (objPtr == global::System.IntPtr.Zero)
+    {
+        return null;
+    }
+    else
+    {
+        var result = MgObjectFactory.CreateObject<$csclassname>(objPtr);
+        return result;
+    }
+}
+
+%pragma(csharp) imclasscode=%{
+    [global::System.Runtime.InteropServices.DllImport("$dllimport", EntryPoint="GetClassId")]
+    internal static extern int GetClassId(global::System.IntPtr objPtr);
+
+    [global::System.Runtime.InteropServices.DllImport("$dllimport", EntryPoint="GetClassName")]
+    internal static extern global::System.IntPtr GetClassName(global::System.IntPtr objPtr);
+%}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/custom.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/custom.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/custom.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,129 @@
+/**
+ * custom.i
+ *
+ * Custom class augmentations for specific classes of the MapGuide API
+ */
+
+// MgBatchPropertyCollection
+
+%typemap(csimports) MgBatchPropertyCollection %{
+using System;
+using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
+//These warnings are false positives as a result of SWIG generated code
+#pragma warning disable 0108, 0114
+%}
+
+%typemap(csinterfaces_derived) MgBatchPropertyCollection "System.Collections.Generic.IList<MgPropertyCollection>"
+
+%typemap(cscode) MgBatchPropertyCollection %{
+    int IList<MgPropertyCollection>.IndexOf(MgPropertyCollection item)
+    {
+        return -1;
+    }
+    
+    /*
+    void IList<MgPropertyCollection>.Insert(int index, MgPropertyCollection item)
+    {
+        this.Insert(index, item);
+    }
+    
+    void IList<MgPropertyCollection>.RemoveAt(int index)
+    {
+        this.RemoveAt(index);
+    }
+    */
+    
+    public MgPropertyCollection this[int index]
+    {
+        get { return this.GetItem(index); }
+        set { this.SetItem(index, value); }
+    }
+    
+    /*
+    void ICollection<MgPropertyCollection>.Add(MgPropertyCollection item)
+    {
+        this.Add(item);
+    }
+    
+    void ICollection<MgPropertyCollection>.Clear()
+    {
+        this.Clear();
+    }
+    */
+    
+    bool ICollection<MgPropertyCollection>.Contains(MgPropertyCollection item)
+    {
+        return false;
+    }
+    
+    public void CopyTo(MgPropertyCollection[] array, int arrayIndex)
+    {
+        throw new global::System.NotImplementedException();
+    }
+    
+    bool ICollection<MgPropertyCollection>.Remove(MgPropertyCollection item)
+    {
+        int count = this.GetCount();
+        this.Remove(item);
+        return this.GetCount() < count;
+    }
+    
+    public int Count
+    {
+        get { return this.GetCount(); }
+    }
+    
+    public bool IsReadOnly
+    {
+        get { return false; }
+    }
+    
+    class CollectionEnumerator : IEnumerator<MgPropertyCollection>
+    {
+        private IList<MgPropertyCollection> _list;
+        private int _position;
+        private int _count;
+        
+        public CollectionEnumerator(IList<MgPropertyCollection> list)
+        {
+            _list = list;
+            _count = list.Count;
+            _position = -1;
+        }
+        
+        bool IEnumerator.MoveNext()
+        {
+            _position++;
+            return _position < _count;
+        }
+        
+        void IEnumerator.Reset()
+        {
+            _position = -1;
+        }
+        
+        object IEnumerator.Current
+        {
+            get { return _list[_position]; }
+        }
+        
+        MgPropertyCollection IEnumerator<MgPropertyCollection>.Current
+        {
+            get { return _list[_position]; }
+        }
+        
+        public void Dispose() { }
+    }
+    
+    public IEnumerator<MgPropertyCollection> GetEnumerator()
+    {
+        return new CollectionEnumerator(this);
+    }
+    
+    IEnumerator IEnumerable.GetEnumerator()
+    {
+        return new CollectionEnumerator(this);
+    }
+%}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/string.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/string.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/string.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,34 @@
+%typemap(imtype) STRINGPARAM "[global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.LPWStr)] System.String"
+%typemap(imtype) STRING      "global::System.IntPtr"
+
+%typemap(ctype)  STRINGPARAM "const wchar_t *"
+%typemap(cstype) STRINGPARAM "string"
+%typemap(cstype) STRING      "string"
+
+%typemap(in)  STRINGPARAM    %{ $1 = $input; %}
+%typemap(out) STRING         %{ $result = $1; %}
+
+%typecheck(SWIG_TYPECHECK_STRING)
+    char *,
+    wchar_t *,
+    STRINGPARAM,
+    STRING,
+    char[ANY]
+    ""
+    
+%typemap(csin) STRINGPARAM "$csinput"
+
+%typemap(csout, excode=SWIGEXCODE) STRING {
+    System.IntPtr cPtr = $imcall;$excode
+    System.String str = global::System.Runtime.InteropServices.Marshal.PtrToStringUni(cPtr);
+    global::System.Runtime.InteropServices.Marshal.FreeCoTaskMem(cPtr);
+    return str;
+}
+
+%typemap(csvarout) STRINGPARAM, STRING %{
+    get 
+    {
+        var result = $imcall;$excode
+        return result;
+    } 
+%}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/sugar.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/sugar.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/DotNet/sugar.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,197 @@
+/**
+ * sugar.i
+ *
+ * Helper macros to implement .net collections and other assorted syntactic sugar
+ */
+
+%define IMPLEMENT_LIST(collection_type, item_type)
+//Necessary imports
+%typemap(csimports) collection_type %{
+using System;
+using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
+//These warnings are false positives as a result of SWIG generated code
+#pragma warning disable 0108, 0114
+%}
+//Collection Interfaces implemented by the implementing proxy class
+%typemap(csinterfaces_derived) collection_type "IList<item_type>"
+//This is the IList<T> implementation that is injected into the implementing proxy class
+%typemap(cscode) collection_type %{
+    /*
+    int IList<item_type>.IndexOf(item_type item)
+    {
+        return this.IndexOf(item);
+    }
+    
+    void IList<item_type>.Insert(int index, item_type item)
+    {
+        this.Insert(index, item);
+    }
+    
+    void IList<item_type>.RemoveAt(int index)
+    {
+        this.RemoveAt(index);
+    }
+    */
+    public item_type this[int index]
+    {
+        get { return this.GetItem(index); }
+        set { this.SetItem(index, value); }
+    }
+    /*
+    void ICollection<item_type>.Add(item_type item)
+    {
+        this.Add(item);
+    }
+    
+    void ICollection<item_type>.Clear()
+    {
+        this.Clear();
+    }
+    
+    bool ICollection<item_type>.Contains(item_type item)
+    {
+        return this.Contains(item);
+    }
+    */
+    public void CopyTo(item_type[] array, int arrayIndex)
+    {
+        throw new global::System.NotImplementedException();
+    }
+    
+    bool ICollection<item_type>.Remove(item_type item)
+    {
+        int count = this.GetCount();
+        this.Remove(item);
+        return this.GetCount() < count;
+    }
+    
+    public int Count
+    {
+        get { return this.GetCount(); }
+    }
+    
+    public bool IsReadOnly
+    {
+        get { return false; }
+    }
+    
+    class CollectionEnumerator : IEnumerator<item_type>
+    {
+        private IList<item_type> _list;
+        private int _position;
+        private int _count;
+        
+        public CollectionEnumerator(IList<item_type> list)
+        {
+            _list = list;
+            _count = list.Count;
+            _position = -1;
+        }
+        
+        bool IEnumerator.MoveNext()
+        {
+            _position++;
+            return _position < _count;
+        }
+        
+        void IEnumerator.Reset()
+        {
+            _position = -1;
+        }
+        
+        object IEnumerator.Current
+        {
+            get { return _list[_position]; }
+        }
+        
+        item_type IEnumerator<item_type>.Current
+        {
+            get { return _list[_position]; }
+        }
+        
+        public void Dispose() { }
+    }
+    
+    public IEnumerator<item_type> GetEnumerator()
+    {
+        return new CollectionEnumerator(this);
+    }
+    
+    IEnumerator IEnumerable.GetEnumerator()
+    {
+        return new CollectionEnumerator(this);
+    }
+%}
+%enddef
+%define IMPLEMENT_READONLY_LIST(collection_type, item_type)
+//Necessary imports
+%typemap(csimports) collection_type %{
+using System;
+using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
+%}
+//Collection Interfaces implemented by the implementing proxy class
+%typemap(csinterfaces_derived) collection_type "IReadOnlyList<item_type>"
+//This is the IReadOnlyList<T> implementation that is injected into the implementing proxy class
+%typemap(cscode) collection_type %{
+    item_type IReadOnlyList<item_type>.this[int index]
+    {
+        get { return this.GetItem(index); }
+    }
+    
+    int IReadOnlyCollection<item_type>.Count
+    {
+        get { return this.GetCount(); }
+    }
+    
+    class CollectionEnumerator : IEnumerator<item_type>
+    {
+        private IReadOnlyList<item_type> _list;
+        private int _position;
+        private int _count;
+        
+        public CollectionEnumerator(IReadOnlyList<item_type> list)
+        {
+            _list = list;
+            _count = list.Count;
+            _position = -1;
+        }
+        
+        bool IEnumerator.MoveNext()
+        {
+            _position++;
+            return _position < _count;
+        }
+        
+        void IEnumerator.Reset()
+        {
+            _position = -1;
+        }
+        
+        object IEnumerator.Current
+        {
+            get { return _list[_position]; }
+        }
+        
+        item_type IEnumerator<item_type>.Current
+        {
+            get { return _list[_position]; }
+        }
+        
+        public void Dispose() { }
+    }
+    
+    IEnumerator<item_type> IEnumerable<item_type>.GetEnumerator()
+    {
+        return new CollectionEnumerator(this);
+    }
+    
+    IEnumerator IEnumerable.GetEnumerator()
+    {
+        return new CollectionEnumerator(this);
+    }
+%}
+%enddef
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/exception.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/exception.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/exception.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,40 @@
+/**
+ * exception.i
+ *
+ * Exception support
+ */
+
+/**
+ * Java Exception hierarchy
+ *
+ * By default MgException inherits from MgSerializable. For it to be throwable in Java, we have to break
+ * the inheritance chain and have MgException derive from our AppThrowable base exception class instead
+ *
+ * Breaking the MgSerializable inheritance chain is inconsequential as the methods provided by MgSerializable
+ * are not available for managed code consumption
+ */
+%typemap(javabase, replace="1") MgException "AppThrowable"
+
+%insert(header) %{
+void ThrowJavaExceptionWrapper(JNIEnv *jenv, MgException* e) 
+{
+    std::string exName = "org/osgeo/mapguide/";
+    exName += e->GetMultiByteClassName();
+    jclass exCls = jenv->FindClass(exName.c_str());
+    jmethodID ctorId = jenv->GetMethodID(exCls, "<init>", "(JZ)V");
+    jthrowable exObj = (jthrowable)jenv->NewObject(exCls, ctorId, (jlong)e, (jboolean)1);
+    jenv->Throw(exObj);
+}
+%}
+
+%exception {
+    MG_TRY()
+        $action
+    MG_CATCH(L"$wrapname")
+    if (mgException != NULL) {
+        //AddRef the exception as this is a Ptr<> it will auto-unref when leaving the method
+        (*mgException).AddRef();
+        RefCount(mgException);
+        ThrowJavaExceptionWrapper(jenv, mgException);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/extensions.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/extensions.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/extensions.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,318 @@
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+//---------------------- Java interface decorations ------------------------------//
+
+//NOTE: If we're eventually targeting JDK7, change to AutoCloseable for automatic try-with-resources syntax support
+%typemap(javainterfaces) MgReader "java.io.Closeable"
+%typemap(javainterfaces) MgFeatureReader "java.io.Closeable"
+%typemap(javainterfaces) MgDataReader "java.io.Closeable"
+%typemap(javainterfaces) MgSqlDataReader "java.io.Closeable"
+%typemap(javainterfaces) MgLongTransactionReader "java.io.Closeable"
+%typemap(javainterfaces) MgSpatialContextReader "java.io.Closeable"
+
+//This is a helper macro to implement the required APIs for collection items
+//
+//This could possibly apply to all proxy classes, but for now we're only interested in classes
+//that are part of any collection class that implements java.util.Collection. This is needed for
+//contains() and other container tests for equality to work
+//
+//Implementation borrowed from OGR swig java bindings
+%define IMPLEMENT_MG_ITEM_API(item_type)
+
+%typemap(javacode) item_type %{
+    public boolean equals(Object obj) {
+        boolean equal=false;
+        if(obj instanceof $javaclassname)
+            equal=((($javaclassname)obj).swigCPtr==this.swigCPtr);
+        return equal;
+    }
+    
+    public int hashCode() {
+        return (int)swigCPtr;
+    }
+%}
+
+%enddef
+
+//This is a helper macro to implement the bulk of the java.util.Collection interface for any proxy class
+//that needs one
+//
+//Kinda like C++ templates for Java without the suck. And we all know how much Java generics suck :D
+//
+//TODO: Not sure if a SWIG macro can call into another SWIG macro, but if it can it should call into
+//the IMPLEMENT_MG_JAVA_ITERABLE_API
+%define IMPLEMENT_MG_JAVA_COLLECTION_API(collection_type, item_type)
+//Necessary imports
+%typemap(javaimports) collection_type %{
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+%}
+//Java Interfaces implemented by the java proxy class
+%typemap(javainterfaces) collection_type "Collection<item_type>"
+//This is the java.util.Collection implementation that is injected into each java proxy class
+%typemap(javacode) collection_type %{
+    /**
+     * An iterator to iterate over this collection
+     */
+    class ItemIterator implements Iterator<item_type> {
+        private collection_type _collection;
+        private int _pos;
+        private int _count;
+        
+        public ItemIterator(collection_type c) { 
+            _collection = c; 
+            _count = _collection.getCount();
+            _pos = -1;
+        }
+        
+        /**
+         * Returns true if the iteration has more elements
+         */
+        public boolean hasNext() {
+            return _pos + 1 < _count;
+        }
+        
+        /**
+         * Returns the next item_type in the collection
+         */
+        public item_type next() {
+            _pos++;
+            if (_pos >= _count)
+                throw new NoSuchElementException();
+            return _collection.getItem(_pos);
+        }
+        
+        /**
+         * Removes from the underlying collection the last element returned by the iterator (not supported).
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+    
+    /**
+     * Returns an iterator over the elements in this collection.
+     */
+    public Iterator<item_type> iterator() { return new ItemIterator(this); }
+
+    /**
+     * Ensures that this collection contains the specified element (optional operation).
+     */
+    public boolean add(item_type item) {
+        this.addItem(item);
+        return true;
+    }
+
+    /**
+     * Adds all of the elements in the specified collection to this collection (optional operation).
+     */
+    public boolean addAll(Collection<? extends item_type> c) {
+        int added = 0;
+        for (item_type item : c) {
+            this.addItem(item);
+            added++;
+        }
+        return added > 0;
+    }
+
+    /**
+     *  Returns true if this collection contains the specified element.
+     */
+    public boolean contains(Object o) {
+        if (o instanceof item_type) {
+            return this.contains((item_type)o);
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if this collection contains all of the elements in the specified collection.
+     */
+    public boolean containsAll(Collection<?> c) {
+        for (Object o : c) {
+            if (!this.contains(o))
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns true if this collection contains no elements.
+     */
+    public boolean isEmpty() { return this.getCount() == 0; }
+
+    /**
+     * Removes a single instance of the specified element from this collection, if it is present (optional operation).
+     */
+    public boolean remove(Object o) {
+        if (o instanceof item_type) {
+            return this.remove((item_type)o);
+        }
+        return false;
+    }
+
+    /**
+     * Removes all of this collection's elements that are also contained in the specified collection (optional operation).
+     */
+    public boolean removeAll(Collection<?> c) {
+        int removed = 0;
+        for (Object o : c) {
+            if (this.remove(o))
+                removed++;
+        }
+        return removed > 0;
+    }
+
+    /**
+     * Retains only the elements in this collection that are contained in the specified collection (optional operation).
+     */
+    public boolean retainAll(Collection<?> c) {
+        int removed = 0;
+        ArrayList<item_type> remove = new ArrayList<item_type>();
+        for (int i = 0; i < this.getCount(); i++) {
+            item_type item = this.getItem(i);
+            if (!c.contains(item))
+                remove.add(item);
+        }
+        if (remove.size() > 0) {
+            return this.removeAll(remove);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the number of elements in this collection.
+     */
+    public int size() { return this.getCount(); }
+
+    /**
+     * Returns an array containing all of the elements in this collection.
+     */
+    public Object[] toArray() {
+        int count = this.getCount();
+        Object[] items = new Object[count];
+        for (int i = 0; i < count; i++) {
+            items[i] = this.getItem(i);
+        }
+        return items;
+    }
+
+    /**
+     * Returns an array containing all of the elements in this collection; the runtime type of the returned array is that of the specified array.
+     */
+    public <item_type> item_type[] toArray(item_type[] a) {
+        Object[] items = this.toArray();
+        if (a.length >= items.length) {
+            for (int i = 0; i < items.length; i++) {
+                a[i] = (item_type)items[i];
+            }
+            for (int i = items.length; i < a.length; i++) {
+                a[i] = null;
+            }
+            return a;
+        } else {
+            item_type[] retVal = (item_type[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), items.length);
+            for (int i = 0; i < retVal.length; i++) {
+                retVal[i] = (item_type)items[i];
+            }
+            return retVal;
+        }
+    }
+
+%}
+%enddef
+
+//This helper macro implements the java.lang.Iterable API allowing the instance of the
+//java proxy to be used in an enhanced for loop (akin to IEnumerable/foreach in C#)
+%define IMPLEMENT_MG_JAVA_ITERABLE_API(collection_type, item_type)
+//Necessary imports
+%typemap(javaimports) collection_type %{
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+%}
+//Java Interfaces implemented by the java proxy class
+%typemap(javainterfaces) collection_type "java.lang.Iterable<item_type>"
+//This is the java.lang.Iterable<T> implementation that is injected into each java proxy class
+%typemap(javacode) collection_type %{
+    /**
+     * An iterator to iterate over this collection
+     */
+    class ItemIterator implements Iterator<item_type> {
+        private collection_type _collection;
+        private int _pos;
+        private int _count;
+        
+        public ItemIterator(collection_type c) { 
+            _collection = c; 
+            _count = _collection.getCount();
+            _pos = -1;
+        }
+        
+        /**
+         * Returns true if the iteration has more elements
+         */
+        public boolean hasNext() {
+            return _pos + 1 < _count;
+        }
+        
+        /**
+         * Returns the next item_type in the collection
+         */
+        public item_type next() {
+            _pos++;
+            if (_pos >= _count)
+                throw new NoSuchElementException();
+            return _collection.getItem(_pos);
+        }
+        
+        /**
+         * Removes from the underlying collection the last element returned by the iterator (not supported).
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * Returns an iterator over the elements in this collection.
+     */
+    public Iterator<item_type> iterator() { return new ItemIterator(this); }
+    
+%}
+%enddef
+
+//Plug the stock implementation for our MapGuide collection and item classes
+IMPLEMENT_MG_ITEM_API(MgClassDefinition)
+IMPLEMENT_MG_ITEM_API(MgFeatureSchema)
+IMPLEMENT_MG_ITEM_API(MgPropertyDefinition)
+IMPLEMENT_MG_ITEM_API(MgProperty)
+IMPLEMENT_MG_ITEM_API(MgLayerBase)
+IMPLEMENT_MG_ITEM_API(MgLayerGroup)
+
+//IMPLEMENT_MG_JAVA_COLLECTION_API(MgBatchPropertyCollection, MgPropertyCollection)
+IMPLEMENT_MG_JAVA_COLLECTION_API(MgClassDefinitionCollection, MgClassDefinition)
+IMPLEMENT_MG_JAVA_COLLECTION_API(MgFeatureSchemaCollection, MgFeatureSchema)
+IMPLEMENT_MG_JAVA_COLLECTION_API(MgPropertyDefinitionCollection, MgPropertyDefinition)
+IMPLEMENT_MG_JAVA_COLLECTION_API(MgPropertyCollection, MgProperty)
+IMPLEMENT_MG_JAVA_COLLECTION_API(MgStringCollection, String)
+IMPLEMENT_MG_JAVA_COLLECTION_API(MgLayerCollection, MgLayerBase)
+IMPLEMENT_MG_JAVA_COLLECTION_API(MgLayerGroupCollection, MgLayerGroup)
+IMPLEMENT_MG_JAVA_ITERABLE_API(MgReadOnlyLayerCollection, MgLayerBase)
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/monkey_patch.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/monkey_patch.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Java/monkey_patch.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,52 @@
+/**
+ * Apply a global firstlowercase naming convention
+ */
+%rename("%(firstlowercase)s",%$isfunction) "";
+
+//---------------------- Renames to avoid Java/C++ API clashes ---------------------------//
+
+/**
+ * Rename SWIG's "delete" to "destroy". However the typemaps to do this cannot just rename the thing
+ * we have to repeat the expected implementation verbatim with the new name
+ */
+%typemap(javafinalize) SWIGTYPE %{
+  protected void finalize() {
+    destroy();  // renamed to prevent conflict with existing delete method
+  }
+%}
+%typemap(javadestruct, methodname="destroy", methodmodifiers="public synchronized") SWIGTYPE
+{
+    if (swigCPtr != 0) {
+      if (swigCMemOwn) {
+        swigCMemOwn = false;
+        $jnicall;
+      }
+      swigCPtr = 0;
+    }
+}
+%typemap(javadestruct_derived, methodname="destroy", methodmodifiers="public synchronized") SWIGTYPE
+{
+    if (swigCPtr != 0) {
+      if (swigCMemOwn) {
+        swigCMemOwn = false;
+        $jnicall;
+      }
+      swigCPtr = 0;
+    }
+    super.destroy();
+}
+
+//Already defined in Java Exception so rename our proxy method
+%rename(getExceptionStackTrace) MgException::GetStackTrace;
+
+//If we want to implement java.util.Collection, we need to rename this incompatible API (as add() is expected to 
+//return boolean in the java.util.Collection API)
+%rename(addItem) MgBatchPropertyCollection::Add;
+%rename(addItem) MgClassDefinitionCollection::Add;
+%rename(addItem) MgFeatureSchemaCollection::Add;
+%rename(addItem) MgPropertyDefinitionCollection::Add;
+%rename(addItem) MgIntCollection::Add;
+%rename(addItem) MgPropertyCollection::Add;
+%rename(addItem) MgStringCollection::Add;
+%rename(addItem) MgLayerCollection::Add;
+%rename(addItem) MgLayerGroupCollection::Add;
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/exception.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/exception.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/exception.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,70 @@
+/**
+ * exception.i
+ *
+ * Exception support
+ */
+
+//To make MgException a throwable PHP exception, we need to break its inheritance chain
+//which means we have to ignore MgSerializable. Although this is part of the public API
+//surface, it is nothing more than a "marker" base class for internal server/webtier
+//plumbing, none of which concerns and MapGuide application built on top of this. This
+//will affect the inheritance chain of other classes, but once again due to the "marker"
+//nature of this class, the breakage of this inheritance chain is inconsequential.
+%ignore MgSerializable;
+
+//Now that the inheritance chain has been broken, we can re-base MgException on top of
+//PHP exception
+%feature("exceptionclass") MgException;
+
+%insert(header) %{
+void ThrowPhpExceptionWrapper(MgException* e) 
+{
+    const char* exClassName = ResolveMgClassName(static_cast<MgObject*>(e)->GetClassId());
+    swig_type_info* ty = SWIG_TypeQuery(exClassName);
+    if (NULL == ty)
+    {
+        std::string msg = "Tried to throw ";
+        msg += exClassName;
+        msg += ", but no PHP proxy class definition could be found";
+        zend_throw_exception(zend_ce_exception, msg.c_str(), 0);
+    }
+    else
+    {
+        zend_class_entry* ce = NULL;
+        const char* origClsName = e->GetMultiByteClassName();
+        zend_string * classname = zend_string_init(origClsName, strlen(origClsName), 0);
+        ce = zend_lookup_class(classname);
+        zend_string_release(classname);
+
+        if (NULL == ce)
+        {
+            std::string msg = "Tried to throw ";
+            msg += exClassName;
+            msg += ", but no PHP proxy class definition could be found";
+            zend_throw_exception(zend_ce_exception, msg.c_str(), 0);
+        }
+        else
+        {
+            zval obj;
+            zval cPtr;
+            SWIG_SetPointerZval(&cPtr, (void *)e, ty, 1);
+            object_init_ex(&obj, ce);
+            add_property_zval(&obj, "_cPtr", &cPtr);
+            zend_throw_exception_object(&obj);
+        }
+    }
+}
+%}
+
+%exception {
+    MG_TRY()
+        $action
+    MG_CATCH(L"$wrapname")
+    if (mgException != NULL) {
+        //AddRef the exception as this is a Ptr<> it will auto-unref when leaving the method
+        (*mgException).AddRef();
+        RefCount(mgException);
+        ThrowPhpExceptionWrapper(mgException);
+        goto thrown; //thrown is a SWIG-generated label
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/monkey_patch.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/monkey_patch.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/monkey_patch.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,76 @@
+/**
+ * monkey_patch.i
+ *
+ * Selective monkey patching of classes and/or APIs that we're currently finding difficulties wrapping through
+ * vanilla SWIG
+ *
+ * Motivation:
+ *
+ * The current version of SWIG (3.0.12) is generating incorrect methods for certain overloads, this is most
+ * prevalent on methods that have overloaded variations whose return type is some kind of abstract class.
+ *
+ * Due to insufficient resources, we cannot truly identify the cause and fix.
+ *
+ * However, we can workaround this by being leveraging SWIG facilities to "un-overload" a problematic method and hand-write
+ * a fixed version of the orignal overloaded method on the PHP side to properly call into the correct signature
+ *
+ * Anatomy of a monkey-patched API:
+ *
+ *  1. Use %ignore to hide the problematic API
+ *  2. Use %extend on the affected class to declare uniquely named "de-overloaded" variations of the API, these just call into
+ *     original overload signature
+ *  3. Insert a PHP trait implementation that provides the original problematic API that calls into the correct "de-overloaded" method
+ *     based on the arguments passed into it
+ *  4. A post-processor will then need to insert a trait usage statement into the affected PHP classes. SWIG cannot do this part currently as
+ *     it does not provide sufficient augmentation points for generated PHP classes.
+ */
+
+%ignore MgConfigurationLoadFailedException::GetExceptionMessage;
+%ignore MgResource::Save;
+%ignore MgMapBase::Open;
+%ignore MgLayerBase::MgLayerBase;
+
+%ignore MgWktReaderWriter::Read;
+%extend MgWktReaderWriter 
+{
+    MgGeometry* _Read_1(STRINGPARAM wkt)
+    {
+        return $self->Read(wkt, NULL);
+    }
+
+    MgGeometry* _Read_2(STRINGPARAM wkt, MgTransform* xform)
+    {
+        return $self->Read(wkt, xform);
+    }
+}
+
+%extend MgException
+{
+    void _ReleaseMe()
+    {
+        ReleaseObject($self);
+    }
+}
+
+%pragma(php) code="
+//======================= Begin PHP Traits ==========================//
+
+//Trait to augment exception cleanup
+trait MgExceptionPatched {
+    public function __destruct() {
+        $this->_ReleaseMe();
+    }
+}
+
+//Trait that monkey-patches MgWktReaderWriter::Read
+trait MgWktReaderWriterPatched {
+    public function Read($wkt, $xform = NULL) {
+        if ($xform != NULL) {
+            return $this->_Read_2($wkt, $xform);
+        } else {
+            return $this->_Read_1($wkt, $xform);
+        }
+    }
+}
+
+//======================== End PHP Traits ===========================//"
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/pointer.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/pointer.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/Php/pointer.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,25 @@
+/**
+ * pointer.i
+ *
+ * Generic pointer typemap for MgObject-derived classes (ie. Everything in the MapGuide API)
+ */
+
+///////////////////////////////////////////////////////////
+// Custom generic pointer typemap. This overrides the default
+// typemap to support downcasting
+//
+%typemap(out) SWIGTYPE* 
+{
+    const char* retClassName = ResolveMgClassName(static_cast<MgObject*>($1)->GetClassId());
+    swig_type_info* ty = NULL;
+    if (NULL != retClassName)
+    {
+        ty = SWIG_TypeQuery(retClassName);
+    }
+    if (NULL == ty) //Fallback to original descriptor
+    {
+        ty = $1_descriptor;
+    }
+    SWIG_SetPointerZval(return_value, (void *)$1, ty, 1); /* $owner */
+    RefCount($1);
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/refcount.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/refcount.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Common/refcount.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,54 @@
+/**
+ * refcount.i
+ *
+ * Common refcounting module applicable for all language targets
+ */
+
+// SWIG is refcounting aware and since our C++ classes follow a refcounting scheme
+// we can tap into this feature
+//
+// NOTE: We don't implement ref because anything from the native boundary is already AddRef'd
+// All the managed layer should do when Disposed or GC'd is to make sure it is released
+%feature("ref")   MgDisposable "RefCount($this);"
+%feature("unref") MgDisposable "ReleaseObject($this);"
+
+%runtime %{
+#include "Foundation.h"
+
+#if defined(REFCOUNTING_DIAGNOSTICS)
+INT32 RefCount(MgDisposable* obj)
+{
+    if (NULL != obj)
+    {
+        INT32 rc = obj->GetRefCount();
+    #if defined(SWIGPHP)
+        zend_printf("[zend]: Ref-count for instance of (%s): %p - %d\n", obj->GetMultiByteClassName(), (void*)obj, rc);
+    #else
+        printf("[native]: Ref-count for instance of (%s): %p - %d\n", obj->GetMultiByteClassName(), (void*)obj, rc);
+    #endif
+        return rc;
+    }
+    return -1;
+}
+
+void ReleaseObject(MgDisposable* obj)
+{
+    if (NULL != obj)
+    {
+        INT32 rc = obj->GetRefCount();
+    #if defined(SWIGPHP)
+        zend_printf("[zend]: Releasing instance of (%s): %p (%d -> %d)\n", obj->GetMultiByteClassName(), (void*)obj, rc, rc - 1);
+    #else
+        printf("[native]: Releasing instance of (%s): %p (%d -> %d)\n", obj->GetMultiByteClassName(), (void*)obj, rc, rc - 1);
+    #endif
+    }
+    SAFE_RELEASE(obj);
+}
+#else
+#define RefCount(obj)
+void ReleaseObject(MgDisposable* obj)
+{
+    SAFE_RELEASE(obj);
+}
+#endif
+%}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/CMakeLists.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/CMakeLists.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/CMakeLists.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,103 @@
+project(MapGuideDotNetUnmanagedApi)
+
+include_directories(
+    ${MG_COMMON_DIR}/MdfModel
+    ${MG_COMMON_DIR}/Foundation
+    ${MG_COMMON_DIR}/Geometry
+    ${MG_COMMON_DIR}/PlatformBase
+    ${MG_COMMON_DIR}/MapGuideCommon
+    ${ACE_INCLUDE_DIR}
+    ${MG_WEB_DIR}/HttpHandler
+    ${MG_WEB_DIR}/WebSupport
+    ${MG_WEB_DIR}/WebApp
+    ${XERCESC_INCLUDE_DIR}
+)
+
+set(DotNetUnmanagedApi_SRCS
+    ${CMAKE_CURRENT_BINARY_DIR}/MgApi_wrap.cpp
+)
+
+add_library(MapGuideDotNetUnmanagedApi${MG_VERSION_SUFFIX} SHARED ${DotNetUnmanagedApi_SRCS})
+add_dependencies(MapGuideDotNetUnmanagedApi${MG_VERSION_SUFFIX} IMake)
+if (UNIX)
+    add_definitions(-DDOTNETCORE)
+endif (UNIX)
+if (MSVC)
+    add_definitions(-DSWIG_PUBLIC_API)
+    add_definitions(/bigobj)
+    set_target_properties(MapGuideDotNetUnmanagedApi${MG_VERSION_SUFFIX} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${MG_DOTNET_OUTPUT_DIR} )
+    set_target_properties(MapGuideDotNetUnmanagedApi${MG_VERSION_SUFFIX} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${MG_DOTNET_OUTPUT_DIR} )
+    set_target_properties(MapGuideDotNetUnmanagedApi${MG_VERSION_SUFFIX} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${MG_DOTNET_OUTPUT_DIR} )
+endif (MSVC)
+
+set(IMAKE_RELATIVE_RESOLUTION "-r ${CMAKE_CURRENT_SOURCE_DIR}")
+if (UNIX)
+    set(IMAKE_RELATIVE_RESOLUTION "-a")
+endif (UNIX)
+
+set(SWIG_DOTNET_DEFS "")
+if (WIN32)
+    set(SWIG_DOTNET_DEFS "-DWIN32")
+endif (WIN32)
+
+message(STATUS "Using IMake at: ${IMAKE_TOOL}")
+add_custom_command(OUTPUT ${DotNetUnmanagedApi_SRCS}
+    COMMAND ${CMAKE_COMMAND} -E echo Removing: ${MG_DOTNET_PROJECT_DIR}/*.cs
+    COMMAND ${CMAKE_COMMAND} -E remove -f 
+            ${MG_DOTNET_PROJECT_DIR}/*.cs
+    COMMAND ${CMAKE_COMMAND} -E echo Regenerating: ${MG_DOTNET_PROJECT_DIR}/Constants.cs
+    COMMAND ${IMAKE_TOOL} -p ${CMAKE_CURRENT_BINARY_DIR}/../MapGuideApi/Constants.xml 
+            -l "C#"
+            ${IMAKE_RELATIVE_RESOLUTION}
+            -o ${MG_DOTNET_PROJECT_DIR}/Constants.cs
+            -t
+    COMMAND ${CMAKE_COMMAND} -E copy
+            ${CMAKE_CURRENT_BINARY_DIR}/dotnetcore.i
+            ${CMAKE_CURRENT_BINARY_DIR}/language.i
+    COMMAND ${IMAKE_TOOL} -p ${CMAKE_CURRENT_BINARY_DIR}/../MapGuideApi/MapGuideApiGen.xml
+            -l "C#"
+            ${IMAKE_RELATIVE_RESOLUTION}
+            -o ${CMAKE_CURRENT_BINARY_DIR}
+    COMMAND ${CMAKE_COMMAND} -E echo Running SWIG [DotNet]. Output Dir: ${MG_DOTNET_PROJECT_DIR}
+    COMMAND ${SWIG_TOOL} -c++ -csharp 
+            ${SWIG_DOTNET_DEFS} -DDOTNETCORE -DSWIG_CSHARP_NO_EXCEPTION_HELPER
+            -DSWIG_CSHARP_NO_STRING_HELPER -DSWIG_CSHARP_NO_WSTRING_HELPER
+            -namespace OSGeo.MapGuide
+            -nodefaultctor -nodefaultdtor
+            -module MapGuideDotNetUnmanagedApi
+            -o "${CMAKE_CURRENT_BINARY_DIR}/MgApi_wrap.cpp"
+            -outdir "${MG_DOTNET_PROJECT_DIR}" 
+            "${CMAKE_CURRENT_BINARY_DIR}/MapGuideApi.i"
+    COMMAND ${CMAKE_COMMAND} -E remove -f 
+            ${MG_DOTNET_PROJECT_DIR}/MapGuideDotNetUnmanagedApi.cs
+)
+
+target_link_libraries(MapGuideDotNetUnmanagedApi${MG_VERSION_SUFFIX}
+    ${ACE_LIBRARY}
+    ${MG_FOUNDATION_LIBRARY}
+    ${MG_GEOMETRY_LIBRARY}
+    ${MG_PLATFORMBASE_LIBRARY}
+    ${MG_COMMON_LIBRARY}
+    ${MG_HTTPHANDLER_LIBRARY}
+    ${MG_WEBAPP_LIBRARY}
+    ${MG_MDFMODEL_LIBRARY}
+    ${MG_MDFPARSER_LIBRARY}
+    ${MG_WEBSUPPORT_LIBRARY}
+    #MgFoundation${MG_VERSION_SUFFIX}
+    #MgGeometry${MG_VERSION_SUFFIX}
+    #MgPlatformBase${MG_VERSION_SUFFIX}
+    #MgMapGuideCommon${MG_VERSION_SUFFIX}
+    #MgHttpHandler${MG_VERSION_SUFFIX}
+    #MgMdfModel${MG_VERSION_SUFFIX}
+    #MgMdfParser${MG_VERSION_SUFFIX}
+    ${XERCESC_LIBRARIES}
+    #MgWebApp${MG_VERSION_SUFFIX}
+)
+
+# Only needed for Linux
+if (UNIX)
+    file(COPY "InitializeWebTier.cpp" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+    install( TARGETS MapGuideDotNetUnmanagedApi${MG_VERSION_SUFFIX} DESTINATION ${MG_DOTNET_OUTPUT_DIR})
+endif (UNIX)
+
+file(COPY "dotnetcore.i" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/DotNetUnmanagedApi.rc
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/DotNetUnmanagedApi.rc	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/DotNetUnmanagedApi.rc	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,96 @@
+// Microsoft Visual C++ generated resource script.
+//
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+    "#include ""winresrc.h""\r\n"
+    "\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 3,0,0,0
+ PRODUCTVERSION 3,0,0,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "Open Source Geospatial Foundation"
+            VALUE "FileDescription", "MapGuideDotNetUnmanagedApi Dynamic Link Library"
+            VALUE "FileVersion", "3, 0, 0, 0"
+            VALUE "InternalName", "MapGuideDotNetCoreApi"
+            VALUE "LegalCopyright", "Copyright (C) 2006-2015 by Autodesk, Inc."
+            VALUE "OriginalFilename", "MapGuideDotNetUnmanagedApi.dll"
+            VALUE "ProductName", "MapGuide Open Source"
+            VALUE "ProductVersion", "3, 0, 0, 0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/DotNetUnmanagedApi.vcxproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/DotNetUnmanagedApi.vcxproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/DotNetUnmanagedApi.vcxproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,488 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug_VC11|Win32">
+      <Configuration>Debug_VC11</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug_VC11|x64">
+      <Configuration>Debug_VC11</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug_VC14|Win32">
+      <Configuration>Debug_VC14</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug_VC14|x64">
+      <Configuration>Debug_VC14</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release_VC11|Win32">
+      <Configuration>Release_VC11</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release_VC11|x64">
+      <Configuration>Release_VC11</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release_VC14|Win32">
+      <Configuration>Release_VC14</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release_VC14|x64">
+      <Configuration>Release_VC14</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{FDC09E9D-72BB-4C74-B88B-727D9D1D6C4E}</ProjectGuid>
+    <RootNamespace>DotNetUnmanagedApi</RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_VC11|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_VC14|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_VC11|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_VC14|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v110</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_VC11|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_VC14|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_VC11|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_VC14|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|Win32'">bin\$(Configuration)\</OutDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|Win32'">bin\$(Configuration)\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|Win32'">obj\$(Configuration)\DotNetUnmanagedApi\</IntDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|Win32'">obj\$(Configuration)\DotNetUnmanagedApi\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|Win32'">true</LinkIncremental>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|Win32'">true</LinkIncremental>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|x64'">bin\$(Configuration)64\</OutDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|x64'">bin\$(Configuration)64\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|x64'">obj\$(Configuration)64\DotNetUnmanagedApi\</IntDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|x64'">obj\$(Configuration)64\DotNetUnmanagedApi\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|x64'">true</LinkIncremental>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|x64'">true</LinkIncremental>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release_VC11|Win32'">..\..\Managed\DotNet\MapGuideDotNetApi\runtimes\win-x86\</OutDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release_VC14|Win32'">..\..\Managed\DotNet\MapGuideDotNetApi\runtimes\win-x86\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release_VC11|Win32'">obj\$(Configuration)\DotNetUnmanagedApi\</IntDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release_VC14|Win32'">obj\$(Configuration)\DotNetUnmanagedApi\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release_VC11|Win32'">false</LinkIncremental>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release_VC14|Win32'">false</LinkIncremental>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release_VC11|x64'">..\..\Managed\DotNet\MapGuideDotNetApi\runtimes\win-x64\</OutDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release_VC14|x64'">..\..\Managed\DotNet\MapGuideDotNetApi\runtimes\win-x64\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release_VC11|x64'">obj\$(Configuration)64\DotNetUnmanagedApi\</IntDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release_VC14|x64'">obj\$(Configuration)64\DotNetUnmanagedApi\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release_VC11|x64'">false</LinkIncremental>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release_VC14|x64'">false</LinkIncremental>
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|Win32'" />
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|Win32'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|Win32'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|Win32'" />
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|x64'" />
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|x64'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|x64'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|x64'" />
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release_VC11|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release_VC14|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release_VC11|Win32'" />
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release_VC14|Win32'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release_VC11|Win32'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release_VC14|Win32'" />
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release_VC11|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release_VC14|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release_VC11|x64'" />
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release_VC14|x64'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release_VC11|x64'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release_VC14|x64'" />
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|Win32'">MapGuideDotNetUnmanagedApid</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|Win32'">MapGuideDotNetUnmanagedApid</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|x64'">MapGuideDotNetUnmanagedApid</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|x64'">MapGuideDotNetUnmanagedApid</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release_VC11|x64'">MapGuideDotNetUnmanagedApi</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release_VC14|x64'">MapGuideDotNetUnmanagedApi</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release_VC11|Win32'">MapGuideDotNetUnmanagedApi</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release_VC14|Win32'">MapGuideDotNetUnmanagedApi</TargetName>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|Win32'">
+    <PreBuildEvent>
+      <Command>del /Q "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\*.cs"
+if exist "$(ProjectDir)MapGuideApi_Properties.i" del /Q "$(ProjectDir)MapGuideApi_Properties.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l C# -o "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\Constants.cs" -t
+copy "$(ProjectDir)dotnetcore.i" "$(ProjectDir)language.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l C#
+$(SWIG_TOOL_PATH)\swig -c++ -csharp -DDOTNETCORE -DWIN32 -DSWIG_CSHARP_NO_EXCEPTION_HELPER -DSWIG_CSHARP_NO_STRING_HELPER -DSWIG_CSHARP_NO_WSTRING_HELPER -namespace OSGeo.MapGuide -nodefaultctor -nodefaultdtor -module MapGuideDotNetUnmanagedApi -o "$(ProjectDir)MgApi_wrap.cpp" -outdir "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi" MapGuideApi.i
+del /Q ..\..\Managed\DotNet\MapGuideDotNetApi\MapGuideDotNetUnmanagedApi.cs
+</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_USE_32BIT_TIME_T;SWIG_PUBLIC_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACEd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\runtimes\win-x86\native\MapGuideDotNetUnmanagedApid.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB);$(MG_OEM_ACE_LIB_ROOT)\lib\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)MapGuideDotNetUnmanagedApid.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>lib\$(Configuration)\MapGuideDotNetUnmanagedApid.lib</ImportLibrary>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|Win32'">
+    <PreBuildEvent>
+      <Command>del /Q "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\*.cs"
+if exist "$(ProjectDir)MapGuideApi_Properties.i" del /Q "$(ProjectDir)MapGuideApi_Properties.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l C# -o "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\Constants.cs" -t
+copy "$(ProjectDir)dotnetcore.i" "$(ProjectDir)language.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l C#
+$(SWIG_TOOL_PATH)\swig -c++ -csharp -DDOTNETCORE -DWIN32 -DSWIG_CSHARP_NO_EXCEPTION_HELPER -DSWIG_CSHARP_NO_STRING_HELPER -DSWIG_CSHARP_NO_WSTRING_HELPER -namespace OSGeo.MapGuide -nodefaultctor -nodefaultdtor -module MapGuideDotNetUnmanagedApi -o "$(ProjectDir)MgApi_wrap.cpp" -outdir "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi" MapGuideApi.i
+del /Q ..\..\Managed\DotNet\MapGuideDotNetApi\MapGuideDotNetUnmanagedApi.cs
+</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;_USE_32BIT_TIME_T;SWIG_PUBLIC_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACEd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\runtimes\win-x86\native\MapGuideDotNetUnmanagedApid.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB);$(MG_OEM_ACE_LIB_ROOT)\lib\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)MapGuideDotNetUnmanagedApid.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>lib\$(Configuration)\MapGuideDotNetUnmanagedApid.lib</ImportLibrary>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|x64'">
+    <PreBuildEvent>
+      <Command>del /Q "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\*.cs"
+if exist "$(ProjectDir)MapGuideApi_Properties.i" del /Q "$(ProjectDir)MapGuideApi_Properties.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l C# -o "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\Constants.cs" -t
+copy "$(ProjectDir)dotnetcore.i" "$(ProjectDir)language.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l C#
+$(SWIG_TOOL_PATH)\swig -c++ -csharp -DDOTNETCORE -DWIN32 -DSWIG_CSHARP_NO_EXCEPTION_HELPER -DSWIG_CSHARP_NO_STRING_HELPER -DSWIG_CSHARP_NO_WSTRING_HELPER -namespace OSGeo.MapGuide -nodefaultctor -nodefaultdtor -module MapGuideDotNetUnmanagedApi -o "$(ProjectDir)MgApi_wrap.cpp" -outdir "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi" MapGuideApi.i
+del /Q ..\..\Managed\DotNet\MapGuideDotNetApi\MapGuideDotNetUnmanagedApi.cs
+</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;SWIG_PUBLIC_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACEd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\runtimes\win-x64\native\MapGuideDotNetUnmanagedApid.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB64);$(MG_OEM_ACE_LIB_ROOT)\lib64\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)MapGuideDotNetUnmanagedApid.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>lib\$(Configuration)64\MapGuideDotNetUnmanagedApid.lib</ImportLibrary>
+      <TargetMachine>MachineX64</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|x64'">
+    <PreBuildEvent>
+      <Command>del /Q "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\*.cs"
+if exist "$(ProjectDir)MapGuideApi_Properties.i" del /Q "$(ProjectDir)MapGuideApi_Properties.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l C# -o "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\Constants.cs" -t
+copy "$(ProjectDir)dotnetcore.i" "$(ProjectDir)language.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l C#
+$(SWIG_TOOL_PATH)\swig -c++ -csharp -DDOTNETCORE -DWIN32 -DSWIG_CSHARP_NO_EXCEPTION_HELPER -DSWIG_CSHARP_NO_STRING_HELPER -DSWIG_CSHARP_NO_WSTRING_HELPER -namespace OSGeo.MapGuide -nodefaultctor -nodefaultdtor -module MapGuideDotNetUnmanagedApi -o "$(ProjectDir)MgApi_wrap.cpp" -outdir "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi" MapGuideApi.i
+del /Q ..\..\Managed\DotNet\MapGuideDotNetApi\MapGuideDotNetUnmanagedApi.cs
+</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;SWIG_PUBLIC_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACEd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\runtimes\win-x64\native\MapGuideDotNetUnmanagedApid.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB64);$(MG_OEM_ACE_LIB_ROOT)\lib64\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)MapGuideDotNetUnmanagedApid.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>lib\$(Configuration)64\MapGuideDotNetUnmanagedApid.lib</ImportLibrary>
+      <TargetMachine>MachineX64</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_VC11|Win32'">
+    <PreBuildEvent>
+      <Command>del /Q "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\*.cs"
+if exist "$(ProjectDir)MapGuideApi_Properties.i" del /Q "$(ProjectDir)MapGuideApi_Properties.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l C# -o "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\Constants.cs" -t
+copy "$(ProjectDir)dotnetcore.i" "$(ProjectDir)language.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l C#
+$(SWIG_TOOL_PATH)\swig -c++ -csharp -DDOTNETCORE -DWIN32 -DSWIG_CSHARP_NO_EXCEPTION_HELPER -DSWIG_CSHARP_NO_STRING_HELPER -DSWIG_CSHARP_NO_WSTRING_HELPER -namespace OSGeo.MapGuide -nodefaultctor -nodefaultdtor -module MapGuideDotNetUnmanagedApi -o "$(ProjectDir)MgApi_wrap.cpp" -outdir "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi" MapGuideApi.i
+del /Q ..\..\Managed\DotNet\MapGuideDotNetApi\MapGuideDotNetUnmanagedApi.cs
+</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_USE_32BIT_TIME_T;SWIG_PUBLIC_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACE.lib;MgFoundation.lib;MgGeometry.lib;MgPlatformBase.lib;MgMapGuideCommon.lib;HttpHandler.lib;WebApp.lib;WebSupport.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\runtimes\win-x86\native\MapGuideDotNetUnmanagedApi.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB);$(MG_SOURCE_ROOT)\Common\lib\Release;$(MG_SOURCE_ROOT)\Web\lib\Release;$(MG_OEM_ACE_LIB_ROOT)\lib\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)MapGuideDotNetUnmanagedApi.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>lib\$(Configuration)\MapGuideDotNetUnmanagedApi.lib</ImportLibrary>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_VC14|Win32'">
+    <PreBuildEvent>
+      <Command>del /Q "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\*.cs"
+if exist "$(ProjectDir)MapGuideApi_Properties.i" del /Q "$(ProjectDir)MapGuideApi_Properties.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l C# -o "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\Constants.cs" -t
+copy "$(ProjectDir)dotnetcore.i" "$(ProjectDir)language.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l C#
+$(SWIG_TOOL_PATH)\swig -c++ -csharp -DDOTNETCORE -DWIN32 -DSWIG_CSHARP_NO_EXCEPTION_HELPER -DSWIG_CSHARP_NO_STRING_HELPER -DSWIG_CSHARP_NO_WSTRING_HELPER -namespace OSGeo.MapGuide -nodefaultctor -nodefaultdtor -module MapGuideDotNetUnmanagedApi -o "$(ProjectDir)MgApi_wrap.cpp" -outdir "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi" MapGuideApi.i
+del /Q ..\..\Managed\DotNet\MapGuideDotNetApi\MapGuideDotNetUnmanagedApi.cs
+</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_USE_32BIT_TIME_T;SWIG_PUBLIC_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACE.lib;MgFoundation.lib;MgGeometry.lib;MgPlatformBase.lib;MgMapGuideCommon.lib;HttpHandler.lib;WebApp.lib;WebSupport.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\runtimes\win-x86\native\MapGuideDotNetUnmanagedApi.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB);$(MG_SOURCE_ROOT)\Common\lib\Release;$(MG_SOURCE_ROOT)\Web\lib\Release;$(MG_OEM_ACE_LIB_ROOT)\lib\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)\..\..\..\bin\MapGuideDotNetUnmanagedApi.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>lib\$(Configuration)\MapGuideDotNetUnmanagedApi.lib</ImportLibrary>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_VC11|x64'">
+    <PreBuildEvent>
+      <Command>del /Q "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\*.cs"
+if exist "$(ProjectDir)MapGuideApi_Properties.i" del /Q "$(ProjectDir)MapGuideApi_Properties.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l C# -o "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\Constants.cs" -t
+copy "$(ProjectDir)dotnetcore.i" "$(ProjectDir)language.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l C#
+$(SWIG_TOOL_PATH)\swig -c++ -csharp -DDOTNETCORE -DWIN32 -DSWIG_CSHARP_NO_EXCEPTION_HELPER -DSWIG_CSHARP_NO_STRING_HELPER -DSWIG_CSHARP_NO_WSTRING_HELPER -namespace OSGeo.MapGuide -nodefaultctor -nodefaultdtor -module MapGuideDotNetUnmanagedApi -o "$(ProjectDir)MgApi_wrap.cpp" -outdir "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi" MapGuideApi.i
+del /Q ..\..\Managed\DotNet\MapGuideDotNetApi\MapGuideDotNetUnmanagedApi.cs
+</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_WIN64;SWIG_PUBLIC_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACE.lib;MgFoundation.lib;MgGeometry.lib;MgPlatformBase.lib;MgMapGuideCommon.lib;HttpHandler.lib;WebApp.lib;WebSupport.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\runtimes\win-x64\native\MapGuideDotNetUnmanagedApi.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB64);$(MG_SOURCE_ROOT)\Common\lib\Release64;$(MG_SOURCE_ROOT)\Web\lib\Release64;$(MG_OEM_ACE_LIB_ROOT)\lib64\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)MapGuideDotNetUnmanagedApi.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>lib\$(Configuration)64\MapGuideDotNetUnmanagedApi.lib</ImportLibrary>
+      <TargetMachine>MachineX64</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_VC14|x64'">
+    <PreBuildEvent>
+      <Command>del /Q "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\*.cs"
+if exist "$(ProjectDir)MapGuideApi_Properties.i" del /Q "$(ProjectDir)MapGuideApi_Properties.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l C# -o "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\Constants.cs" -t
+copy "$(ProjectDir)dotnetcore.i" "$(ProjectDir)language.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l C#
+$(SWIG_TOOL_PATH)\swig -c++ -csharp -DDOTNETCORE -DWIN32 -DSWIG_CSHARP_NO_EXCEPTION_HELPER -DSWIG_CSHARP_NO_STRING_HELPER -DSWIG_CSHARP_NO_WSTRING_HELPER -namespace OSGeo.MapGuide -nodefaultctor -nodefaultdtor -module MapGuideDotNetUnmanagedApi -o "$(ProjectDir)MgApi_wrap.cpp" -outdir "$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi" MapGuideApi.i
+del /Q ..\..\Managed\DotNet\MapGuideDotNetApi\MapGuideDotNetUnmanagedApi.cs
+</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_WIN64;SWIG_PUBLIC_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACE.lib;MgFoundation.lib;MgGeometry.lib;MgPlatformBase.lib;MgMapGuideCommon.lib;HttpHandler.lib;WebApp.lib;WebSupport.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(ProjectDir)..\..\Managed\DotNet\MapGuideDotNetApi\runtimes\win-x64\native\MapGuideDotNetUnmanagedApi.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB64);$(MG_SOURCE_ROOT)\Common\lib\Release64;$(MG_SOURCE_ROOT)\Web\lib\Release64;$(MG_OEM_ACE_LIB_ROOT)\lib64\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)\..\..\..\bin\MapGuideDotNetUnmanagedApi.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>lib\$(Configuration)64\MapGuideDotNetUnmanagedApi.lib</ImportLibrary>
+      <TargetMachine>MachineX64</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="MgApi_wrap.cpp">
+      <PreprocessToFile Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|Win32'">false</PreprocessToFile>
+      <PreprocessToFile Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|Win32'">false</PreprocessToFile>
+      <PreprocessSuppressLineNumbers Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|Win32'">false</PreprocessSuppressLineNumbers>
+      <PreprocessSuppressLineNumbers Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|Win32'">false</PreprocessSuppressLineNumbers>
+      <PreprocessToFile Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|x64'">false</PreprocessToFile>
+      <PreprocessToFile Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|x64'">false</PreprocessToFile>
+      <PreprocessSuppressLineNumbers Condition="'$(Configuration)|$(Platform)'=='Debug_VC11|x64'">false</PreprocessSuppressLineNumbers>
+      <PreprocessSuppressLineNumbers Condition="'$(Configuration)|$(Platform)'=='Debug_VC14|x64'">false</PreprocessSuppressLineNumbers>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="DotNetUnmanagedApi.rc" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/InitializeWebTier.cpp
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/InitializeWebTier.cpp	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/InitializeWebTier.cpp	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+// What is this file?
+//
+// MapGuideApiGen.xml tries to include this file on Linux, presumably because its default include paths
+// includes WebSupport that would have this file present. In our context, this file doesn't exist so
+// this file just basically re-includes WebSupport.h, just like it would on Windows
+#ifndef _WIN32
+#include "WebSupport.h"
+#endif
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/Makefile
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/Makefile	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/Makefile	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,29 @@
+CPPFLAGS=-g -m64 -fPIC -Wno-write-strings -DDOTNETCORE -I$(MG_OEM_ACE_INCLUDE_DIR) -I$(MG_OEM_XERCES_INCLUDE_DIR) -I$(MG_HTTPHANDLER_INCLUDE_DIR) -I$(MG_WEBAPP_INCLUDE_DIR) -I$(MG_WEBSUPPORT_INCLUDE_DIR) -I$(MG_MDFMODEL_INCLUDE_DIR) -I$(MG_FOUNDATION_INCLUDE_DIR) -I$(MG_GEOMETRY_INCLUDE_DIR) -I$(MG_PLATFORMBASE_INCLUDE_DIR) -I$(MG_MAPGUIDECOMMON_INCLUDE_DIR)
+LDFLAGS=-g -m64 -fPIC -shared
+LDLIBS=-L/usr/local/mapguideopensource-$(MG_VER_FULL)/lib -L/usr/local/mapguideopensource-$(MG_VER_FULL)/webserverextensions/lib -lMgFoundation -lMgGeometry -lMgPlatformBase -lMgMapGuideCommon -lMgHttpHandler -lMgWebApp -lMgWebSupport
+
+all: libMapGuideDotNetUnmanagedApi.so
+
+clean:
+	rm -rf libMapGuideDotNetUnmanagedApi.so
+	rm -rf *.o
+	rm -rf MgApi_wrap.cpp
+	@rm -f MapGuideApi.i
+	@rm -f MapGuideApi_Doc.i
+	@rm -f MapGuideApi_Properties.i
+
+install: libMapGuideDotNetUnmanagedApi.so
+	@cp libMapGuideDotNetUnmanagedApi.so /usr/local/mapguideopensource-$(MG_VER_FULL)/webserverextensions/lib
+
+libMapGuideDotNetUnmanagedApi.so: MgApi_wrap.o
+	g++ $(LDFLAGS) -o libMapGuideDotNetUnmanagedApi.so MgApi_wrap.o $(LDLIBS)
+
+MgApi_wrap.o: MgApi_wrap.cpp
+	g++ $(CPPFLAGS) -c MgApi_wrap.cpp
+
+MgApi_wrap.cpp:
+	if [ -f MapGuideApi_Properties.i ]; then rm MapGuideApi_Properties.i; fi;
+	../../IMake/Linux/IMake ../MapGuideApi/Constants.xml C# MapGuideDotNetCoreApi/Constants.cs
+	cp dotnetcore.i language.i
+	../../IMake/Linux/IMake ../MapGuideApi/MapGuideApiGen.xml C#
+	$(SWIG_TOOL_PATH)/swig -c++ -csharp -DDOTNETCORE -DSWIG_CSHARP_NO_EXCEPTION_HELPER -DSWIG_CSHARP_NO_STRING_HELPER -DSWIG_CSHARP_NO_WSTRING_HELPER -namespace OSGeo.MapGuide -nodefaultctor -nodefaultdtor -module MapGuideDotNetCoreUnmanagedApi -o MgApi_wrap.cpp -outdir ./MapGuideDotNetCoreApi MapGuideApi.i
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/README.md
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/README.md	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/README.md	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,25 @@
+# .net wrapper notes
+
+This document describes the .net language binding for the MapGuide API
+
+# Differences from the official .net binding
+
+ * General
+    * This binding is back to being a monolithic assembly. A split assembly layout is not possible due to missing types in .net Core that made this possible in the full framework (eg. AppDomain)
+    * This assembly is not strong named.
+    * In line with the .net core build system, the unit of consumption is a nuget package.
+    * The nuget package is completely portable and self-contained. It contains both x86 and x64 windows binaries (and also includes a native interop binary for Ubuntu 14.04 64-bit) and because it targets `netstandard2.0` it can be used in both .net Core and Full Framework.
+      * For full .net Framework, the package includes a MSBuild `.targets` file that will ensure that in the consuming project, the supporting windows binaries are copied to the project's output directory
+         * The project must be explicitly set to build for `x86` or `x64` (and not `AnyCPU`) for supporting binaries to be copied.
+    * If you intend to develop/deploy your .net application on .net Core on Linux, the only supported Linux distro is the one that official MapGuide binaries are provided for: Ubuntu 14.04 64-bit. The nuget package includes the necessary native interop library.
+ * API
+    * General
+       * The ```(IntPtr cPtr, bool memOwn)``` constructor signature is no longer public. This was always for SWIG internal use and should not be public. 
+    * MgColor
+       * There is no overload that accepts ```System.Drawing.Color``` as a parameter. (```System.Drawing``` does not exist in .net core)
+    * MgStringCollection
+       * Implements ```IList<string>```
+       * Optional constructor no longer takes a ```StringCollection```, but a ```IEnumerable<string>```
+       * Implicit conversion operators have been removed
+ * New APIs
+    * `MgReadOnlyStream`: A convenience `System.IO.Stream` adapter over any `MgByteReader` instance.
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/dotnetcore.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/dotnetcore.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/DotNet/dotnetcore.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,163 @@
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+%include <wchar.i>
+%include "MapGuideApi_Properties.i"
+%include "../Common/DotNet/string.i"
+%include "../Common/DotNet/coreclr_compat.i"
+%include "../Common/DotNet/sugar.i"
+%include "../Common/DotNet/custom.i"
+
+// Add default namespaces for all generated proxies
+%typemap(csimports) SWIGTYPE %{
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+//These warnings are false positives as a result of SWIG generated code
+#pragma warning disable 0108, 0114
+%}
+
+%pragma(csharp) moduleimports=%{
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+%}
+
+// These methods have to be invoked C-style
+%ignore MgObject::GetClassId;
+%ignore MgObject::GetClassName;
+
+%include "../Common/refcount.i"
+
+// Have these collections implement the .net collection interfaces
+IMPLEMENT_LIST(MgClassDefinitionCollection, MgClassDefinition)
+IMPLEMENT_LIST(MgFeatureSchemaCollection, MgFeatureSchema)
+IMPLEMENT_LIST(MgPropertyDefinitionCollection, MgPropertyDefinition)
+IMPLEMENT_LIST(MgPropertyCollection, MgProperty)
+IMPLEMENT_LIST(MgStringCollection, String)
+IMPLEMENT_LIST(MgLayerCollection, MgLayerBase)
+IMPLEMENT_LIST(MgLayerGroupCollection, MgLayerGroup)
+IMPLEMENT_LIST(MgStringPropertyCollection, MgStringProperty)
+IMPLEMENT_LIST(MgFeatureCommandCollection, MgFeatureCommand)
+IMPLEMENT_LIST(MgMapCollection, MgMapBase)
+IMPLEMENT_LIST(MgMapPlotCollection, MgMapPlot)
+//IMPLEMENT_LIST(MgBatchPropertyCollection, MgPropertyCollection)
+IMPLEMENT_LIST(MgIntCollection, int)
+IMPLEMENT_LIST(MgCoordinateCollection, MgCoordinate)
+IMPLEMENT_LIST(MgPointCollection, MgPoint)
+IMPLEMENT_LIST(MgLineStringCollection, MgLineString)
+IMPLEMENT_LIST(MgLinearRingCollection, MgLinearRing)
+IMPLEMENT_LIST(MgCurveRingCollection, MgCurveRing)
+IMPLEMENT_LIST(MgCurveStringCollection, MgCurveString)
+IMPLEMENT_LIST(MgCurveSegmentCollection, MgCurveSegment)
+IMPLEMENT_LIST(MgCurvePolygonCollection, MgCurvePolygon)
+IMPLEMENT_LIST(MgPolygonCollection, MgPolygon)
+IMPLEMENT_LIST(MgGeometryCollection, MgGeometry)
+IMPLEMENT_READONLY_LIST(MgReadOnlyLayerCollection, MgLayerBase)
+
+///////////////////////////////////////////////////////////
+// STRINGPARAM "in" typemap
+// Marshal a C++ style string to a wstring
+// Allocate temporary memory only when required, otherwise
+// use a buffer on the stack
+//
+#if defined(DOTNETCORE)
+#if defined(WIN32)
+%typemap(in) STRINGPARAM
+{
+    //out typemap - WIN32 (DOTNETCORE)
+    if (NULL == $input)
+    {
+        $1 = STRINGPARAM(L"");
+    }
+    else
+    {
+        $1 = (STRINGPARAM)$input;
+    }
+}
+#else
+%typemap(in) STRINGPARAM
+{
+    //out typemap - LINUX (DOTNETCORE)
+    if (NULL == $input)
+    {
+        $1 = STRINGPARAM(L"");
+    }
+    else
+    {
+        $1 = (STRINGPARAM) X2W((XMLCh*)$input);
+    }
+}
+#endif
+#else
+%typemap(in) STRINGPARAM
+{
+    $1 = (STRINGPARAM) $input;
+}
+#endif
+
+///////////////////////////////////////////////////////////
+// STRING "out" typemap
+// Marshal a string returned by C++ to CSharp
+//
+#if defined(DOTNETCORE)
+#if defined(WIN32)
+%typemap(out) STRING
+{
+    //out typemap - WIN32 (DOTNETCORE)
+    $result = mg_string_callback((int)($1.length()+1)*sizeof(wchar_t));
+    wcscpy((wchar_t*)$result, $1.c_str());
+}
+#else
+%typemap(out) STRING
+{
+    //out typemap - LINUX (DOTNETCORE)
+    xstring u16String;
+    UnicodeString::UTF32toUTF16((const LCh*) $1.c_str(), u16String);
+    $result = mg_string_callback((int)(u16String.length()+1)*sizeof(LCh));
+    XMLString::copyString((XMLCh*)$result, u16String.c_str());
+}
+#endif
+#else
+%typemap(out) STRING
+{
+    $result = mg_string_callback((int)($1.length()+1)*sizeof(wchar_t));
+    wcscpy((wchar_t*)$result, $1.c_str());
+}
+#endif
+
+///////////////////////////////////////////////////////////
+// BYTE_ARRAY_OUT "cstype" typemap
+// Type substitution in .NET and proxy code
+//
+%typemap(cstype) BYTE_ARRAY_OUT "global::System.Byte[]"
+%typemap(imtype) BYTE_ARRAY_OUT "global::System.Byte[]"
+%typemap(ctype)  BYTE_ARRAY_OUT "unsigned char*"
+
+///////////////////////////////////////////////////////////
+// BYTE_ARRAY_IN "cstype" typemap
+// Type substitution in .NET and proxy code
+//
+%typemap(cstype) BYTE_ARRAY_IN "global::System.Byte[]"
+%typemap(imtype) BYTE_ARRAY_IN "global::System.Byte[]"
+%typemap(ctype)  BYTE_ARRAY_IN "unsigned char*"
+
+///////////////////////////////////////////////////////////
+// Global functions
+//
+void MgInitializeWebTier(STRINGPARAM configFile);

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/CMakeLists.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/CMakeLists.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/CMakeLists.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,114 @@
+project(MapGuideJavaApi)
+
+include_directories(${JNI_INCLUDE_DIRS}
+    ${MG_COMMON_DIR}/MdfModel
+    ${MG_COMMON_DIR}/Foundation
+    ${MG_COMMON_DIR}/Geometry
+    ${MG_COMMON_DIR}/PlatformBase
+    ${MG_COMMON_DIR}/MapGuideCommon
+    ${ACE_INCLUDE_DIR}
+    ${MG_WEB_DIR}/HttpHandler
+    ${MG_WEB_DIR}/WebSupport
+    ${MG_WEB_DIR}/WebApp
+    ${XERCESC_INCLUDE_DIR}
+)
+
+set(SWIG_JAVA_DEFS "")
+if (WIN32)
+    set(SWIG_JAVA_DEFS "-DWIN32")
+endif (WIN32)
+
+set(MG_JAVA_OUTPUT_DIR "${MG_PACKAGE_OUTPUT_DIR}/Java/${CMAKE_BUILD_TYPE}/${MG_ARCH}")
+
+set(MapGuideJavaApi_SRCS
+    ${CMAKE_CURRENT_BINARY_DIR}/MgApi_wrap.cpp
+)
+
+set(IMAKE_RELATIVE_RESOLUTION "-r ${CMAKE_CURRENT_SOURCE_DIR}")
+if (UNIX)
+    set(IMAKE_RELATIVE_RESOLUTION "-a")
+endif (UNIX)
+
+message(STATUS "Using IMake at: ${IMAKE_TOOL}")
+add_custom_command(OUTPUT ${MapGuideJavaApi_SRCS}
+    COMMAND ${CMAKE_COMMAND} -E make_directory ${MG_JAVA_OUTPUT_DIR}
+    COMMAND ${CMAKE_COMMAND} -E echo Regenerating constant classes
+    COMMAND ${IMAKE_TOOL} -p ${CMAKE_CURRENT_BINARY_DIR}/../MapGuideApi/Constants.xml 
+            -l Java 
+            ${IMAKE_RELATIVE_RESOLUTION}
+            -o ${MG_JAVA_PROJECT_DIR}/org/osgeo/mapguide
+            -t
+    COMMAND ${CMAKE_COMMAND} -E copy
+            ${CMAKE_CURRENT_BINARY_DIR}/java.i
+            ${CMAKE_CURRENT_BINARY_DIR}/language.i
+    COMMAND ${IMAKE_TOOL} -p ../MapGuideApi/MapGuideApiGen.xml
+            -l Java
+            ${IMAKE_RELATIVE_RESOLUTION}
+            -o ${CMAKE_CURRENT_BINARY_DIR}
+    COMMAND ${CMAKE_COMMAND} -E echo Running SWIG [Java]
+    COMMAND ${SWIG_TOOL} -c++ -java
+            -DJAVA ${SWIG_JAVA_DEFS} 
+            -package org.osgeo.mapguide 
+            -nodefaultctor -nodefaultdtor 
+            -module MapGuideJavaApi 
+            -o "${CMAKE_CURRENT_BINARY_DIR}/MgApi_wrap.cpp" 
+            ${CMAKE_CURRENT_BINARY_DIR}/MapGuideApi.i
+    COMMAND ${CMAKE_COMMAND} -E remove -f 
+            ${MG_JAVA_PROJECT_DIR}/org/osgeo/mapguide/*.class 
+    COMMAND ${MOVE_COMMAND} ${MOVE_ARGS}
+            *.java
+            ${MG_JAVA_PROJECT_DIR}/org/osgeo/mapguide
+)
+
+add_library(MapGuideJavaApi${MG_VERSION_SUFFIX} SHARED ${MapGuideJavaApi_SRCS})
+add_definitions(-DJAVA -DSWIG_PUBLIC_API)
+if (UNIX)
+    add_definitions(-fno-strict-aliasing -fno-var-tracking-assignments)
+endif (UNIX)
+if (MSVC)
+    add_definitions(/bigobj)
+    set_target_properties(MapGuideJavaApi${MG_VERSION_SUFFIX} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${MG_JAVA_OUTPUT_DIR} )
+    set_target_properties(MapGuideJavaApi${MG_VERSION_SUFFIX} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${MG_JAVA_OUTPUT_DIR} )
+    set_target_properties(MapGuideJavaApi${MG_VERSION_SUFFIX} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${MG_JAVA_OUTPUT_DIR} )
+endif (MSVC)
+add_dependencies(MapGuideJavaApi${MG_VERSION_SUFFIX} IMake)
+
+# if (UNIX)
+#     install(TARGETS MapGuideJavaApi${MG_VERSION_SUFFIX} DESTINATION ${LIB_INSTALL_DIR} COMPONENT ${MG_COMPONENT})
+#     install_symlink(${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/libMapGuideJavaApi${MG_VERSION_SUFFIX}.so libMapGuideJavaApi.so ${MG_COMPONENT})
+#     set(MG_JARS
+#         "${MG_JAVA_OUTPUT_DIR}/MapGuideApi.jar"
+#         "${MG_JAVA_OUTPUT_DIR}/MapGuideApi-sources.jar"
+#     )
+#     install(FILES ${MG_JARS} DESTINATION ${MG_WWWROOT}/WEB-INF/lib)
+# endif (UNIX)
+target_link_libraries(MapGuideJavaApi${MG_VERSION_SUFFIX}
+    ${ACE_LIBRARY}
+    #${JNI_LIBRARIES}
+    ${MG_FOUNDATION_LIBRARY}
+    ${MG_GEOMETRY_LIBRARY}
+    ${MG_PLATFORMBASE_LIBRARY}
+    ${MG_COMMON_LIBRARY}
+    ${MG_HTTPHANDLER_LIBRARY}
+    ${MG_WEBAPP_LIBRARY}
+    ${MG_MDFMODEL_LIBRARY}
+    ${MG_MDFPARSER_LIBRARY}
+    ${MG_WEBSUPPORT_LIBRARY}
+    #MgFoundation${MG_VERSION_SUFFIX}
+    #MgGeometry${MG_VERSION_SUFFIX}
+    #MgPlatformBase${MG_VERSION_SUFFIX}
+    #MgMapGuideCommon${MG_VERSION_SUFFIX}
+    #MgHttpHandler${MG_VERSION_SUFFIX}
+    #MgMdfModel${MG_VERSION_SUFFIX}
+    #MgMdfParser${MG_VERSION_SUFFIX}
+    ${XERCESC_LIBRARIES}
+    #MgWebApp${MG_VERSION_SUFFIX}
+)
+
+# Only needed for Linux
+if (UNIX)
+    file(COPY "InitializeWebTier.cpp" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+    install( TARGETS MapGuideJavaApi${MG_VERSION_SUFFIX} DESTINATION "${MG_JAVA_OUTPUT_DIR}/${MG_DISTRO}" )
+endif (UNIX)
+
+file(COPY "java.i" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/COPYRIGHT
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/COPYRIGHT	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/COPYRIGHT	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,19 @@
+Where copyrights have not been included in the source file, this
+copyright shall be used for all source files in this directory
+and subdirectories.
+
+Copyright (C) 2004-2011 by Autodesk, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of version 2.1 of the GNU Lesser
+General Public License as published by the Free Software Foundation.
+
+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 St, Fifth Floor, Boston, MA  02110-1301  USA
+

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/InitializeWebTier.cpp
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/InitializeWebTier.cpp	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/InitializeWebTier.cpp	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+// What is this file?
+//
+// MapGuideApiGen.xml tries to include this file on Linux, presumably because its default include paths
+// includes WebSupport that would have this file present. In our context, this file doesn't exist so
+// this file just basically re-includes WebSupport.h, just like it would on Windows
+#ifndef _WIN32
+#include "WebSupport.h"
+#endif
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/JavaApi.rc
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/JavaApi.rc	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/JavaApi.rc	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,96 @@
+// Microsoft Visual C++ generated resource script.
+//
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+    "#include ""winresrc.h""\r\n"
+    "\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 3,1,0,0
+ PRODUCTVERSION 3,1,0,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "Open Source Geospatial Foundation"
+            VALUE "FileDescription", "MapGuideJavaApiEx Dynamic Link Library"
+            VALUE "FileVersion", "3, 1, 0, 0"
+            VALUE "InternalName", "MapGuideJavaApiEx"
+            VALUE "LegalCopyright", "Copyright (C) 2006-2011 by Autodesk, Inc."
+            VALUE "OriginalFilename", "MapGuideJavaApiEx.dll"
+            VALUE "ProductName", "MapGuide Open Source"
+            VALUE "ProductVersion", "3, 1, 0, 0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/JavaApi.vcxproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/JavaApi.vcxproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/JavaApi.vcxproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,280 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{C02F44CB-D916-428D-A1A5-04E225A2A4EC}</ProjectGuid>
+    <RootNamespace>JavaApi</RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\packages\Java\$(Configuration)\x86\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\obj\$(Configuration)\JavaApi\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\..\packages\Java\$(Configuration)\x64\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\obj\$(Configuration)64\JavaApi\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\packages\Java\$(Configuration)\x86\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\obj\$(Configuration)\JavaApi\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\..\packages\Java\$(Configuration)\x64\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\obj\$(Configuration)64\JavaApi\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">MapGuideJavaApid</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">MapGuideJavaApid</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MapGuideJavaApi</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MapGuideJavaApi</TargetName>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <PreBuildEvent>
+      <Command>"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l Java -o . -t
+copy .\java.i .\language.i
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l Java
+$(SWIG_TOOL_PATH)\swig -c++ -java -DJAVA -DWIN32 -package org.osgeo.mapguide -nodefaultctor -nodefaultdtor -module MapGuideJavaApi -o MgApi_wrap.cpp MapGuideApi.i
+move /Y *.java "$(ProjectDir)..\..\Managed\Java\org\osgeo\mapguide"
+if exist "$(ProjectDir)..\..\Managed\Java\org\osgeo\mapguide\*.class" del "$(ProjectDir)..\..\Managed\Java\org\osgeo\mapguide\*.class"</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);$(JAVA_HOME)\include;$(JAVA_HOME)\include\win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;JAVA;SWIG_PUBLIC_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACEd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(OutDir)MapGuideJavaApid.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB);$(MG_OEM_ACE_LIB_ROOT)\lib\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)MapGuideJavaApid.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>..\..\lib\$(Configuration)\MapGuideJavaApid.lib</ImportLibrary>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <PreBuildEvent>
+      <Command>"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l Java -o . -t
+copy .\java.i .\language.i
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l Java
+$(SWIG_TOOL_PATH)\swig -c++ -java -DJAVA -DWIN32 -package org.osgeo.mapguide -nodefaultctor -nodefaultdtor -module MapGuideJavaApi -o MgApi_wrap.cpp MapGuideApi.i
+move /Y "$(ProjectDir)*.java" "$(ProjectDir)..\..\Managed\Java\org\osgeo\mapguide"
+if exist "$(ProjectDir)..\..\Managed\Java\org\osgeo\mapguide\*.class" del "$(ProjectDir)..\..\Managed\Java\org\osgeo\mapguide\*.class"</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);$(JAVA_HOME)\include;$(JAVA_HOME)\include\win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;JAVA;SWIG_PUBLIC_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACEd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(OutDir)MapGuideJavaApid.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB64);$(MG_OEM_ACE_LIB_ROOT)\lib64\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)MapGuideJavaApid.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>..\..\lib\$(Configuration)64\MapGuideJavaApid.lib</ImportLibrary>
+      <TargetMachine>MachineX64</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <PreBuildEvent>
+      <Command>"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l Java -o . -t
+copy .\java.i .\language.i
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l Java
+$(SWIG_TOOL_PATH)\swig -c++ -java -DJAVA -DWIN32 -package org.osgeo.mapguide -nodefaultctor -nodefaultdtor -module MapGuideJavaApi -o MgApi_wrap.cpp MapGuideApi.i
+move /Y *.java "$(ProjectDir)..\..\Managed\Java\org\osgeo\mapguide"
+if exist "$(ProjectDir)..\..\Managed\Java\org\osgeo\mapguide\*.class" del "$(ProjectDir)..\..\Managed\Java\org\osgeo\mapguide\*.class"</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);$(JAVA_HOME)\include;$(JAVA_HOME)\include\win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;JAVA;SWIG_PUBLIC_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACE.lib;MgFoundation.lib;MgGeometry.lib;MgPlatformBase.lib;MgMapGuideCommon.lib;HttpHandler.lib;WebApp.lib;WebSupport.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(OutDir)MapGuideJavaApi.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB);$(MG_OEM_ACE_LIB_ROOT)\lib\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)MapGuideJavaApi.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>..\..\lib\$(Configuration)\MapGuideJavaApi.lib</ImportLibrary>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <PreBuildEvent>
+      <Command>"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l Java -o . -t
+copy .\java.i .\language.i
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l Java
+$(SWIG_TOOL_PATH)\swig -c++ -java -DJAVA -DWIN32 -package org.osgeo.mapguide -nodefaultctor -nodefaultdtor -module MapGuideJavaApi -o MgApi_wrap.cpp MapGuideApi.i
+move /Y *.java "$(ProjectDir)..\..\Managed\Java\org\osgeo\mapguide"
+if exist "$(ProjectDir)..\..\Managed\Java\org\osgeo\mapguide\*.class" del "$(ProjectDir)..\..\Managed\Java\org\osgeo\mapguide\*.class"</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);$(JAVA_HOME)\include;$(JAVA_HOME)\include\win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;JAVA;SWIG_PUBLIC_API;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACE.lib;MgFoundation.lib;MgGeometry.lib;MgPlatformBase.lib;MgMapGuideCommon.lib;HttpHandler.lib;WebApp.lib;WebSupport.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(OutDir)MapGuideJavaApi.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB64);$(MG_OEM_ACE_LIB_ROOT)\lib64\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)MapGuideJavaApi.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>..\..\lib\$(Configuration)64\MapGuideJavaApi.lib</ImportLibrary>
+      <TargetMachine>MachineX64</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <None Include="catchall.code" />
+    <None Include="getclassid.code" />
+    <None Include="java.i" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="JavaApi.rc" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="MgApi_wrap.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\Common\Foundation\Foundation.vcxproj">
+      <Project>{a82adc7d-4da4-42f2-9bf6-df5dcfb44425}</Project>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\Common\Geometry\Geometry.vcxproj">
+      <Project>{d954daac-e305-40ce-b3f3-c229a0bef4f0}</Project>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\Common\MapGuideCommon\MapGuideCommon.vcxproj">
+      <Project>{5287a594-4d4f-43fe-a281-e279ab708cf1}</Project>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
+    <ProjectReference Include="..\..\..\Common\PlatformBase\PlatformBase.vcxproj">
+      <Project>{f7334b1b-0efa-47e3-8e66-df158e61b7e4}</Project>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
+    <ProjectReference Include="..\HttpHandler\HttpHandler.vcxproj">
+      <Project>{78619d0e-d3f9-4ddf-b90e-f99cb03dfc44}</Project>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
+    <ProjectReference Include="..\WebApp\WebApp.vcxproj">
+      <Project>{b797917b-6842-467c-8b14-e00b76a91247}</Project>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
+    <ProjectReference Include="..\WebSupport\WebSupport.vcxproj">
+      <Project>{795b1b0e-4ec8-469d-b641-e26324266fbf}</Project>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/Makefile.am
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/Makefile.am	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/Makefile.am	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,468 @@
+AUTOMAKE_OPTIONS = foreign
+
+EXTRA_DIST = getclassid.code \
+             catchall.code \
+             java.i \
+             JavaApiEx.rc \
+             JavaApiEx.vcproj \
+             org/osgeo/mapguide/AppThrowable.java \
+             org/osgeo/mapguide/MgBase64.java \
+             org/osgeo/mapguide/MgLocalizer.java
+
+prefix=@prefix@/webserverextensions
+
+INCLUDES = -I../../../Common/MdfModel \
+           -I../../../Common/Foundation \
+           -I../../../Common/Geometry \
+           -I../../../Common/PlatformBase \
+           -I../../../Common/MapGuideCommon \
+           -I$(JAVA_HOME)/include \
+           -I$(JAVA_HOME)/include/linux \
+           -I../../../Oem/ACE/ACE_wrappers \
+           -I../HttpHandler \
+           -I../WebSupport \
+           -I../WebApp \
+           -I../../../Oem/dbxml/xerces-c-src/src
+
+BUILT_SOURCES = MgApi_wrap.cpp
+
+lib_LTLIBRARIES = libMapGuideJavaApiEx.la
+
+nodist_libMapGuideJavaApiEx_la_SOURCES = MgApi_wrap.cpp
+
+AM_CXXFLAGS = @CXXFLAGS@ -fno-strict-aliasing -fno-var-tracking-assignments -DJAVA
+
+libMapGuideJavaApiEx_la_LIBADD = \
+    -lACE \
+    -lMgFoundation \
+    -lMgGeometry \
+    -lMgPlatformBase \
+    -lMgMapGuideCommon \
+    -lMgHttpHandler \
+    -lMgMdfModel \
+    -lMgMdfParser \
+    -lxerces-c \
+    -lMgWebApp
+
+libMapGuideJavaApiEx_la_LDFLAGS = -release $(PACKAGE_VERSION) \
+    -L../../../Oem/ACE/ACE_wrappers/ace \
+    -L../../../Common/MdfModel \
+    -L../../../Common/MdfParser \
+    -L../../../Common/Foundation \
+    -L../../../Common/Geometry \
+    -L../../../Common/PlatformBase \
+    -L../../../Common/MapGuideCommon \
+    -L../HttpHandler \
+    -L../WebApp \
+    -L../../../Oem/dbxml/install/lib
+
+MgApi_wrap.cpp : \
+    ../WebApp/WebApp.h \
+    ../WebSupport/InitializeWebTier.cpp \
+    ../../../Common/MdfModel/UnicodeString.h \
+    ../HttpHandler/HttpHandlerApiDllExport.h \
+    ../HttpHandler/HttpHandler.h \
+    ../../../Common/Foundation/System/Object.h \
+    ../../../Common/Foundation/System/Disposable.h \
+    ../../../Common/Foundation/System/GuardDisposable.h \
+    ../../../Common/Foundation/System/Serializable.h \
+    ../../../Common/Foundation/System/NamedSerializable.h \
+    ../../../Common/Foundation/Data/Collection.h \
+    ../../../Common/Foundation/Data/Property.h \
+    ../../../Common/Foundation/System/DisposableCollection.h \
+    ../../../Common/Foundation/Data/BatchPropertyCollection.h \
+    ../../../Common/Foundation/Data/ByteReader.h \
+    ../../../Common/Foundation/Data/ByteSource.h \
+    ../../../Common/Foundation/Data/ByteSink.h \
+    ../../../Common/Foundation/Data/DateTime.h \
+    ../../../Common/Foundation/Data/NullableProperty.h \
+    ../../../Common/Foundation/Data/PropertyCollection.h \
+    ../../../Common/Foundation/Data/PropertyDefinition.h \
+    ../../../Common/Foundation/Data/StringCollection.h \
+    ../../../Common/Foundation/Data/StringProperty.h \
+    ../../../Common/Foundation/Data/FeaturePropertyType.h \
+    ../../../Common/Foundation/Data/MimeType.h \
+    ../../../Common/Foundation/Data/PropertyType.h \
+    ../../../Common/Foundation/Exception/Exception.h \
+    ../../../Common/Foundation/Exception/SystemException.h \
+    ../../../Common/Foundation/Exception/IoException.h \
+    ../../../Common/Foundation/Exception/FileIoException.h \
+    ../../../Common/Foundation/Exception/ApplicationException.h \
+    ../../../Common/Foundation/Exception/ThirdPartyException.h \
+    ../../../Common/Foundation/Exception/StreamIoException.h \
+    ../../../Common/Foundation/Exception/OutOfRangeException.h \
+    ../../../Common/Foundation/Exception/ArgumentOutOfRangeException.h \
+    ../../../Common/Foundation/Exception/ClassNotFoundException.h \
+    ../../../Common/Foundation/Exception/ConfigurationException.h \
+    ../../../Common/Foundation/Exception/ConfigurationLoadFailedException.h \
+    ../../../Common/Foundation/Exception/ConfigurationSaveFailedException.h \
+    ../../../Common/Foundation/Exception/DateTimeException.h \
+    ../../../Common/Foundation/Exception/DecryptionException.h \
+    ../../../Common/Foundation/Exception/DirectoryNotFoundException.h \
+    ../../../Common/Foundation/Exception/DivideByZeroException.h \
+    ../../../Common/Foundation/Exception/DomainException.h \
+    ../../../Common/Foundation/Exception/DuplicateDirectoryException.h \
+    ../../../Common/Foundation/Exception/DuplicateFileException.h \
+    ../../../Common/Foundation/Exception/DuplicateObjectException.h \
+    ../../../Common/Foundation/Exception/EncryptionException.h \
+    ../../../Common/Foundation/Exception/FileNotFoundException.h \
+    ../../../Common/Foundation/Exception/IndexOutOfRangeException.h \
+    ../../../Common/Foundation/Exception/InvalidArgumentException.h \
+    ../../../Common/Foundation/Exception/InvalidCastException.h \
+    ../../../Common/Foundation/Exception/InvalidOperationException.h \
+    ../../../Common/Foundation/Exception/InvalidPropertyTypeException.h \
+    ../../../Common/Foundation/Exception/InvalidStreamHeaderException.h \
+    ../../../Common/Foundation/Exception/LengthException.h \
+    ../../../Common/Foundation/Exception/LogicException.h \
+    ../../../Common/Foundation/Exception/NotFiniteNumberException.h \
+    ../../../Common/Foundation/Exception/NotImplementedException.h \
+    ../../../Common/Foundation/Exception/NullArgumentException.h \
+    ../../../Common/Foundation/Exception/NullPropertyValueException.h \
+    ../../../Common/Foundation/Exception/NullReferenceException.h \
+    ../../../Common/Foundation/Exception/ObjectNotFoundException.h \
+    ../../../Common/Foundation/Exception/OutOfMemoryException.h \
+    ../../../Common/Foundation/Exception/OverflowException.h \
+    ../../../Common/Foundation/Exception/PlatformNotSupportedException.h \
+    ../../../Common/Foundation/Exception/ResourcesException.h \
+    ../../../Common/Foundation/Exception/ResourcesLoadFailedException.h \
+    ../../../Common/Foundation/Exception/RuntimeException.h \
+    ../../../Common/Foundation/Exception/TemporaryFileNotAvailableException.h \
+    ../../../Common/Foundation/Exception/UnclassifiedException.h \
+    ../../../Common/Foundation/Exception/UnderflowException.h \
+    ../../../Common/Foundation/Exception/XmlException.h \
+    ../../../Common/Foundation/Exception/XmlParserException.h \
+    ../../../Common/Geometry/GeometricEntity.h \
+    ../../../Common/Geometry/Geometry.h \
+    ../../../Common/Geometry/GeometryComponent.h \
+    ../../../Common/Geometry/Curve.h \
+    ../../../Common/Geometry/CurveSegment.h \
+    ../../../Common/Geometry/Ring.h \
+    ../../../Common/Geometry/Region.h \
+    ../../../Common/Geometry/AgfReaderWriter.h \
+    ../../../Common/Geometry/AggregateGeometry.h \
+    ../../../Common/Geometry/ArcSegment.h \
+    ../../../Common/Geometry/Coordinate.h \
+    ../../../Common/Geometry/CoordinateCollection.h \
+    ../../../Common/Geometry/CoordinateIterator.h \
+    ../../../Common/Geometry/CoordinateXY.h \
+    ../../../Common/Geometry/CoordinateXYM.h \
+    ../../../Common/Geometry/CoordinateXYZ.h \
+    ../../../Common/Geometry/CoordinateXYZM.h \
+    ../../../Common/Geometry/CurvePolygon.h \
+    ../../../Common/Geometry/CurvePolygonCollection.h \
+    ../../../Common/Geometry/CurveRing.h \
+    ../../../Common/Geometry/CurveRingCollection.h \
+    ../../../Common/Geometry/CurveSegmentCollection.h \
+    ../../../Common/Geometry/CurveString.h \
+    ../../../Common/Geometry/CurveStringCollection.h \
+    ../../../Common/Geometry/Envelope.h \
+    ../../../Common/Geometry/GeometryCollection.h \
+    ../../../Common/Geometry/GeometryFactory.h \
+    ../../../Common/Geometry/LinearRing.h \
+    ../../../Common/Geometry/LinearRingCollection.h \
+    ../../../Common/Geometry/LinearSegment.h \
+    ../../../Common/Geometry/LineString.h \
+    ../../../Common/Geometry/LineStringCollection.h \
+    ../../../Common/Geometry/Measure.h \
+    ../../../Common/Geometry/MultiCurvePolygon.h \
+    ../../../Common/Geometry/MultiCurveString.h \
+    ../../../Common/Geometry/MultiGeometry.h \
+    ../../../Common/Geometry/MultiLineString.h \
+    ../../../Common/Geometry/MultiPoint.h \
+    ../../../Common/Geometry/MultiPolygon.h \
+    ../../../Common/Geometry/Point.h \
+    ../../../Common/Geometry/PointCollection.h \
+    ../../../Common/Geometry/Polygon.h \
+    ../../../Common/Geometry/PolygonCollection.h \
+    ../../../Common/Geometry/Transform.h \
+    ../../../Common/Geometry/WktReaderWriter.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystem.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemDictionaryBase.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemCatalog.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemCategory.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemCategoryDictionary.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemDatum.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemDatumDictionary.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformDefParams.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticPathElement.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticPath.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticPathDictionary.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformDef.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformGridFile.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticAnalyticalTransformDefParams.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticInterpolationTransformDefParams.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticMultipleRegressionTransformDefParams.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticStandaloneTransformDefParams.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformDefDictionary.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemDictionary.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemDictionaryUtility.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemEllipsoid.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemEllipsoidDictionary.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemEnum.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemEnumInteger32.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemFactory.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemFilter.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemFilterInteger32.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemFormatConverter.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformation.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGrids.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemMathComparator.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemMeasure.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemMgrs.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemProjectionInformation.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemTransform.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemUnitInformation.h \
+    ../../../Common/Geometry/Exception/CoordinateSystemComputationFailedException.h \
+    ../../../Common/Geometry/Exception/CoordinateSystemConversionFailedException.h \
+    ../../../Common/Geometry/Exception/CoordinateSystemInitializationFailedException.h \
+    ../../../Common/Geometry/Exception/CoordinateSystemLoadFailedException.h \
+    ../../../Common/Geometry/Exception/CoordinateSystemMeasureFailedException.h \
+    ../../../Common/Geometry/Exception/CoordinateSystemTransformFailedException.h \
+    ../../../Common/Geometry/Exception/GeometryException.h \
+    ../../../Common/Geometry/Exception/InvalidCoordinateSystemException.h \
+    ../../../Common/Geometry/Exception/InvalidCoordinateSystemTypeException.h \
+    ../../../Common/Geometry/Exception/InvalidCoordinateSystemUnitsException.h \
+    ../../../Common/Geometry/CoordinateDimension.h \
+    ../../../Common/Geometry/GeometricEntityType.h \
+    ../../../Common/Geometry/GeometricPathInstructionType.h \
+    ../../../Common/Geometry/GeometryComponentType.h \
+    ../../../Common/Geometry/GeometryType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemCodeFormat.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemErrorCode.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformationMethod.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGridOrientation.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGridSpecializationType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemMgrsGridLevel.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemMgrsLetteringScheme.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemMgrsGridSquarePosition.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemProjectionCode.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemProjectionFormatType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemProjectionLogicalType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemProjectionParameterType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemUnitCode.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemUnitType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemWktFlavor.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformDefType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticAnalyticalTransformationMethod.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticMultipleRegressionTransformationMethod.h \
+	../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticStandaloneTransformationMethod.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformGridFileFormat.h \
+    ../../../Common/PlatformBase/Data/BlobProperty.h \
+    ../../../Common/PlatformBase/Data/BooleanProperty.h \
+    ../../../Common/PlatformBase/Data/ByteProperty.h \
+    ../../../Common/PlatformBase/Data/ClobProperty.h \
+    ../../../Common/PlatformBase/Data/Color.h \
+    ../../../Common/PlatformBase/Data/DateTimeProperty.h \
+    ../../../Common/PlatformBase/Data/DoubleProperty.h \
+    ../../../Common/PlatformBase/Data/Int16Property.h \
+    ../../../Common/PlatformBase/Data/Int32Property.h \
+    ../../../Common/PlatformBase/Data/Int64Property.h \
+    ../../../Common/PlatformBase/Data/IntCollection.h \
+    ../../../Common/PlatformBase/Data/PropertyDefinitionCollection.h \
+    ../../../Common/PlatformBase/Data/SingleProperty.h \
+    ../../../Common/PlatformBase/Data/StringPropertyCollection.h \
+    ../../../Common/PlatformBase/Exception/ArrayTypeMismatchException.h \
+    ../../../Common/PlatformBase/Exception/DuplicateResourceDataException.h \
+    ../../../Common/PlatformBase/Exception/DuplicateResourceException.h \
+    ../../../Common/PlatformBase/Exception/EmptyFeatureSetException.h \
+    ../../../Common/PlatformBase/Exception/FdoException.h \
+    ../../../Common/PlatformBase/Exception/FeatureServiceException.h \
+    ../../../Common/PlatformBase/Exception/InvalidMapDefinitionException.h \
+    ../../../Common/PlatformBase/Exception/InvalidRepositoryNameException.h \
+    ../../../Common/PlatformBase/Exception/InvalidRepositoryTypeException.h \
+    ../../../Common/PlatformBase/Exception/InvalidResourceDataNameException.h \
+    ../../../Common/PlatformBase/Exception/InvalidResourceDataTypeException.h \
+    ../../../Common/PlatformBase/Exception/InvalidResourceNameException.h \
+    ../../../Common/PlatformBase/Exception/InvalidResourcePathException.h \
+    ../../../Common/PlatformBase/Exception/InvalidResourcePreProcessingTypeException.h \
+    ../../../Common/PlatformBase/Exception/InvalidResourceTypeException.h \
+    ../../../Common/PlatformBase/Exception/LayerNotFoundException.h \
+    ../../../Common/PlatformBase/Exception/ResourceBusyException.h \
+    ../../../Common/PlatformBase/Exception/ResourceDataNotFoundException.h \
+    ../../../Common/PlatformBase/Exception/ResourceNotFoundException.h \
+    ../../../Common/PlatformBase/Exception/ServiceNotAvailableException.h \
+    ../../../Common/PlatformBase/Exception/ServiceNotSupportedException.h \
+    ../../../Common/PlatformBase/Exception/UserNotFoundException.h \
+    ../../../Common/PlatformBase/Services/FeatureCommand.h \
+    ../../../Common/PlatformBase/Services/FeatureQueryOptions.h \
+    ../../../Common/PlatformBase/Services/FeatureSourceParams.h \
+    ../../../Common/PlatformBase/Services/Reader.h \
+    ../../../Common/PlatformBase/Services/Service.h \
+    ../../../Common/PlatformBase/Services/BaseService.h \
+    ../../../Common/PlatformBase/Services/ClassDefinition.h \
+    ../../../Common/PlatformBase/Services/ClassDefinitionCollection.h \
+    ../../../Common/PlatformBase/Services/FileFeatureSourceParams.h \
+    ../../../Common/PlatformBase/Services/DataPropertyDefinition.h \
+    ../../../Common/PlatformBase/Services/DataReader.h \
+    ../../../Common/PlatformBase/Services/DeleteFeatures.h \
+    ../../../Common/PlatformBase/Services/FeatureAggregateOptions.h \
+    ../../../Common/PlatformBase/Services/FeatureCommandCollection.h \
+    ../../../Common/PlatformBase/Services/FeatureProperty.h \
+    ../../../Common/PlatformBase/Services/FeatureReader.h \
+    ../../../Common/PlatformBase/Services/FeatureSchema.h \
+    ../../../Common/PlatformBase/Services/FeatureSchemaCollection.h \
+    ../../../Common/PlatformBase/Services/FeatureService.h \
+    ../../../Common/PlatformBase/Services/GeometricPropertyDefinition.h \
+    ../../../Common/PlatformBase/Services/GeometryProperty.h \
+    ../../../Common/PlatformBase/Services/GeometryTypeInfo.h \
+    ../../../Common/PlatformBase/Services/GwsFeatureReader.h \
+    ../../../Common/PlatformBase/Services/InsertFeatures.h \
+    ../../../Common/PlatformBase/Services/LongTransactionReader.h \
+    ../../../Common/PlatformBase/Services/ObjectPropertyDefinition.h \
+    ../../../Common/PlatformBase/Services/Parameter.h \
+    ../../../Common/PlatformBase/Services/ParameterCollection.h \
+    ../../../Common/PlatformBase/Services/Raster.h \
+    ../../../Common/PlatformBase/Services/RasterProperty.h \
+    ../../../Common/PlatformBase/Services/RasterPropertyDefinition.h \
+    ../../../Common/PlatformBase/Services/Resource.h \
+    ../../../Common/PlatformBase/Services/ResourceIdentifier.h \
+    ../../../Common/PlatformBase/Services/ResourceService.h \
+    ../../../Common/PlatformBase/Services/SpatialContextReader.h \
+    ../../../Common/PlatformBase/Services/SqlDataReader.h \
+    ../../../Common/PlatformBase/Services/Transaction.h \
+    ../../../Common/PlatformBase/Services/UpdateFeatures.h \
+    ../../../Common/PlatformBase/Services/Warnings.h \
+    ../../../Common/PlatformBase/Services/BaseServiceDefs.h \
+    ../../../Common/PlatformBase/Services/FeatureCommandType.h \
+    ../../../Common/PlatformBase/Services/FeatureGeometricType.h \
+    ../../../Common/PlatformBase/Services/FeatureSpatialOperations.h \
+    ../../../Common/PlatformBase/Services/ObjectPropertyType.h \
+    ../../../Common/PlatformBase/Services/OrderingOption.h \
+    ../../../Common/PlatformBase/Services/ParameterDirection.h \
+    ../../../Common/PlatformBase/Services/RasterTypes.h \
+    ../../../Common/PlatformBase/Services/ReaderType.h \
+    ../../../Common/PlatformBase/Services/ResourceDefs.h \
+    ../../../Common/PlatformBase/Services/SpatialContextExtentType.h \
+    ../../../Common/PlatformBase/MapLayer/LayerBase.h \
+    ../../../Common/PlatformBase/MapLayer/LayerCollection.h \
+    ../../../Common/PlatformBase/MapLayer/LayerGroup.h \
+    ../../../Common/PlatformBase/MapLayer/LayerGroupCollection.h \
+    ../../../Common/PlatformBase/MapLayer/MapBase.h \
+    ../../../Common/PlatformBase/MapLayer/MapCollection.h \
+    ../../../Common/PlatformBase/MapLayer/ReadOnlyLayerCollection.h \
+    ../../../Common/PlatformBase/MapLayer/SelectionBase.h \
+    ../../../Common/PlatformBase/MapLayer/LayerGroupType.h \
+    ../../../Common/PlatformBase/MapLayer/LayerType.h \
+    ../../../Common/MapGuideCommon/Exception/AuthenticationFailedException.h \
+    ../../../Common/MapGuideCommon/Exception/ConnectionFailedException.h \
+    ../../../Common/MapGuideCommon/Exception/ConnectionNotOpenException.h \
+    ../../../Common/MapGuideCommon/Exception/DbException.h \
+    ../../../Common/MapGuideCommon/Exception/DbXmlException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateGroupException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateNameException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateParameterException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateRepositoryException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateRoleException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateServerException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateSessionException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateUserException.h \
+    ../../../Common/MapGuideCommon/Exception/DwfException.h \
+    ../../../Common/MapGuideCommon/Exception/DwfSectionNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/DwfSectionResourceNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/EndOfStreamException.h \
+    ../../../Common/MapGuideCommon/Exception/GroupNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidDwfPackageException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidDwfSectionException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidFeatureSourceException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidIpAddressException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidLicenseException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidLogEntryException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidPasswordException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidPrintLayoutFontSizeUnitsException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidPrintLayoutPositionUnitsException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidPrintLayoutSizeUnitsException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidServerNameException.h \
+    ../../../Common/MapGuideCommon/Exception/LicenseException.h \
+    ../../../Common/MapGuideCommon/Exception/LicenseExpiredException.h \
+    ../../../Common/MapGuideCommon/Exception/OperationProcessingException.h \
+    ../../../Common/MapGuideCommon/Exception/ParameterNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/PathTooLongException.h \
+    ../../../Common/MapGuideCommon/Exception/PortNotAvailableException.h \
+    ../../../Common/MapGuideCommon/Exception/PrintToScaleModeNotSelectedException.h \
+    ../../../Common/MapGuideCommon/Exception/RepositoryCreationFailedException.h \
+    ../../../Common/MapGuideCommon/Exception/RepositoryNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/RepositoryNotOpenException.h \
+    ../../../Common/MapGuideCommon/Exception/RepositoryOpenFailedException.h \
+    ../../../Common/MapGuideCommon/Exception/RoleNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/ServerNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/ServerNotOnlineException.h \
+    ../../../Common/MapGuideCommon/Exception/SessionExpiredException.h \
+    ../../../Common/MapGuideCommon/Exception/SessionNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/UnauthorizedAccessException.h \
+    ../../../Common/MapGuideCommon/Exception/UriFormatException.h \
+    ../../../Common/MapGuideCommon/Services/DrawingService.h \
+    ../../../Common/MapGuideCommon/Services/DwfVersion.h \
+    ../../../Common/MapGuideCommon/Services/FeatureInformation.h \
+    ../../../Common/MapGuideCommon/Services/KmlService.h \
+    ../../../Common/MapGuideCommon/Services/Layout.h \
+    ../../../Common/MapGuideCommon/Services/MappingService.h \
+    ../../../Common/MapGuideCommon/Services/MapPlot.h \
+    ../../../Common/MapGuideCommon/Services/MapPlotCollection.h \
+    ../../../Common/MapGuideCommon/Services/PackageStatusInformation.h \
+    ../../../Common/MapGuideCommon/Services/PlotSpecification.h \
+    ../../../Common/MapGuideCommon/Services/ProfilingService.h \
+    ../../../Common/MapGuideCommon/Services/RenderingOptions.h \
+    ../../../Common/MapGuideCommon/Services/RenderingService.h \
+    ../../../Common/MapGuideCommon/Services/ServerAdmin.h \
+    ../../../Common/MapGuideCommon/Services/Site.h \
+    ../../../Common/MapGuideCommon/Services/SiteConnection.h \
+    ../../../Common/MapGuideCommon/Services/SiteInfo.h \
+    ../../../Common/MapGuideCommon/Services/TileService.h \
+    ../../../Common/MapGuideCommon/System/UserInformation.h \
+    ../../../Common/MapGuideCommon/MapLayer/Layer.h \
+    ../../../Common/MapGuideCommon/MapLayer/Map.h \
+    ../../../Common/MapGuideCommon/MapLayer/Selection.h \
+    ../../../Common/MapGuideCommon/Services/MappingDefs.h \
+    ../../../Common/MapGuideCommon/Services/MapPlotInstruction.h \
+    ../../../Common/MapGuideCommon/Services/ServerAdminDefs.h \
+    ../../../Common/MapGuideCommon/Services/UnitType.h \
+    ../../../Common/MapGuideCommon/System/ConfigProperties.h \
+    ../WebApp/WebCommand.h \
+    ../WebApp/WebUiTargetCommand.h \
+    ../WebApp/WebWidget.h \
+    ../WebApp/WebWidgetCollection.h \
+    ../WebApp/WebUiPane.h \
+    ../WebApp/WebUiSizablePane.h \
+    ../WebApp/WebBufferCommand.h \
+    ../WebApp/WebCommandCollection.h \
+    ../WebApp/WebCommandWidget.h \
+    ../WebApp/WebContextMenu.h \
+    ../WebApp/WebFlyoutWidget.h \
+    ../WebApp/WebGetPrintablePageCommand.h \
+    ../WebApp/WebHelpCommand.h \
+    ../WebApp/WebInformationPane.h \
+    ../WebApp/WebInvokeScriptCommand.h \
+    ../WebApp/WebInvokeUrlCommand.h \
+    ../WebApp/WebLayout.h \
+    ../WebApp/WebMeasureCommand.h \
+    ../WebApp/WebPrintCommand.h \
+    ../WebApp/WebSearchCommand.h \
+    ../WebApp/WebSelectWithinCommand.h \
+    ../WebApp/WebSeparatorWidget.h \
+    ../WebApp/WebTaskBar.h \
+    ../WebApp/WebTaskBarWidget.h \
+    ../WebApp/WebTaskPane.h \
+    ../WebApp/WebToolbar.h \
+    ../WebApp/WebViewOptionsCommand.h \
+    ../WebApp/WebActions.h \
+    ../WebApp/WebTargetType.h \
+    ../WebApp/WebTargetViewerType.h \
+    ../WebApp/WebTaskButtonType.h \
+    ../WebApp/WebWidgetType.h \
+    ../HttpHandler/HttpHeader.h \
+    ../HttpHandler/HttpPrimitiveValue.h \
+    ../HttpHandler/HttpRequest.h \
+    ../HttpHandler/HttpRequestMetadata.h \
+    ../HttpHandler/HttpRequestParam.h \
+    ../HttpHandler/HttpResponse.h \
+    ../HttpHandler/HttpResult.h
+	../../../BuildTools/WebTools/IMake/Linux/IMake ../MapGuideApi/MapGuideApiGen.xml Java
+	cp -f java.i language.i
+	../../../BuildTools/WebTools/IMake/Linux/IMake ../MapGuideApi/Constants.xml Java .
+	../../../Oem/SWIGEx/Linux/swig -dllname MapGuideJavaApiEx -c++ -java -mgjavanothrow -mgjavacasing -DJAVA -package org.osgeo.mapguide -root MgObject -baseexception MgException -clsidcode getclassid.code -clsiddata m_cls_id -catchallcode catchall.code -dispose "((MgDisposable*)arg1)->Release()" -rethrow "e->Raise();" -nodefault -noconstants -module MapGuideJavaApiEx -o MgApi_wrap.cpp -lib ../../../Oem/SWIGEx/Lib MapGuideApi.i
+	rm -f org/osgeo/mapguide/*.class
+	mv -f *.java org/osgeo/mapguide
+	$(JAVA_HOME)/bin/javac -classpath . org/osgeo/mapguide/*.java
+	$(JAVA_HOME)/bin/jar cf ../WEB-INF/lib/MapGuideApiEx.jar org/osgeo/mapguide/*.class
+	$(JAVA_HOME)/bin/jar cf ../WEB-INF/lib/MapGuideApiEx-sources.jar org/osgeo/mapguide/*.java

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/README.md
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/README.md	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/README.md	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,97 @@
+# Java wrapper notes
+
+This document describes the Java language binding for the MapGuide API
+
+# Usage
+
+Reference `MapGuideApi.jar` like you would any other java jar library and make sure that the `java.library.path` of your root Java application contains the path to:
+
+ * `MapGuideJavaApi.dll` and supporting dlls on Windows
+ * `libMapGuideJavaApi.so` on Linux. The zip package includes distro-specific builds of this library, make sure to use the correct library for your distro. All builds of this library have their RPATH set to `/usr/local/mapguideopensource-3.1.1/lib` and `/usr/local/mapguideopensource-3.1.1/webserverextensions/lib`, so any dependent libraries of `libMapGuideJavaApi.so` will be automatically detected and loaded assuming you have MapGuide Open Source 3.1.1 already installed.
+
+# Overview of differences
+
+This wrapper is based on the `MapGuideJavaApiEx` variant of the official Java binding ([original RFC here](https://trac.osgeo.org/mapguide/wiki/MapGuideRfc129)), and carries most of its changes/differences:
+
+## 1. Minimum Java version
+
+This binding was built with JDK 7
+
+## 2. `MgException`/`AppThrowable` is no longer a checked exception
+
+`AppThrowable` now extends `RuntimeException` making it (and `MgException` and its subclasses) unchecked exceptions, all methods in the MapGuide API no longer have the (`throws MgException`) clause.
+
+## 3. Method names follow Java naming conventions
+
+All method names in the Java proxy classes are now in lowerCamelCase instead of the MapGuide-default UpperCamelCase
+
+ eg. Instead of this:
+```
+MgSiteConnection siteConn = new MgSiteConnection();
+MgUserInformation userInfo = new MgUserInfomration(sessionId);
+siteConn.Open(userInfo);
+MgFeatureService featureSvc = (MgFeatureService)siteConn.CreateService(MgServiceType.FeatureService);
+MgFeatureSchemaCollection schema = featureSvc.DescribeSchema(new MgResourceIdentifier("Library://Samples/Sheboygan/Data/Parcels.FeatureSource"), "SHP_Schema");
+```
+ It is now this:
+```
+MgSiteConnection siteConn = new MgSiteConnection();
+MgUserInformation userInfo = new MgUserInfomration(sessionId);
+siteConn.open(userInfo); //Note the lowercase
+MgFeatureService featureSvc = (MgFeatureService)siteConn.createService(MgServiceType.FeatureService); //Note the lowercase
+MgFeatureSchemaCollection schema = featureSvc.describeSchema(new MgResourceIdentifier("Library://Samples/Sheboygan/Data/Parcels.FeatureSource"), "SHP_Schema"); //Note the lowercase
+```
+
+The `MgInitializeWebTier` entry point also obeys this convention (now named `mgInitializeWebTier`)
+
+## 4. `java.util.Collection<T>` support
+
+The following MapGuide collection classes now implement `java.util.Collection<T>`:
+
+ - `MgBatchPropertyCollection` (T is `MgPropertyCollection`)
+ - `MgClassDefinitionCollection` (T is `MgClassDefinition`)
+ - `MgFeatureSchemaCollection` (T is `MgFeatureSchema`)
+ - `MgPropertyCollection` (T is `MgProperty`)
+ - `MgStringCollection` (T is `String`)
+ 
+## 5. `java.util.Iterable<T>` support
+
+The following classes now implement `java.util.Iterable<T>` allowing them to be used in an enhanced for-loop
+
+ - `MgReadOnlyLayerCollection` (T is `MgLayerBase`)
+
+Having `java.util.Iterable<T>` means that such instances can be be looped using the [enhanced for loop](https://blogs.oracle.com/corejavatechtips/using-enhanced-for-loops-with-your-classes) like so:
+
+```
+MgReadOnlyLayerCollection readOnlyLayers = ...;
+for (MgLayerBase layer : readOnlyLayers) {
+    ...
+}
+```
+
+As `java.util.Collection<T>` inherits from `java.util.Iterable<T>`, such implementing classes can be used with the enhanced for loop as well.
+
+## 6. Renamed methods
+
+To avoid naming conflicts with SWIG generated code and methods from inherited java classes or interfaces as a result of the above changes, the following class methods have been renamed in the Java MapGuide API:
+
+ - `MgException.GetStackTrace`        is now `MgException.getExceptionStackTrace`
+ - `MgBatchPropertyCollection.Add`    is now `MgBatchPropertyCollection.addItem`
+ - `MgClassDefinitionCollection.Add`  is now `MgClassDefinitionCollection.addItem`
+ - `MgFeatureSchemaCollection.Add`    is now `MgFeatureSchemaCollection.addItem`
+ - `MgIntCollection.Add`              is now `MgIntCollection.addItem`
+ - `MgPropertyCollection.Add`         is now `MgPropertyCollection.addItem`
+ - `MgStringCollection.Add`           is now `MgStringCollection.addItem`
+
+## 7. The following classes implement `java.lang.AutoCloseable` and can be used with [try-with-resources](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) statements:
+
+ - `MgReader`
+ - `MgFeatureReader`
+ - `MgDataReader`
+ - `MgSqlDataReader`
+ - `MgLongTransactionReader`
+ - `MgSpatialContextReader`
+
+## 8. Tightened encapsulation
+
+The SWIG-generated constructor for every proxy class is no longer `public`. This constructor was always reserved for use by SWIG.
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/catchall.code
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/catchall.code	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/catchall.code	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+
+void onCatchAll(JNIEnv* jenv, const char* proxyfname)
+{
+    string s = proxyfname;
+    wstring fname = MgUtil::MultiByteToWideChar(s);
+    MgUnclassifiedException* e = new MgUnclassifiedException(fname, __LINE__, __WFILE__, NULL, L"", NULL);
+    ThrowJavaExceptionWrapper(jenv, e);
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/getclassid.code
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/getclassid.code	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/getclassid.code	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,6 @@
+
+/* Get a class identifier from an Object instance */
+JNIEXPORT jint JNICALL Java_org_osgeo_mapguide_MapGuideJavaApiExJNI_getClassId(JNIEnv* jenv, jclass jcls, jlong jarg1)
+{
+  return ((MgObject*)(void*)jarg1)->GetClassId();
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/java.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/java.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Java/java.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,128 @@
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+%include "../Common/Java/exception.i"
+%include "../Common/Java/monkey_patch.i"
+%include "../Common/Java/extensions.i"
+
+//Required JNI bootstrap
+%pragma(java) jniclasscode=%{
+  static {
+    try {
+      System.loadLibrary("MapGuideJavaApi");
+    } catch (UnsatisfiedLinkError e) {
+      System.err.println("Native code library failed to load. \n" + e);
+      System.exit(1);
+    }
+  }
+%}	
+
+%typemap(javaout) SWIGTYPE *
+{
+    long cPtr = $jnicall;
+    return (cPtr == 0) ? null : ($javaclassname)ObjectFactory.createObject($imclassname.MgObject_getClassId(cPtr, this), cPtr, true);
+}
+
+// These methods have to be invoked C-style
+%ignore MgObject::GetClassName;
+
+%include "../Common/refcount.i"
+
+%typemap(jni) STRINGPARAM "jstring"
+%typemap(jtype) STRINGPARAM "String"
+%typemap(jstype) STRINGPARAM "String"
+%typemap(javain) STRINGPARAM "$javainput"
+%typemap(in) STRINGPARAM
+{
+    if($input == NULL)
+       $1 = std::wstring(L"");
+    else
+    {
+        int len = jenv->GetStringLength($input);
+        const jchar *pstr = jenv->GetStringChars($input, 0);
+        if (!pstr)
+            $1 = std::wstring(L"ERROR: SWIG string conversion"); 
+        else
+        {
+#ifdef WIN32
+            $1 =  std::wstring((const wchar_t*)pstr, len);
+#else
+            xstring u16String = xstring((XMLCh*)pstr, len);
+            lstring u32String;
+            UnicodeString::UTF16toUTF32(u16String.c_str(),u32String);
+            $1 =  std::wstring((const wchar_t*) u32String.c_str());
+#endif
+            jenv->ReleaseStringChars($input, pstr);
+        }
+    }
+}
+
+%typemap(jni) STRING "jstring"
+%typemap(jtype) STRING "String"
+%typemap(jstype) STRING "String"
+%typemap(javaout) STRING
+{
+  { return $jnicall; }
+}
+%typemap(out) STRING
+{
+#ifdef WIN32
+    jresult = jenv->NewString((const jchar *)result.c_str(), (jsize)result.length());
+#else
+    xstring u16String;
+    UnicodeString::UTF32toUTF16((const LCh*) result.c_str(), u16String);
+    jresult = jenv->NewString((const jchar *)u16String.c_str(), (jsize)u16String.length());
+#endif
+}
+
+%typemap(jni) BYTE_ARRAY_IN "jbyteArray"
+%typemap(jtype) BYTE_ARRAY_IN "byte[]"
+%typemap(jstype) BYTE_ARRAY_IN "byte[]"
+%typemap(in) BYTE_ARRAY_IN (jbyte* bytes)
+{
+    bytes = jenv->GetByteArrayElements($input, 0);
+    if (!bytes)
+        return $null;
+    $1 = (BYTE_ARRAY_IN)bytes;
+}
+%typemap(freearg) BYTE_ARRAY_IN
+{
+    jenv->ReleaseByteArrayElements($input, bytes$argnum, 0);
+}
+%typemap(javain) BYTE_ARRAY_IN "$javainput"
+
+
+%typemap(jni) BYTE_ARRAY_OUT "jbyteArray"
+%typemap(jtype) BYTE_ARRAY_OUT "byte[]"
+%typemap(jstype) BYTE_ARRAY_OUT "byte[]"
+%typemap(in) BYTE_ARRAY_OUT (jbyte* bytes)
+{
+    bytes = jenv->GetByteArrayElements($input, 0);
+    if (!bytes)
+        return $null;
+    $1 = (BYTE_ARRAY_OUT)bytes;
+}
+%typemap(freearg) BYTE_ARRAY_OUT
+{
+    jenv->ReleaseByteArrayElements($input, bytes$argnum, JNI_COMMIT);
+}
+%typemap(javain) BYTE_ARRAY_OUT "$javainput"
+
+///////////////////////////////////////////////////////////
+// Global functions
+//
+void MgInitializeWebTier(STRINGPARAM configFile);

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/CMakeLists.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/CMakeLists.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/CMakeLists.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,109 @@
+project(php_MapGuideApi)
+
+if (CMAKE_COMPILER_IS_GNUCXX)
+    message ("Restoring default linker flags for libphp_MapGuideApi")
+    set ( CMAKE_SHARED_LINKER_FLAGS "${WEB_CMAKE_SHARED_LINKER_FLAGS}")
+endif (CMAKE_COMPILER_IS_GNUCXX)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}
+    ${MG_COMMON_DIR}/MdfModel
+    ${MG_COMMON_DIR}/Foundation
+    ${MG_COMMON_DIR}/Geometry
+    ${MG_COMMON_DIR}/PlatformBase
+    ${MG_COMMON_DIR}/MapGuideCommon
+    ${PHP_INCLUDE_DIR}/TSRM
+    ${PHP_INCLUDE_DIR}/Zend
+    ${PHP_INCLUDE_DIR}/main
+    ${PHP_INCLUDE_DIR}
+    ${ACE_INCLUDE_DIR}
+    ${MG_WEB_DIR}/HttpHandler
+    ${MG_WEB_DIR}/WebSupport
+    ${MG_WEB_DIR}/WebApp
+    ${XERCESC_INCLUDE_DIR}
+)
+
+set(MG_PHP_OUTPUT_DIR "${MG_PACKAGE_OUTPUT_DIR}/Php/${CMAKE_BUILD_TYPE}/${MG_ARCH}")
+
+set(php_MapGuideApi_SRCS
+    ${CMAKE_CURRENT_BINARY_DIR}/MgApi_wrap.cpp
+)
+
+add_library(php_MapGuideApi${MG_VERSION_SUFFIX} SHARED ${php_MapGuideApi_SRCS})
+add_dependencies(php_MapGuideApi${MG_VERSION_SUFFIX} IMake)
+add_definitions(-DPHP -DPHPPROXYAPI_EXPORTS)
+if (UNIX)
+    add_definitions(-fno-var-tracking-assignments)
+endif (UNIX)
+if (MSVC)
+    add_definitions(-D_USRDLL -DSWIG_PUBLIC_API -D_XKEYCHECK_H -DZEND_WIN32 -DPHP_WIN32 -DZEND_DEBUG=0)
+    if (MG_CPU EQUAL 32)
+        add_definitions(-D_USE_32BIT_TIME_T)
+    endif (MG_CPU EQUAL 32)
+    add_definitions(/bigobj)
+    set_target_properties(php_MapGuideApi${MG_VERSION_SUFFIX} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${MG_PHP_OUTPUT_DIR} )
+    set_target_properties(php_MapGuideApi${MG_VERSION_SUFFIX} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${MG_PHP_OUTPUT_DIR} )
+    set_target_properties(php_MapGuideApi${MG_VERSION_SUFFIX} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${MG_PHP_OUTPUT_DIR} )
+endif (MSVC)
+
+set(IMAKE_RELATIVE_RESOLUTION "-r ${CMAKE_CURRENT_SOURCE_DIR}")
+if (UNIX)
+    set(IMAKE_RELATIVE_RESOLUTION "-a")
+endif (UNIX)
+
+message(STATUS "Using IMake at: ${IMAKE_TOOL}")
+add_custom_command(OUTPUT ${php_MapGuideApi_SRCS}
+    COMMAND ${CMAKE_COMMAND} -E make_directory ${MG_PHP_OUTPUT_DIR}
+    COMMAND ${CMAKE_COMMAND} -E echo Regenerating: ${CMAKE_CURRENT_BINARY_DIR}/constants.php
+    COMMAND ${IMAKE_TOOL} -p ${CMAKE_CURRENT_BINARY_DIR}/../MapGuideApi/Constants.xml 
+        -l PHP 
+        ${IMAKE_RELATIVE_RESOLUTION}
+        -o ${MG_PHP_OUTPUT_DIR}/constants.php
+        -t
+    COMMAND ${CMAKE_COMMAND} -E copy
+        ${CMAKE_CURRENT_BINARY_DIR}/php.i
+        ${CMAKE_CURRENT_BINARY_DIR}/language.i
+    COMMAND ${IMAKE_TOOL} -p ../MapGuideApi/MapGuideApiGen.xml
+        -l PHP
+        ${IMAKE_RELATIVE_RESOLUTION}
+        -o ${CMAKE_CURRENT_BINARY_DIR}
+    COMMAND ${CMAKE_COMMAND} -E echo Running SWIG [PHP7]
+    COMMAND ${SWIG_TOOL} -c++ -php7 
+        -DWIN32 -DPHPSWIG
+        -nodefaultctor -nodefaultdtor 
+        -o "${CMAKE_CURRENT_BINARY_DIR}/MgApi_wrap.cpp"
+        ${CMAKE_CURRENT_BINARY_DIR}/MapGuideApi.i
+    COMMAND ${CMAKE_COMMAND} -E echo Applying PHP7 swig workaround patch
+    COMMAND ${SED_COMMAND} ${SED_ARGS} "s#(MapGuideApi_globals.error_msg)#ZEND_MODULE_GLOBALS_ACCESSOR(MapGuideApi, error_msg)#g; s#(MapGuideApi_globals.error_code)#ZEND_MODULE_GLOBALS_ACCESSOR(MapGuideApi, error_code)#g"
+        "${CMAKE_CURRENT_BINARY_DIR}/MgApi_wrap.cpp"
+    COMMAND ${MOVE_COMMAND} ${MOVE_ARGS}
+        MapGuideApi.php
+        ${MG_PHP_OUTPUT_DIR}
+)
+
+# if (UNIX)
+#     install(TARGETS php_MapGuideApi${MG_VERSION_SUFFIX} DESTINATION ${LIB_INSTALL_DIR} COMPONENT ${MG_COMPONENT})
+#     install_symlink(${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/libphp_MapGuideApi${MG_VERSION_SUFFIX}.so libphp_MapGuideApi.so ${MG_COMPONENT})
+#     install(FILES ${CMAKE_CURRENT_BINARY_DIR}/constants.php DESTINATION ${MG_WWWROOT}/mapadmin COMPONENT ${MG_COMPONENT})
+#     install(FILES ${CMAKE_CURRENT_BINARY_DIR}/constants.php DESTINATION ${MG_WWWROOT}/mapviewerphp COMPONENT ${MG_COMPONENT})
+# endif (UNIX)
+target_link_libraries(php_MapGuideApi${MG_VERSION_SUFFIX}
+    ${ACE_LIBRARY}
+    ${PHP_LIBRARY}
+    ${MG_FOUNDATION_LIBRARY}
+    ${MG_GEOMETRY_LIBRARY}
+    ${MG_PLATFORMBASE_LIBRARY}
+    ${MG_COMMON_LIBRARY}
+    ${MG_HTTPHANDLER_LIBRARY}
+    ${MG_WEBAPP_LIBRARY}
+    ${MG_MDFMODEL_LIBRARY}
+    ${MG_MDFPARSER_LIBRARY}
+    ${MG_WEBSUPPORT_LIBRARY}
+    ${XERCESC_LIBRARIES}
+)
+
+# Only needed for Linux
+if (UNIX)
+    file(COPY "InitializeWebTier.cpp" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+endif (UNIX)
+
+file(COPY "php.i" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/COPYRIGHT
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/COPYRIGHT	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/COPYRIGHT	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,19 @@
+Where copyrights have not been included in the source file, this
+copyright shall be used for all source files in this directory
+and subdirectories.
+
+Copyright (C) 2004-2011 by Autodesk, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of version 2.1 of the GNU Lesser
+General Public License as published by the Free Software Foundation.
+
+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 St, Fifth Floor, Boston, MA  02110-1301  USA
+

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/InitializeWebTier.cpp
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/InitializeWebTier.cpp	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/InitializeWebTier.cpp	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+// What is this file?
+//
+// MapGuideApiGen.xml tries to include this file on Linux, presumably because its default include paths
+// includes WebSupport that would have this file present. In our context, this file doesn't exist so
+// this file just basically re-includes WebSupport.h, just like it would on Windows
+#ifndef _WIN32
+#include "WebSupport.h"
+#endif
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/Makefile.am
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/Makefile.am	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/Makefile.am	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,468 @@
+AUTOMAKE_OPTIONS = foreign
+
+EXTRA_DIST = getclassid.code \
+             catchall.code \
+             php.i \
+             PhpApi.rc \
+             PhpApi.vcproj \
+             PhpLocalizer.cpp
+
+prefix=@prefix@/webserverextensions
+
+INCLUDES = -I../../../Common/MdfModel \
+           -I../../../Common/Foundation \
+           -I../../../Common/Geometry \
+           -I../../../Common/PlatformBase \
+           -I../../../Common/MapGuideCommon \
+           -I$(php_src)/TSRM \
+           -I$(php_src)/Zend \
+           -I$(php_src)/main \
+           -I$(php_src) \
+           -I../../../Oem/ACE/ACE_wrappers \
+           -I../HttpHandler \
+           -I../WebSupport \
+           -I../WebApp \
+           -I../../../Oem/dbxml/xerces-c-src/src
+
+lib_LTLIBRARIES = libphp_MapGuideApi.la
+
+nodist_libphp_MapGuideApi_la_SOURCES = MgApi_wrap.cpp
+
+AM_CXXFLAGS = @CXXFLAGS@ -fno-var-tracking-assignments -DPHP -DPHPPROXYAPI_EXPORTS
+
+libphp_MapGuideApi_la_LIBADD = \
+    -lACE \
+    -lMgFoundation \
+    -lMgGeometry \
+    -lMgPlatformBase \
+    -lMgMapGuideCommon \
+    -lMgHttpHandler \
+    -lMgMdfModel \
+    -lMgMdfParser \
+    -lxerces-c \
+    -lMgWebApp
+
+libphp_MapGuideApi_la_LDFLAGS = -release $(PACKAGE_VERSION) \
+    -L../../../Oem/ACE/ACE_wrappers/ace \
+    -L../../../Common/MdfModel \
+    -L../../../Common/MdfParser \
+    -L../../../Common/Foundation \
+    -L../../../Common/Geometry \
+    -L../../../Common/PlatformBase \
+    -L../../../Common/MapGuideCommon \
+    -L../HttpHandler \
+    -L../WebApp \
+    -L../../../Oem/dbxml/install/lib
+
+MgApi_wrap.cpp : \
+    ../WebApp/WebApp.h \
+    ../WebSupport/InitializeWebTier.cpp \
+    ../../../Common/MdfModel/UnicodeString.h \
+    ../HttpHandler/HttpHandlerApiDllExport.h \
+    ../HttpHandler/HttpHandler.h \
+    ../../../Common/Foundation/System/Object.h \
+    ../../../Common/Foundation/System/Disposable.h \
+    ../../../Common/Foundation/System/GuardDisposable.h \
+    ../../../Common/Foundation/System/Serializable.h \
+    ../../../Common/Foundation/System/NamedSerializable.h \
+    ../../../Common/Foundation/Data/Collection.h \
+    ../../../Common/Foundation/Data/Property.h \
+    ../../../Common/Foundation/System/DisposableCollection.h \
+    ../../../Common/Foundation/Data/BatchPropertyCollection.h \
+    ../../../Common/Foundation/Data/ByteReader.h \
+    ../../../Common/Foundation/Data/ByteSource.h \
+    ../../../Common/Foundation/Data/ByteSink.h \
+    ../../../Common/Foundation/Data/DateTime.h \
+    ../../../Common/Foundation/Data/NullableProperty.h \
+    ../../../Common/Foundation/Data/PropertyCollection.h \
+    ../../../Common/Foundation/Data/PropertyDefinition.h \
+    ../../../Common/Foundation/Data/StringCollection.h \
+    ../../../Common/Foundation/Data/StringProperty.h \
+    ../../../Common/Foundation/Data/FeaturePropertyType.h \
+    ../../../Common/Foundation/Data/MimeType.h \
+    ../../../Common/Foundation/Data/PropertyType.h \
+    ../../../Common/Foundation/Exception/Exception.h \
+    ../../../Common/Foundation/Exception/SystemException.h \
+    ../../../Common/Foundation/Exception/IoException.h \
+    ../../../Common/Foundation/Exception/FileIoException.h \
+    ../../../Common/Foundation/Exception/ApplicationException.h \
+    ../../../Common/Foundation/Exception/ThirdPartyException.h \
+    ../../../Common/Foundation/Exception/StreamIoException.h \
+    ../../../Common/Foundation/Exception/OutOfRangeException.h \
+    ../../../Common/Foundation/Exception/ArgumentOutOfRangeException.h \
+    ../../../Common/Foundation/Exception/ClassNotFoundException.h \
+    ../../../Common/Foundation/Exception/ConfigurationException.h \
+    ../../../Common/Foundation/Exception/ConfigurationLoadFailedException.h \
+    ../../../Common/Foundation/Exception/ConfigurationSaveFailedException.h \
+    ../../../Common/Foundation/Exception/DateTimeException.h \
+    ../../../Common/Foundation/Exception/DecryptionException.h \
+    ../../../Common/Foundation/Exception/DirectoryNotFoundException.h \
+    ../../../Common/Foundation/Exception/DivideByZeroException.h \
+    ../../../Common/Foundation/Exception/DomainException.h \
+    ../../../Common/Foundation/Exception/DuplicateDirectoryException.h \
+    ../../../Common/Foundation/Exception/DuplicateFileException.h \
+    ../../../Common/Foundation/Exception/DuplicateObjectException.h \
+    ../../../Common/Foundation/Exception/EncryptionException.h \
+    ../../../Common/Foundation/Exception/FileNotFoundException.h \
+    ../../../Common/Foundation/Exception/IndexOutOfRangeException.h \
+    ../../../Common/Foundation/Exception/InvalidArgumentException.h \
+    ../../../Common/Foundation/Exception/InvalidCastException.h \
+    ../../../Common/Foundation/Exception/InvalidOperationException.h \
+    ../../../Common/Foundation/Exception/InvalidPropertyTypeException.h \
+    ../../../Common/Foundation/Exception/InvalidStreamHeaderException.h \
+    ../../../Common/Foundation/Exception/LengthException.h \
+    ../../../Common/Foundation/Exception/LogicException.h \
+    ../../../Common/Foundation/Exception/NotFiniteNumberException.h \
+    ../../../Common/Foundation/Exception/NotImplementedException.h \
+    ../../../Common/Foundation/Exception/NullArgumentException.h \
+    ../../../Common/Foundation/Exception/NullPropertyValueException.h \
+    ../../../Common/Foundation/Exception/NullReferenceException.h \
+    ../../../Common/Foundation/Exception/ObjectNotFoundException.h \
+    ../../../Common/Foundation/Exception/OutOfMemoryException.h \
+    ../../../Common/Foundation/Exception/OverflowException.h \
+    ../../../Common/Foundation/Exception/PlatformNotSupportedException.h \
+    ../../../Common/Foundation/Exception/ResourcesException.h \
+    ../../../Common/Foundation/Exception/ResourcesLoadFailedException.h \
+    ../../../Common/Foundation/Exception/RuntimeException.h \
+    ../../../Common/Foundation/Exception/TemporaryFileNotAvailableException.h \
+    ../../../Common/Foundation/Exception/UnclassifiedException.h \
+    ../../../Common/Foundation/Exception/UnderflowException.h \
+    ../../../Common/Foundation/Exception/XmlException.h \
+    ../../../Common/Foundation/Exception/XmlParserException.h \
+    ../../../Common/Geometry/GeometricEntity.h \
+    ../../../Common/Geometry/Geometry.h \
+    ../../../Common/Geometry/GeometryComponent.h \
+    ../../../Common/Geometry/Curve.h \
+    ../../../Common/Geometry/CurveSegment.h \
+    ../../../Common/Geometry/Ring.h \
+    ../../../Common/Geometry/Region.h \
+    ../../../Common/Geometry/AgfReaderWriter.h \
+    ../../../Common/Geometry/AggregateGeometry.h \
+    ../../../Common/Geometry/ArcSegment.h \
+    ../../../Common/Geometry/Coordinate.h \
+    ../../../Common/Geometry/CoordinateCollection.h \
+    ../../../Common/Geometry/CoordinateIterator.h \
+    ../../../Common/Geometry/CoordinateXY.h \
+    ../../../Common/Geometry/CoordinateXYM.h \
+    ../../../Common/Geometry/CoordinateXYZ.h \
+    ../../../Common/Geometry/CoordinateXYZM.h \
+    ../../../Common/Geometry/CurvePolygon.h \
+    ../../../Common/Geometry/CurvePolygonCollection.h \
+    ../../../Common/Geometry/CurveRing.h \
+    ../../../Common/Geometry/CurveRingCollection.h \
+    ../../../Common/Geometry/CurveSegmentCollection.h \
+    ../../../Common/Geometry/CurveString.h \
+    ../../../Common/Geometry/CurveStringCollection.h \
+    ../../../Common/Geometry/Envelope.h \
+    ../../../Common/Geometry/GeometryCollection.h \
+    ../../../Common/Geometry/GeometryFactory.h \
+    ../../../Common/Geometry/LinearRing.h \
+    ../../../Common/Geometry/LinearRingCollection.h \
+    ../../../Common/Geometry/LinearSegment.h \
+    ../../../Common/Geometry/LineString.h \
+    ../../../Common/Geometry/LineStringCollection.h \
+    ../../../Common/Geometry/Measure.h \
+    ../../../Common/Geometry/MultiCurvePolygon.h \
+    ../../../Common/Geometry/MultiCurveString.h \
+    ../../../Common/Geometry/MultiGeometry.h \
+    ../../../Common/Geometry/MultiLineString.h \
+    ../../../Common/Geometry/MultiPoint.h \
+    ../../../Common/Geometry/MultiPolygon.h \
+    ../../../Common/Geometry/Point.h \
+    ../../../Common/Geometry/PointCollection.h \
+    ../../../Common/Geometry/Polygon.h \
+    ../../../Common/Geometry/PolygonCollection.h \
+    ../../../Common/Geometry/Transform.h \
+    ../../../Common/Geometry/WktReaderWriter.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystem.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemDictionaryBase.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemCatalog.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemCategory.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemCategoryDictionary.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemDatum.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemDatumDictionary.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformDefParams.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticPathElement.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticPath.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticPathDictionary.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformDef.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformGridFile.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticAnalyticalTransformDefParams.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticInterpolationTransformDefParams.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticMultipleRegressionTransformDefParams.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticStandaloneTransformDefParams.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformDefDictionary.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemDictionary.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemDictionaryUtility.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemEllipsoid.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemEllipsoidDictionary.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemEnum.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemEnumInteger32.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemFactory.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemFilter.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemFilterInteger32.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemFormatConverter.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformation.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGrids.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemMathComparator.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemMeasure.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemMgrs.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemProjectionInformation.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemTransform.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemUnitInformation.h \
+    ../../../Common/Geometry/Exception/CoordinateSystemComputationFailedException.h \
+    ../../../Common/Geometry/Exception/CoordinateSystemConversionFailedException.h \
+    ../../../Common/Geometry/Exception/CoordinateSystemInitializationFailedException.h \
+    ../../../Common/Geometry/Exception/CoordinateSystemLoadFailedException.h \
+    ../../../Common/Geometry/Exception/CoordinateSystemMeasureFailedException.h \
+    ../../../Common/Geometry/Exception/CoordinateSystemTransformFailedException.h \
+    ../../../Common/Geometry/Exception/GeometryException.h \
+    ../../../Common/Geometry/Exception/InvalidCoordinateSystemException.h \
+    ../../../Common/Geometry/Exception/InvalidCoordinateSystemTypeException.h \
+    ../../../Common/Geometry/Exception/InvalidCoordinateSystemUnitsException.h \
+    ../../../Common/Geometry/CoordinateDimension.h \
+    ../../../Common/Geometry/GeometricEntityType.h \
+    ../../../Common/Geometry/GeometricPathInstructionType.h \
+    ../../../Common/Geometry/GeometryComponentType.h \
+    ../../../Common/Geometry/GeometryType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemCodeFormat.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemErrorCode.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformationMethod.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGridOrientation.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGridSpecializationType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemMgrsGridLevel.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemMgrsLetteringScheme.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemMgrsGridSquarePosition.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemProjectionCode.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemProjectionFormatType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemProjectionLogicalType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemProjectionParameterType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemUnitCode.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemUnitType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemWktFlavor.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformDefType.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticAnalyticalTransformationMethod.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticMultipleRegressionTransformationMethod.h \
+	../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticStandaloneTransformationMethod.h \
+    ../../../Common/Geometry/CoordinateSystem/CoordinateSystemGeodeticTransformGridFileFormat.h \
+    ../../../Common/PlatformBase/Data/BlobProperty.h \
+    ../../../Common/PlatformBase/Data/BooleanProperty.h \
+    ../../../Common/PlatformBase/Data/ByteProperty.h \
+    ../../../Common/PlatformBase/Data/ClobProperty.h \
+    ../../../Common/PlatformBase/Data/Color.h \
+    ../../../Common/PlatformBase/Data/DateTimeProperty.h \
+    ../../../Common/PlatformBase/Data/DoubleProperty.h \
+    ../../../Common/PlatformBase/Data/Int16Property.h \
+    ../../../Common/PlatformBase/Data/Int32Property.h \
+    ../../../Common/PlatformBase/Data/Int64Property.h \
+    ../../../Common/PlatformBase/Data/IntCollection.h \
+    ../../../Common/PlatformBase/Data/PropertyDefinitionCollection.h \
+    ../../../Common/PlatformBase/Data/SingleProperty.h \
+    ../../../Common/PlatformBase/Data/StringPropertyCollection.h \
+    ../../../Common/PlatformBase/Exception/ArrayTypeMismatchException.h \
+    ../../../Common/PlatformBase/Exception/DuplicateResourceDataException.h \
+    ../../../Common/PlatformBase/Exception/DuplicateResourceException.h \
+    ../../../Common/PlatformBase/Exception/EmptyFeatureSetException.h \
+    ../../../Common/PlatformBase/Exception/FdoException.h \
+    ../../../Common/PlatformBase/Exception/FeatureServiceException.h \
+    ../../../Common/PlatformBase/Exception/InvalidMapDefinitionException.h \
+    ../../../Common/PlatformBase/Exception/InvalidRepositoryNameException.h \
+    ../../../Common/PlatformBase/Exception/InvalidRepositoryTypeException.h \
+    ../../../Common/PlatformBase/Exception/InvalidResourceDataNameException.h \
+    ../../../Common/PlatformBase/Exception/InvalidResourceDataTypeException.h \
+    ../../../Common/PlatformBase/Exception/InvalidResourceNameException.h \
+    ../../../Common/PlatformBase/Exception/InvalidResourcePathException.h \
+    ../../../Common/PlatformBase/Exception/InvalidResourcePreProcessingTypeException.h \
+    ../../../Common/PlatformBase/Exception/InvalidResourceTypeException.h \
+    ../../../Common/PlatformBase/Exception/LayerNotFoundException.h \
+    ../../../Common/PlatformBase/Exception/ResourceBusyException.h \
+    ../../../Common/PlatformBase/Exception/ResourceDataNotFoundException.h \
+    ../../../Common/PlatformBase/Exception/ResourceNotFoundException.h \
+    ../../../Common/PlatformBase/Exception/ServiceNotAvailableException.h \
+    ../../../Common/PlatformBase/Exception/ServiceNotSupportedException.h \
+    ../../../Common/PlatformBase/Exception/UserNotFoundException.h \
+    ../../../Common/PlatformBase/Services/FeatureCommand.h \
+    ../../../Common/PlatformBase/Services/FeatureQueryOptions.h \
+    ../../../Common/PlatformBase/Services/FeatureSourceParams.h \
+    ../../../Common/PlatformBase/Services/Reader.h \
+    ../../../Common/PlatformBase/Services/Service.h \
+    ../../../Common/PlatformBase/Services/BaseService.h \
+    ../../../Common/PlatformBase/Services/ClassDefinition.h \
+    ../../../Common/PlatformBase/Services/ClassDefinitionCollection.h \
+    ../../../Common/PlatformBase/Services/FileFeatureSourceParams.h \
+    ../../../Common/PlatformBase/Services/DataPropertyDefinition.h \
+    ../../../Common/PlatformBase/Services/DataReader.h \
+    ../../../Common/PlatformBase/Services/DeleteFeatures.h \
+    ../../../Common/PlatformBase/Services/FeatureAggregateOptions.h \
+    ../../../Common/PlatformBase/Services/FeatureCommandCollection.h \
+    ../../../Common/PlatformBase/Services/FeatureProperty.h \
+    ../../../Common/PlatformBase/Services/FeatureReader.h \
+    ../../../Common/PlatformBase/Services/FeatureSchema.h \
+    ../../../Common/PlatformBase/Services/FeatureSchemaCollection.h \
+    ../../../Common/PlatformBase/Services/FeatureService.h \
+    ../../../Common/PlatformBase/Services/GeometricPropertyDefinition.h \
+    ../../../Common/PlatformBase/Services/GeometryProperty.h \
+    ../../../Common/PlatformBase/Services/GeometryTypeInfo.h \
+    ../../../Common/PlatformBase/Services/GwsFeatureReader.h \
+    ../../../Common/PlatformBase/Services/InsertFeatures.h \
+    ../../../Common/PlatformBase/Services/LongTransactionReader.h \
+    ../../../Common/PlatformBase/Services/ObjectPropertyDefinition.h \
+    ../../../Common/PlatformBase/Services/Parameter.h \
+    ../../../Common/PlatformBase/Services/ParameterCollection.h \
+    ../../../Common/PlatformBase/Services/Raster.h \
+    ../../../Common/PlatformBase/Services/RasterProperty.h \
+    ../../../Common/PlatformBase/Services/RasterPropertyDefinition.h \
+    ../../../Common/PlatformBase/Services/Resource.h \
+    ../../../Common/PlatformBase/Services/ResourceIdentifier.h \
+    ../../../Common/PlatformBase/Services/ResourceService.h \
+    ../../../Common/PlatformBase/Services/SpatialContextReader.h \
+    ../../../Common/PlatformBase/Services/SqlDataReader.h \
+    ../../../Common/PlatformBase/Services/Transaction.h \
+    ../../../Common/PlatformBase/Services/UpdateFeatures.h \
+    ../../../Common/PlatformBase/Services/Warnings.h \
+    ../../../Common/PlatformBase/Services/BaseServiceDefs.h \
+    ../../../Common/PlatformBase/Services/FeatureCommandType.h \
+    ../../../Common/PlatformBase/Services/FeatureGeometricType.h \
+    ../../../Common/PlatformBase/Services/FeatureSpatialOperations.h \
+    ../../../Common/PlatformBase/Services/ObjectPropertyType.h \
+    ../../../Common/PlatformBase/Services/OrderingOption.h \
+    ../../../Common/PlatformBase/Services/ParameterDirection.h \
+    ../../../Common/PlatformBase/Services/RasterTypes.h \
+    ../../../Common/PlatformBase/Services/ReaderType.h \
+    ../../../Common/PlatformBase/Services/ResourceDefs.h \
+    ../../../Common/PlatformBase/Services/SpatialContextExtentType.h \
+    ../../../Common/PlatformBase/MapLayer/LayerBase.h \
+    ../../../Common/PlatformBase/MapLayer/LayerCollection.h \
+    ../../../Common/PlatformBase/MapLayer/LayerGroup.h \
+    ../../../Common/PlatformBase/MapLayer/LayerGroupCollection.h \
+    ../../../Common/PlatformBase/MapLayer/MapBase.h \
+    ../../../Common/PlatformBase/MapLayer/MapCollection.h \
+    ../../../Common/PlatformBase/MapLayer/ReadOnlyLayerCollection.h \
+    ../../../Common/PlatformBase/MapLayer/SelectionBase.h \
+    ../../../Common/PlatformBase/MapLayer/LayerGroupType.h \
+    ../../../Common/PlatformBase/MapLayer/LayerType.h \
+    ../../../Common/MapGuideCommon/Exception/AuthenticationFailedException.h \
+    ../../../Common/MapGuideCommon/Exception/ConnectionFailedException.h \
+    ../../../Common/MapGuideCommon/Exception/ConnectionNotOpenException.h \
+    ../../../Common/MapGuideCommon/Exception/DbException.h \
+    ../../../Common/MapGuideCommon/Exception/DbXmlException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateGroupException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateNameException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateParameterException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateRepositoryException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateRoleException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateServerException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateSessionException.h \
+    ../../../Common/MapGuideCommon/Exception/DuplicateUserException.h \
+    ../../../Common/MapGuideCommon/Exception/DwfException.h \
+    ../../../Common/MapGuideCommon/Exception/DwfSectionNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/DwfSectionResourceNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/EndOfStreamException.h \
+    ../../../Common/MapGuideCommon/Exception/GroupNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidDwfPackageException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidDwfSectionException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidFeatureSourceException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidIpAddressException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidLicenseException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidLogEntryException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidPasswordException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidPrintLayoutFontSizeUnitsException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidPrintLayoutPositionUnitsException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidPrintLayoutSizeUnitsException.h \
+    ../../../Common/MapGuideCommon/Exception/InvalidServerNameException.h \
+    ../../../Common/MapGuideCommon/Exception/LicenseException.h \
+    ../../../Common/MapGuideCommon/Exception/LicenseExpiredException.h \
+    ../../../Common/MapGuideCommon/Exception/OperationProcessingException.h \
+    ../../../Common/MapGuideCommon/Exception/ParameterNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/PathTooLongException.h \
+    ../../../Common/MapGuideCommon/Exception/PortNotAvailableException.h \
+    ../../../Common/MapGuideCommon/Exception/PrintToScaleModeNotSelectedException.h \
+    ../../../Common/MapGuideCommon/Exception/RepositoryCreationFailedException.h \
+    ../../../Common/MapGuideCommon/Exception/RepositoryNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/RepositoryNotOpenException.h \
+    ../../../Common/MapGuideCommon/Exception/RepositoryOpenFailedException.h \
+    ../../../Common/MapGuideCommon/Exception/RoleNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/ServerNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/ServerNotOnlineException.h \
+    ../../../Common/MapGuideCommon/Exception/SessionExpiredException.h \
+    ../../../Common/MapGuideCommon/Exception/SessionNotFoundException.h \
+    ../../../Common/MapGuideCommon/Exception/UnauthorizedAccessException.h \
+    ../../../Common/MapGuideCommon/Exception/UriFormatException.h \
+    ../../../Common/MapGuideCommon/Services/DrawingService.h \
+    ../../../Common/MapGuideCommon/Services/DwfVersion.h \
+    ../../../Common/MapGuideCommon/Services/FeatureInformation.h \
+    ../../../Common/MapGuideCommon/Services/KmlService.h \
+    ../../../Common/MapGuideCommon/Services/Layout.h \
+    ../../../Common/MapGuideCommon/Services/MappingService.h \
+    ../../../Common/MapGuideCommon/Services/MapPlot.h \
+    ../../../Common/MapGuideCommon/Services/MapPlotCollection.h \
+    ../../../Common/MapGuideCommon/Services/PackageStatusInformation.h \
+    ../../../Common/MapGuideCommon/Services/PlotSpecification.h \
+    ../../../Common/MapGuideCommon/Services/ProfilingService.h \
+    ../../../Common/MapGuideCommon/Services/RenderingOptions.h \
+    ../../../Common/MapGuideCommon/Services/RenderingService.h \
+    ../../../Common/MapGuideCommon/Services/ServerAdmin.h \
+    ../../../Common/MapGuideCommon/Services/Site.h \
+    ../../../Common/MapGuideCommon/Services/SiteConnection.h \
+    ../../../Common/MapGuideCommon/Services/SiteInfo.h \
+    ../../../Common/MapGuideCommon/Services/TileService.h \
+    ../../../Common/MapGuideCommon/System/UserInformation.h \
+    ../../../Common/MapGuideCommon/MapLayer/Layer.h \
+    ../../../Common/MapGuideCommon/MapLayer/Map.h \
+    ../../../Common/MapGuideCommon/MapLayer/Selection.h \
+    ../../../Common/MapGuideCommon/Services/MappingDefs.h \
+    ../../../Common/MapGuideCommon/Services/MapPlotInstruction.h \
+    ../../../Common/MapGuideCommon/Services/ServerAdminDefs.h \
+    ../../../Common/MapGuideCommon/Services/UnitType.h \
+    ../../../Common/MapGuideCommon/System/ConfigProperties.h \
+    ../WebApp/WebCommand.h \
+    ../WebApp/WebUiTargetCommand.h \
+    ../WebApp/WebWidget.h \
+    ../WebApp/WebWidgetCollection.h \
+    ../WebApp/WebUiPane.h \
+    ../WebApp/WebUiSizablePane.h \
+    ../WebApp/WebBufferCommand.h \
+    ../WebApp/WebCommandCollection.h \
+    ../WebApp/WebCommandWidget.h \
+    ../WebApp/WebContextMenu.h \
+    ../WebApp/WebFlyoutWidget.h \
+    ../WebApp/WebGetPrintablePageCommand.h \
+    ../WebApp/WebHelpCommand.h \
+    ../WebApp/WebInformationPane.h \
+    ../WebApp/WebInvokeScriptCommand.h \
+    ../WebApp/WebInvokeUrlCommand.h \
+    ../WebApp/WebLayout.h \
+    ../WebApp/WebMeasureCommand.h \
+    ../WebApp/WebPrintCommand.h \
+    ../WebApp/WebSearchCommand.h \
+    ../WebApp/WebSelectWithinCommand.h \
+    ../WebApp/WebSeparatorWidget.h \
+    ../WebApp/WebTaskBar.h \
+    ../WebApp/WebTaskBarWidget.h \
+    ../WebApp/WebTaskPane.h \
+    ../WebApp/WebToolbar.h \
+    ../WebApp/WebViewOptionsCommand.h \
+    ../WebApp/WebActions.h \
+    ../WebApp/WebTargetType.h \
+    ../WebApp/WebTargetViewerType.h \
+    ../WebApp/WebTaskButtonType.h \
+    ../WebApp/WebWidgetType.h \
+    ../HttpHandler/HttpHeader.h \
+    ../HttpHandler/HttpPrimitiveValue.h \
+    ../HttpHandler/HttpRequest.h \
+    ../HttpHandler/HttpRequestMetadata.h \
+    ../HttpHandler/HttpRequestParam.h \
+    ../HttpHandler/HttpResponse.h \
+    ../HttpHandler/HttpResult.h
+	../../../BuildTools/WebTools/IMake/Linux/IMake -p ../MapGuideApi/Constants.xml -l PHP -o ./constants.php -t
+	cp -f php.i language.i
+	../../../BuildTools/WebTools/IMake/Linux/IMake -p ../MapGuideApi/MapGuideApiGen.xml -l PHP
+	../../../Oem/SWIGEx/Linux/swig -c++ -php -DPHPSWIG -baseexception MgException \
+          -clsidcode getclassid.code \
+          -clsiddata m_cls_id \
+          -catchallcode catchall.code \
+          -dispose "((MgDisposable*)arg1)->Release()" \
+          -rethrow "e->Raise();" \
+          -nodefault -o MgApi_wrap.cpp \
+          -lib ../../../Oem/SWIGEx/Lib MapGuideApi.i

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpApi.rc
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpApi.rc	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpApi.rc	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,96 @@
+// Microsoft Visual C++ generated resource script.
+//
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+    "#include ""winresrc.h""\r\n"
+    "\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 3,3,0,0
+ PRODUCTVERSION 3,3,0,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "Open Source Geospatial Foundation"
+            VALUE "FileDescription", "php_MapGuideApi Dynamic Link Library"
+            VALUE "FileVersion", "3, 3, 0, 0"
+            VALUE "InternalName", "php_MapGuideApi"
+            VALUE "LegalCopyright", "Copyright (C) Autodesk, Inc. 2004 - 2016."
+            VALUE "OriginalFilename", "php_MapGuideApi.dll"
+            VALUE "ProductName", "MapGuide Open Source"
+            VALUE "ProductVersion", "3, 3, 0, 0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpApi.vcxproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpApi.vcxproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpApi.vcxproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,268 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{696D2664-D17F-4357-8A19-2B5DC4B29962}</ProjectGuid>
+    <RootNamespace>PhpApi</RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\packages\php\$(Configuration)\x86\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\obj\$(Configuration)\PhpApi\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\..\packages\php\$(Configuration)\x64\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\obj\$(Configuration)64\PhpApi\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\packages\php\$(Configuration)\x86\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\obj\$(Configuration)\PhpApi\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\..\packages\php\$(Configuration)\x64\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\obj\$(Configuration)64\PhpApi\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+    <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+    <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">php_MapGuideApid</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">php_MapGuideApid</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">php_MapGuideApi</TargetName>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">php_MapGuideApi</TargetName>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <PreBuildEvent>
+      <Command>"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l PHP -o ./constants.php -t
+copy .\constants.php ..\mapadmin
+copy .\constants.php ..\mapviewerphp
+copy .\php.i "$(ProjectDir)language.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l PHP
+$(SWIG_TOOL_PATH)\swig -c++ -php7 -DWIN32 -DPHPSWIG -nodefaultctor -nodefaultdtor -o "$(ProjectDir)MgApi_wrap_orig.cpp" MapGuideApi.i
+move /Y "$(ProjectDir)\*.php" "$(OutDir)"
+sed "s#(MapGuideApi_globals.error_msg)#ZEND_MODULE_GLOBALS_ACCESSOR(MapGuideApi, error_msg)#g; s#(MapGuideApi_globals.error_code)#ZEND_MODULE_GLOBALS_ACCESSOR(MapGuideApi, error_code)#g" "$(ProjectDir)MgApi_wrap_orig.cpp" > "$(ProjectDir)MgApi_wrap.cpp"
+</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);$(PHP_SRC);$(PHP_SRC)\main;$(PHP_SRC)\TSRM;$(PHP_SRC)\Zend;$(PHP_SRC)\ext\w32api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;PHPPROXYAPI_EXPORTS;SWIG_PUBLIC_API;_XKEYCHECK_H;ZEND_WIN32;PHP_WIN32;ZEND_DEBUG=0;PHP;_USE_32BIT_TIME_T;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACEd.lib;php7.lib;MgFoundation.lib;MgGeometry.lib;MgPlatformBase.lib;MgMapGuideCommon.lib;HttpHandler.lib;WebApp.lib;WebSupport.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(OutDir)php_MapGuideApid.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB);..\..\..\Oem\ACE\ACE_wrappers\lib\$(Configuration);$(PHP_LIB);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)php_MapGuideApid.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>..\..\lib\$(Configuration)\php_MapGuideApid.lib</ImportLibrary>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <PreBuildEvent>
+      <Command>"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l PHP -o ./constants.php -t
+copy .\constants.php ..\mapadmin
+copy .\constants.php ..\mapviewerphp
+copy .\php.i "$(ProjectDir)language.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l PHP
+$(SWIG_TOOL_PATH)\swig -c++ -php7 -DWIN32 -DPHPSWIG -nodefaultctor -nodefaultdtor -o "$(ProjectDir)MgApi_wrap_orig.cpp" MapGuideApi.i
+move /Y "$(ProjectDir)\*.php" "$(OutDir)"
+sed "s#(MapGuideApi_globals.error_msg)#ZEND_MODULE_GLOBALS_ACCESSOR(MapGuideApi, error_msg)#g; s#(MapGuideApi_globals.error_code)#ZEND_MODULE_GLOBALS_ACCESSOR(MapGuideApi, error_code)#g" "$(ProjectDir)MgApi_wrap_orig.cpp" > "$(ProjectDir)MgApi_wrap.cpp"
+</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);$(PHP_SRC);$(PHP_SRC)\main;$(PHP_SRC)\TSRM;$(PHP_SRC)\Zend;$(PHP_SRC)\ext\w32api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;PHPPROXYAPI_EXPORTS;SWIG_PUBLIC_API;_XKEYCHECK_H;ZEND_WIN32;PHP_WIN32;ZEND_DEBUG=0;PHP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACEd.lib;php7.lib;MgFoundation.lib;MgGeometry.lib;MgPlatformBase.lib;MgMapGuideCommon.lib;HttpHandler.lib;WebApp.lib;WebSupport.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(OutDir)php_MapGuideApid.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB64);..\..\..\Oem\ACE\ACE_wrappers\lib64\$(Configuration);$(PHP_LIB64);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)php_MapGuideApid.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>..\..\lib\$(Configuration)64\php_MapGuideApid.lib</ImportLibrary>
+      <TargetMachine>MachineX64</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <PreBuildEvent>
+      <Command>"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l PHP -o ./constants.php -t
+copy .\constants.php ..\mapadmin
+copy .\constants.php ..\mapviewerphp
+copy .\php.i "$(ProjectDir)language.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l PHP
+$(SWIG_TOOL_PATH)\swig -c++ -php7 -DWIN32 -DPHPSWIG -nodefaultctor -nodefaultdtor -o "$(ProjectDir)MgApi_wrap_orig.cpp" MapGuideApi.i
+move /Y "$(ProjectDir)\*.php" "$(OutDir)"
+sed "s#(MapGuideApi_globals.error_msg)#ZEND_MODULE_GLOBALS_ACCESSOR(MapGuideApi, error_msg)#g; s#(MapGuideApi_globals.error_code)#ZEND_MODULE_GLOBALS_ACCESSOR(MapGuideApi, error_code)#g" "$(ProjectDir)MgApi_wrap_orig.cpp" > "$(ProjectDir)MgApi_wrap.cpp"
+</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);$(PHP_SRC);$(PHP_SRC)\main;$(PHP_SRC)\TSRM;$(PHP_SRC)\Zend;$(PHP_SRC)\ext\w32api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;PHPPROXYAPI_EXPORTS;SWIG_PUBLIC_API;_XKEYCHECK_H;ZEND_WIN32;PHP_WIN32;ZEND_DEBUG=0;PHP;_USE_32BIT_TIME_T;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACE.lib;php7.lib;MgFoundation.lib;MgGeometry.lib;MgPlatformBase.lib;MgMapGuideCommon.lib;HttpHandler.lib;WebApp.lib;WebSupport.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(OutDir)php_MapGuideApi.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB);..\..\..\Oem\ACE\ACE_wrappers\lib\$(Configuration);$(PHP_LIB);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)php_MapGuideApi.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>..\..\lib\$(Configuration)\php_MapGuideApi.lib</ImportLibrary>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <PreBuildEvent>
+      <Command>"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\Constants.xml" -l PHP -o ./constants.php -t
+copy .\constants.php ..\mapadmin
+copy .\constants.php ..\mapviewerphp
+copy .\php.i "$(ProjectDir)language.i"
+"..\..\IMake\Win32\IMake.exe" -p "$(ProjectDir)..\MapGuideApi\MapGuideApiGen.xml" -l PHP
+$(SWIG_TOOL_PATH)\swig -c++ -php7 -DWIN32 -DPHPSWIG -nodefaultctor -nodefaultdtor -o "$(ProjectDir)MgApi_wrap_orig.cpp" MapGuideApi.i
+move /Y "$(ProjectDir)\*.php" "$(OutDir)"
+sed "s#(MapGuideApi_globals.error_msg)#ZEND_MODULE_GLOBALS_ACCESSOR(MapGuideApi, error_msg)#g; s#(MapGuideApi_globals.error_code)#ZEND_MODULE_GLOBALS_ACCESSOR(MapGuideApi, error_code)#g" "$(ProjectDir)MgApi_wrap_orig.cpp" > "$(ProjectDir)MgApi_wrap.cpp"
+</Command>
+    </PreBuildEvent>
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <AdditionalIncludeDirectories>$(MG_SDK_INC);$(MG_HTTPHANDLER_INCLUDE_DIR);$(MG_WEBAPP_INCLUDE_DIR);$(MG_WEBSUPPORT_INCLUDE_DIR);$(MG_MDFMODEL_INCLUDE_DIR);$(MG_FOUNDATION_INCLUDE_DIR);$(MG_GEOMETRY_INCLUDE_DIR);$(MG_PLATFORMBASE_INCLUDE_DIR);$(MG_MAPGUIDECOMMON_INCLUDE_DIR);$(MG_OEM_ACE_INCLUDE_DIR);$(PHP_SRC);$(PHP_SRC)\main;$(PHP_SRC)\TSRM;$(PHP_SRC)\Zend;$(PHP_SRC)\ext\w32api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;PHPPROXYAPI_EXPORTS;SWIG_PUBLIC_API;_XKEYCHECK_H;ZEND_WIN32;PHP_WIN32;ZEND_DEBUG=0;PHP;_WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Async</ExceptionHandling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <ShowIncludes>false</ShowIncludes>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ACE.lib;php7.lib;MgFoundation.lib;MgGeometry.lib;MgPlatformBase.lib;MgMapGuideCommon.lib;HttpHandler.lib;WebApp.lib;WebSupport.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>$(OutDir)php_MapGuideApi.dll</OutputFile>
+      <AdditionalLibraryDirectories>$(MG_SDK_LIB64);..\..\..\Oem\ACE\ACE_wrappers\lib64\$(Configuration);$(PHP_LIB64);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)php_MapGuideApi.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>..\..\lib\$(Configuration)64\php_MapGuideApi.lib</ImportLibrary>
+      <TargetMachine>MachineX64</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <None Include="catchall.code" />
+    <None Include="getclassid.code" />
+    <None Include="php.i" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="MgApi_wrap.cpp">
+      <PreprocessToFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</PreprocessToFile>
+      <PreprocessSuppressLineNumbers Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</PreprocessSuppressLineNumbers>
+      <PreprocessToFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</PreprocessToFile>
+      <PreprocessSuppressLineNumbers Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</PreprocessSuppressLineNumbers>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="PhpApi.rc" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpClassMap.cpp
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpClassMap.cpp	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpClassMap.cpp	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,358 @@
+// This file is autogenerated by ClassMapGen. DO NOT EDIT
+
+static std::map<int, const char*> InitClassMap()
+{
+    std::map<int, const char*> classNameMap;
+
+    classNameMap[20004] = "_p_MgAgfReaderWriter";
+    classNameMap[20005] = "_p_MgAggregateGeometry";
+    classNameMap[1500] = "_p_MgApplicationException";
+    classNameMap[20006] = "_p_MgArcSegment";
+    classNameMap[1501] = "_p_MgArgumentOutOfRangeException";
+    classNameMap[10500] = "_p_MgArrayTypeMismatchException";
+    classNameMap[30000] = "_p_MgAuthenticationFailedException";
+    classNameMap[1005] = "_p_MgBatchPropertyCollection";
+    classNameMap[10252] = "_p_MgBlobProperty";
+    classNameMap[10253] = "_p_MgBooleanProperty";
+    classNameMap[10254] = "_p_MgByteProperty";
+    classNameMap[1250] = "_p_MgByteReader";
+    classNameMap[1257] = "_p_MgByteSink";
+    classNameMap[1251] = "_p_MgByteSource";
+    classNameMap[11750] = "_p_MgClassDefinition";
+    classNameMap[11780] = "_p_MgClassDefinitionCollection";
+    classNameMap[1502] = "_p_MgClassNotFoundException";
+    classNameMap[10255] = "_p_MgClobProperty";
+    classNameMap[10250] = "_p_MgColor";
+    classNameMap[1503] = "_p_MgConfigurationException";
+    classNameMap[1504] = "_p_MgConfigurationLoadFailedException";
+    classNameMap[1505] = "_p_MgConfigurationSaveFailedException";
+    classNameMap[30001] = "_p_MgConnectionFailedException";
+    classNameMap[30002] = "_p_MgConnectionNotOpenException";
+    classNameMap[20003] = "_p_MgCoordinate";
+    classNameMap[20009] = "_p_MgCoordinateIterator";
+    classNameMap[20500] = "_p_MgCoordinateSystem";
+    classNameMap[20504] = "_p_MgCoordinateSystemCatalog";
+    classNameMap[20506] = "_p_MgCoordinateSystemCategory";
+    classNameMap[20510] = "_p_MgCoordinateSystemCategoryDictionary";
+    classNameMap[21000] = "_p_MgCoordinateSystemComputationFailedException";
+    classNameMap[21001] = "_p_MgCoordinateSystemConversionFailedException";
+    classNameMap[20507] = "_p_MgCoordinateSystemDatum";
+    classNameMap[20512] = "_p_MgCoordinateSystemDatumDictionary";
+    classNameMap[20511] = "_p_MgCoordinateSystemDictionary";
+    classNameMap[20521] = "_p_MgCoordinateSystemDictionaryUtility";
+    classNameMap[20509] = "_p_MgCoordinateSystemEllipsoid";
+    classNameMap[20513] = "_p_MgCoordinateSystemEllipsoidDictionary";
+    classNameMap[20514] = "_p_MgCoordinateSystemEnum";
+    classNameMap[20515] = "_p_MgCoordinateSystemEnumInteger32";
+    classNameMap[20501] = "_p_MgCoordinateSystemFactory";
+    classNameMap[20516] = "_p_MgCoordinateSystemFilter";
+    classNameMap[20517] = "_p_MgCoordinateSystemFilterInteger32";
+    classNameMap[20505] = "_p_MgCoordinateSystemFormatConverter";
+    classNameMap[20542] = "_p_MgCoordinateSystemGeodeticAnalyticalTransformDefParams";
+    classNameMap[20543] = "_p_MgCoordinateSystemGeodeticInterpolationTransformDefParams";
+    classNameMap[20544] = "_p_MgCoordinateSystemGeodeticMultipleRegressionTransformDefParams";
+    classNameMap[20533] = "_p_MgCoordinateSystemGeodeticPath";
+    classNameMap[20535] = "_p_MgCoordinateSystemGeodeticPathDictionary";
+    classNameMap[20534] = "_p_MgCoordinateSystemGeodeticPathElement";
+    classNameMap[20508] = "_p_MgCoordinateSystemGeodeticTransformation";
+    classNameMap[20536] = "_p_MgCoordinateSystemGeodeticTransformDef";
+    classNameMap[20540] = "_p_MgCoordinateSystemGeodeticTransformDefDictionary";
+    classNameMap[20545] = "_p_MgCoordinateSystemGeodeticTransformGridFile";
+    classNameMap[20532] = "_p_MgCoordinateSystemGridBase";
+    classNameMap[20524] = "_p_MgCoordinateSystemGridBoundary";
+    classNameMap[20529] = "_p_MgCoordinateSystemGridLine";
+    classNameMap[20526] = "_p_MgCoordinateSystemGridLineCollection";
+    classNameMap[20530] = "_p_MgCoordinateSystemGridRegion";
+    classNameMap[20527] = "_p_MgCoordinateSystemGridRegionCollection";
+    classNameMap[20525] = "_p_MgCoordinateSystemGridSpecification";
+    classNameMap[20531] = "_p_MgCoordinateSystemGridTick";
+    classNameMap[20528] = "_p_MgCoordinateSystemGridTickCollection";
+    classNameMap[21002] = "_p_MgCoordinateSystemInitializationFailedException";
+    classNameMap[21003] = "_p_MgCoordinateSystemLoadFailedException";
+    classNameMap[20518] = "_p_MgCoordinateSystemMathComparator";
+    classNameMap[20502] = "_p_MgCoordinateSystemMeasure";
+    classNameMap[21004] = "_p_MgCoordinateSystemMeasureFailedException";
+    classNameMap[20522] = "_p_MgCoordinateSystemMgrs";
+    classNameMap[20520] = "_p_MgCoordinateSystemProjectionInformation";
+    classNameMap[20503] = "_p_MgCoordinateSystemTransform";
+    classNameMap[21005] = "_p_MgCoordinateSystemTransformFailedException";
+    classNameMap[20519] = "_p_MgCoordinateSystemUnitInformation";
+    classNameMap[20048] = "_p_MgCoordinateXY";
+    classNameMap[20051] = "_p_MgCoordinateXYM";
+    classNameMap[20049] = "_p_MgCoordinateXYZ";
+    classNameMap[20050] = "_p_MgCoordinateXYZM";
+    classNameMap[20010] = "_p_MgCurve";
+    classNameMap[20011] = "_p_MgCurvePolygon";
+    classNameMap[20047] = "_p_MgCurvePolygonCollection";
+    classNameMap[20012] = "_p_MgCurveRing";
+    classNameMap[20052] = "_p_MgCurveRingCollection";
+    classNameMap[20013] = "_p_MgCurveSegment";
+    classNameMap[20041] = "_p_MgCurveSegmentCollection";
+    classNameMap[20014] = "_p_MgCurveString";
+    classNameMap[20043] = "_p_MgCurveStringCollection";
+    classNameMap[11751] = "_p_MgDataPropertyDefinition";
+    classNameMap[11773] = "_p_MgDataReader";
+    classNameMap[1256] = "_p_MgDateTime";
+    classNameMap[1506] = "_p_MgDateTimeException";
+    classNameMap[10256] = "_p_MgDateTimeProperty";
+    classNameMap[30003] = "_p_MgDbException";
+    classNameMap[30004] = "_p_MgDbXmlException";
+    classNameMap[1507] = "_p_MgDecryptionException";
+    classNameMap[11775] = "_p_MgDeleteFeatures";
+    classNameMap[1508] = "_p_MgDirectoryNotFoundException";
+    classNameMap[1000] = "_p_MgDisposableCollection";
+    classNameMap[1509] = "_p_MgDivideByZeroException";
+    classNameMap[1510] = "_p_MgDomainException";
+    classNameMap[10257] = "_p_MgDoubleProperty";
+    classNameMap[30700] = "_p_MgDrawingService";
+    classNameMap[1511] = "_p_MgDuplicateDirectoryException";
+    classNameMap[1512] = "_p_MgDuplicateFileException";
+    classNameMap[30005] = "_p_MgDuplicateGroupException";
+    classNameMap[30006] = "_p_MgDuplicateNameException";
+    classNameMap[1513] = "_p_MgDuplicateObjectException";
+    classNameMap[30007] = "_p_MgDuplicateParameterException";
+    classNameMap[30008] = "_p_MgDuplicateRepositoryException";
+    classNameMap[10501] = "_p_MgDuplicateResourceDataException";
+    classNameMap[10502] = "_p_MgDuplicateResourceException";
+    classNameMap[30009] = "_p_MgDuplicateRoleException";
+    classNameMap[30010] = "_p_MgDuplicateServerException";
+    classNameMap[30011] = "_p_MgDuplicateSessionException";
+    classNameMap[30012] = "_p_MgDuplicateUserException";
+    classNameMap[30013] = "_p_MgDwfException";
+    classNameMap[30014] = "_p_MgDwfSectionNotFoundException";
+    classNameMap[30015] = "_p_MgDwfSectionResourceNotFoundException";
+    classNameMap[30901] = "_p_MgDwfVersion";
+    classNameMap[10503] = "_p_MgEmptyFeatureSetException";
+    classNameMap[1514] = "_p_MgEncryptionException";
+    classNameMap[30016] = "_p_MgEndOfStreamException";
+    classNameMap[20001] = "_p_MgEnvelope";
+    classNameMap[1515] = "_p_MgException";
+    classNameMap[10504] = "_p_MgFdoException";
+    classNameMap[11772] = "_p_MgFeatureAggregateOptions";
+    classNameMap[11774] = "_p_MgFeatureCommandCollection";
+    classNameMap[31001] = "_p_MgFeatureInformation";
+    classNameMap[11764] = "_p_MgFeatureProperty";
+    classNameMap[11771] = "_p_MgFeatureQueryOptions";
+    classNameMap[11753] = "_p_MgFeatureReader";
+    classNameMap[11778] = "_p_MgFeatureSchema";
+    classNameMap[11779] = "_p_MgFeatureSchemaCollection";
+    classNameMap[11754] = "_p_MgFeatureService";
+    classNameMap[10505] = "_p_MgFeatureServiceException";
+    classNameMap[11786] = "_p_MgFileFeatureSourceParams";
+    classNameMap[1516] = "_p_MgFileIoException";
+    classNameMap[1517] = "_p_MgFileNotFoundException";
+    classNameMap[20016] = "_p_MgGeometricEntity";
+    classNameMap[11756] = "_p_MgGeometricPropertyDefinition";
+    classNameMap[20019] = "_p_MgGeometry";
+    classNameMap[20020] = "_p_MgGeometryCollection";
+    classNameMap[20021] = "_p_MgGeometryComponent";
+    classNameMap[21006] = "_p_MgGeometryException";
+    classNameMap[20002] = "_p_MgGeometryFactory";
+    classNameMap[11758] = "_p_MgGeometryProperty";
+    classNameMap[11785] = "_p_MgGeometryTypeInfo";
+    classNameMap[30018] = "_p_MgGroupNotFoundException";
+    classNameMap[11782] = "_p_MgGwsFeatureReader";
+    classNameMap[40000] = "_p_MgHttpHeader";
+    classNameMap[40006] = "_p_MgHttpPrimitiveValue";
+    classNameMap[40004] = "_p_MgHttpRequest";
+    classNameMap[40002] = "_p_MgHttpRequestMetadata";
+    classNameMap[40001] = "_p_MgHttpRequestParam";
+    classNameMap[40005] = "_p_MgHttpResponse";
+    classNameMap[40003] = "_p_MgHttpResult";
+    classNameMap[1518] = "_p_MgIndexOutOfRangeException";
+    classNameMap[11776] = "_p_MgInsertFeatures";
+    classNameMap[10258] = "_p_MgInt16Property";
+    classNameMap[10259] = "_p_MgInt32Property";
+    classNameMap[10260] = "_p_MgInt64Property";
+    classNameMap[10000] = "_p_MgIntCollection";
+    classNameMap[1519] = "_p_MgInvalidArgumentException";
+    classNameMap[1520] = "_p_MgInvalidCastException";
+    classNameMap[21007] = "_p_MgInvalidCoordinateSystemException";
+    classNameMap[21008] = "_p_MgInvalidCoordinateSystemTypeException";
+    classNameMap[21009] = "_p_MgInvalidCoordinateSystemUnitsException";
+    classNameMap[30019] = "_p_MgInvalidDwfPackageException";
+    classNameMap[30020] = "_p_MgInvalidDwfSectionException";
+    classNameMap[30021] = "_p_MgInvalidFeatureSourceException";
+    classNameMap[30022] = "_p_MgInvalidIpAddressException";
+    classNameMap[30023] = "_p_MgInvalidLicenseException";
+    classNameMap[30024] = "_p_MgInvalidLogEntryException";
+    classNameMap[10507] = "_p_MgInvalidMapDefinitionException";
+    classNameMap[1522] = "_p_MgInvalidOperationException";
+    classNameMap[30026] = "_p_MgInvalidPasswordException";
+    classNameMap[30027] = "_p_MgInvalidPrintLayoutFontSizeUnitsException";
+    classNameMap[30028] = "_p_MgInvalidPrintLayoutPositionUnitsException";
+    classNameMap[30029] = "_p_MgInvalidPrintLayoutSizeUnitsException";
+    classNameMap[1523] = "_p_MgInvalidPropertyTypeException";
+    classNameMap[10508] = "_p_MgInvalidRepositoryNameException";
+    classNameMap[10509] = "_p_MgInvalidRepositoryTypeException";
+    classNameMap[10510] = "_p_MgInvalidResourceDataNameException";
+    classNameMap[10511] = "_p_MgInvalidResourceDataTypeException";
+    classNameMap[10512] = "_p_MgInvalidResourceNameException";
+    classNameMap[10513] = "_p_MgInvalidResourcePathException";
+    classNameMap[10514] = "_p_MgInvalidResourcePreProcessingTypeException";
+    classNameMap[10515] = "_p_MgInvalidResourceTypeException";
+    classNameMap[30031] = "_p_MgInvalidServerNameException";
+    classNameMap[1524] = "_p_MgInvalidStreamHeaderException";
+    classNameMap[1525] = "_p_MgIoException";
+    classNameMap[31300] = "_p_MgKmlService";
+    classNameMap[30501] = "_p_MgLayer";
+    classNameMap[12003] = "_p_MgLayerBase";
+    classNameMap[12002] = "_p_MgLayerCollection";
+    classNameMap[12001] = "_p_MgLayerGroup";
+    classNameMap[12004] = "_p_MgLayerGroupCollection";
+    classNameMap[10517] = "_p_MgLayerNotFoundException";
+    classNameMap[30904] = "_p_MgLayout";
+    classNameMap[1526] = "_p_MgLengthException";
+    classNameMap[30032] = "_p_MgLicenseException";
+    classNameMap[30033] = "_p_MgLicenseExpiredException";
+    classNameMap[20023] = "_p_MgLinearRing";
+    classNameMap[20053] = "_p_MgLinearRingCollection";
+    classNameMap[20024] = "_p_MgLinearSegment";
+    classNameMap[20042] = "_p_MgLineString";
+    classNameMap[20044] = "_p_MgLineStringCollection";
+    classNameMap[1527] = "_p_MgLogicException";
+    classNameMap[11766] = "_p_MgLongTransactionReader";
+    classNameMap[30500] = "_p_MgMap";
+    classNameMap[12000] = "_p_MgMapBase";
+    classNameMap[12005] = "_p_MgMapCollection";
+    classNameMap[30900] = "_p_MgMappingService";
+    classNameMap[30905] = "_p_MgMapPlot";
+    classNameMap[30906] = "_p_MgMapPlotCollection";
+    classNameMap[20029] = "_p_MgMultiCurvePolygon";
+    classNameMap[20030] = "_p_MgMultiCurveString";
+    classNameMap[20031] = "_p_MgMultiGeometry";
+    classNameMap[20032] = "_p_MgMultiLineString";
+    classNameMap[20033] = "_p_MgMultiPoint";
+    classNameMap[20034] = "_p_MgMultiPolygon";
+    classNameMap[1528] = "_p_MgNotFiniteNumberException";
+    classNameMap[1529] = "_p_MgNotImplementedException";
+    classNameMap[1530] = "_p_MgNullArgumentException";
+    classNameMap[1531] = "_p_MgNullPropertyValueException";
+    classNameMap[1532] = "_p_MgNullReferenceException";
+    classNameMap[1533] = "_p_MgObjectNotFoundException";
+    classNameMap[11759] = "_p_MgObjectPropertyDefinition";
+    classNameMap[30035] = "_p_MgOperationProcessingException";
+    classNameMap[1534] = "_p_MgOutOfMemoryException";
+    classNameMap[1535] = "_p_MgOutOfRangeException";
+    classNameMap[1536] = "_p_MgOverflowException";
+    classNameMap[30604] = "_p_MgPackageStatusInformation";
+    classNameMap[11788] = "_p_MgParameter";
+    classNameMap[10004] = "_p_MgParameterCollection";
+    classNameMap[30036] = "_p_MgParameterNotFoundException";
+    classNameMap[30037] = "_p_MgPathTooLongException";
+    classNameMap[1537] = "_p_MgPlatformNotSupportedException";
+    classNameMap[30902] = "_p_MgPlotSpecification";
+    classNameMap[20000] = "_p_MgPoint";
+    classNameMap[20045] = "_p_MgPointCollection";
+    classNameMap[20035] = "_p_MgPolygon";
+    classNameMap[20046] = "_p_MgPolygonCollection";
+    classNameMap[30039] = "_p_MgPortNotAvailableException";
+    classNameMap[30040] = "_p_MgPrintToScaleModeNotSelectedException";
+    classNameMap[31400] = "_p_MgProfilingService";
+    classNameMap[2000] = "_p_MgProperty";
+    classNameMap[1002] = "_p_MgPropertyCollection";
+    classNameMap[2002] = "_p_MgPropertyDefinition";
+    classNameMap[10001] = "_p_MgPropertyDefinitionCollection";
+    classNameMap[11769] = "_p_MgRaster";
+    classNameMap[11770] = "_p_MgRasterProperty";
+    classNameMap[11768] = "_p_MgRasterPropertyDefinition";
+    classNameMap[12006] = "_p_MgReadOnlyLayerCollection";
+    classNameMap[20037] = "_p_MgRegion";
+    classNameMap[31002] = "_p_MgRenderingOptions";
+    classNameMap[31000] = "_p_MgRenderingService";
+    classNameMap[30041] = "_p_MgRepositoryCreationFailedException";
+    classNameMap[30042] = "_p_MgRepositoryNotFoundException";
+    classNameMap[30043] = "_p_MgRepositoryNotOpenException";
+    classNameMap[30044] = "_p_MgRepositoryOpenFailedException";
+    classNameMap[11526] = "_p_MgResource";
+    classNameMap[10518] = "_p_MgResourceBusyException";
+    classNameMap[10519] = "_p_MgResourceDataNotFoundException";
+    classNameMap[11500] = "_p_MgResourceIdentifier";
+    classNameMap[10520] = "_p_MgResourceNotFoundException";
+    classNameMap[11501] = "_p_MgResourceService";
+    classNameMap[1538] = "_p_MgResourcesException";
+    classNameMap[1539] = "_p_MgResourcesLoadFailedException";
+    classNameMap[1540] = "_p_MgResourceTagNotFoundException";
+    classNameMap[20038] = "_p_MgRing";
+    classNameMap[30045] = "_p_MgRoleNotFoundException";
+    classNameMap[1541] = "_p_MgRuntimeException";
+    classNameMap[30502] = "_p_MgSelection";
+    classNameMap[12007] = "_p_MgSelectionBase";
+    classNameMap[30607] = "_p_MgServerAdmin";
+    classNameMap[30046] = "_p_MgServerNotFoundException";
+    classNameMap[30047] = "_p_MgServerNotOnlineException";
+    classNameMap[11251] = "_p_MgService";
+    classNameMap[10521] = "_p_MgServiceNotAvailableException";
+    classNameMap[10522] = "_p_MgServiceNotSupportedException";
+    classNameMap[30048] = "_p_MgSessionExpiredException";
+    classNameMap[30052] = "_p_MgSessionNotFoundException";
+    classNameMap[10261] = "_p_MgSingleProperty";
+    classNameMap[30605] = "_p_MgSite";
+    classNameMap[30601] = "_p_MgSiteConnection";
+    classNameMap[30608] = "_p_MgSiteInfo";
+    classNameMap[11761] = "_p_MgSpatialContextReader";
+    classNameMap[11762] = "_p_MgSqlDataReader";
+    classNameMap[1542] = "_p_MgStreamIoException";
+    classNameMap[1003] = "_p_MgStringCollection";
+    classNameMap[2001] = "_p_MgStringProperty";
+    classNameMap[10003] = "_p_MgStringPropertyCollection";
+    classNameMap[1543] = "_p_MgSystemException";
+    classNameMap[1544] = "_p_MgTemporaryFileNotAvailableException";
+    classNameMap[1545] = "_p_MgThirdPartyException";
+    classNameMap[31200] = "_p_MgTileService";
+    classNameMap[11787] = "_p_MgTransaction";
+    classNameMap[30049] = "_p_MgUnauthorizedAccessException";
+    classNameMap[1547] = "_p_MgUnclassifiedException";
+    classNameMap[1548] = "_p_MgUnderflowException";
+    classNameMap[30056] = "_p_MgUnknownTileProviderException";
+    classNameMap[30057] = "_p_MgUnsupportedTileProviderException";
+    classNameMap[11777] = "_p_MgUpdateFeatures";
+    classNameMap[30050] = "_p_MgUriFormatException";
+    classNameMap[30606] = "_p_MgUserInformation";
+    classNameMap[10523] = "_p_MgUserNotFoundException";
+    classNameMap[11257] = "_p_MgWarnings";
+    classNameMap[50005] = "_p_MgWebBufferCommand";
+    classNameMap[50000] = "_p_MgWebCommand";
+    classNameMap[50012] = "_p_MgWebCommandCollection";
+    classNameMap[50015] = "_p_MgWebCommandWidget";
+    classNameMap[50025] = "_p_MgWebContextMenu";
+    classNameMap[50016] = "_p_MgWebFlyoutWidget";
+    classNameMap[50009] = "_p_MgWebGetPrintablePageCommand";
+    classNameMap[50011] = "_p_MgWebHelpCommand";
+    classNameMap[50022] = "_p_MgWebInformationPane";
+    classNameMap[50003] = "_p_MgWebInvokeScriptCommand";
+    classNameMap[50004] = "_p_MgWebInvokeUrlCommand";
+    classNameMap[50026] = "_p_MgWebLayout";
+    classNameMap[50008] = "_p_MgWebMeasureCommand";
+    classNameMap[50007] = "_p_MgWebPrintCommand";
+    classNameMap[50002] = "_p_MgWebSearchCommand";
+    classNameMap[50006] = "_p_MgWebSelectWithinCommand";
+    classNameMap[50014] = "_p_MgWebSeparatorWidget";
+    classNameMap[50023] = "_p_MgWebTaskBar";
+    classNameMap[50017] = "_p_MgWebTaskBarWidget";
+    classNameMap[50024] = "_p_MgWebTaskPane";
+    classNameMap[50021] = "_p_MgWebToolBar";
+    classNameMap[50019] = "_p_MgWebUiPane";
+    classNameMap[50020] = "_p_MgWebUiSizablePane";
+    classNameMap[50001] = "_p_MgWebUiTargetCommand";
+    classNameMap[50010] = "_p_MgWebViewOptionsCommand";
+    classNameMap[50013] = "_p_MgWebWidget";
+    classNameMap[50018] = "_p_MgWebWidgetCollection";
+    classNameMap[20040] = "_p_MgWktReaderWriter";
+    classNameMap[1549] = "_p_MgXmlException";
+    classNameMap[1550] = "_p_MgXmlParserException";
+
+
+    return classNameMap;
+}
+
+static std::map<int, const char*> smClassMap = InitClassMap();
+
+static const char* ResolveMgClassName(const int classId)
+{
+    if (smClassMap.find(classId) != smClassMap.end())
+    {
+        return smClassMap[classId];
+    }
+    return NULL;
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpLocalizer.cpp
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpLocalizer.cpp	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/PhpLocalizer.cpp	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,148 @@
+#include <string>
+#include <map>
+
+static std::string localizationPath;
+static std::string english = "en";
+typedef std::map<std::string, std::string> STRBUNDLE;
+typedef STRBUNDLE* PSTRBUNDLE;
+static std::map<std::string, PSTRBUNDLE> languages;
+typedef char* CHAR_PTR_NOCOPY;
+
+extern "C" {
+#include "zend_API.h"
+}
+
+#include <algorithm>
+#define MAX_LOC_LEN     4096
+
+static std::string trim(std::string source)
+{
+    std::string cs = "\t \r\n";
+    std::string s = source.erase(0, source.find_first_not_of(cs));
+    return s.erase(s.find_last_not_of(cs) + 1) ;
+}
+
+static void strlower(std::string& str)
+{
+    std::transform(str.begin(), str.end(), str.begin(), ::tolower);
+}
+
+static void SetLocalizedFilesPath(const char*path)
+{
+    localizationPath = path;
+}
+
+PSTRBUNDLE GetStringBundle(std::string& locale_)
+{
+    std::string locale = locale_;
+    if (locale == "")
+        locale = english;
+    else
+        strlower(locale);
+    
+    std::string localKey = localizationPath + locale;
+
+    std::map<std::string, PSTRBUNDLE>::const_iterator it = languages.find(localKey);
+    
+    if (it == languages.end()) {
+        FILE* f = NULL;
+        std::string fname = localKey;
+        f = fopen(fname.c_str(), "r");
+        if(f == NULL) {  // assume file doesn't exists
+            // requested locale is not supported, default to English
+            it = languages.find(localizationPath + english);
+            if(it != languages.end())
+                return it->second;
+            fname = localizationPath + english;
+            f = fopen(fname.c_str(), "r");
+        }
+        PSTRBUNDLE sb = new STRBUNDLE;
+        languages[localKey] = sb;
+        if(f != NULL) {
+            char l[MAX_LOC_LEN + 1];
+            for(int lc = 0; fgets(l, MAX_LOC_LEN, f) != NULL; lc++) {
+                std::string line;
+                if(lc == 0 && (unsigned char)l[0] == 0xEF)
+                    line = trim(std::string(l + 3)); //Skip UTF8 BOF marker
+                else
+                    line = trim(std::string(l));
+                if(line.empty() || line.at(0) == '#')
+                    continue;
+                size_t sep = line.find('=');
+                if (sep == std::string::npos)
+                    continue;
+                std::string key = trim(line.substr(0, sep));
+                if (key.empty())
+                    continue;
+                std::string value = trim(line.substr(sep + 1));
+                (*sb)[key] = value;
+            }
+            fclose(f);
+        }
+    }
+    return languages[localKey];
+}
+
+static char* Localize(const char* text_, const char* locale_, int os)
+{
+    std::string locale = locale_;
+    strlower(locale);
+    std::string text = text_;
+    std::string fontSuffix = (os == 0 ? "Windows" : (os == 1 ? "Macintosh" : "Linux"));
+
+
+    PSTRBUNDLE sb = GetStringBundle(locale);
+    if(sb == NULL)
+        return estrdup("");
+    size_t len = text.length();
+    for(size_t i = 0; i < len; )
+    {
+        bool fontTag = false;
+        size_t pos1 = text.find("__#", i);
+        if (pos1 != std::string::npos)
+        {
+            size_t pos2 = text.find("#__", pos1 + 3);
+            if (pos2 != std::string::npos)
+            {
+                std::string id = text.substr(pos1 + 3, pos2 - pos1 - 3);
+                std::string locStr;
+                std::map<std::string, std::string>::const_iterator it = sb->find(id == "@font" || id == "@fontsize" ? id + fontSuffix : id);
+                if (it == sb->end())
+                    locStr = "";
+                else
+                    locStr = it->second;
+                size_t locLen = locStr.length();
+
+                std::string begin, end;
+                if (pos1 > 0)
+                    begin = text.substr(0, pos1);
+                else
+                    begin = "";
+                end = text.substr(pos2 + 3);
+                text = begin + locStr + end;
+
+                len = len - 6 - id.length() + locLen;
+                i = pos1 + locLen;
+            }
+            else
+                i = len;
+        }
+        else
+            i = len;
+    }
+    return estrdup(text.c_str());
+}
+
+static char* GetLocalizedString(const char* id_, const char* locale_)
+{
+    PSTRBUNDLE sb = NULL;
+    std::string locale = locale_;
+    sb = GetStringBundle(locale);
+    if(sb == NULL)
+        return estrdup("");
+    std::string id = id_;
+    std::map<std::string, std::string>::const_iterator it = sb->find(id);
+    if (it == sb->end())
+        return estrdup("");
+    return estrdup(it->second.c_str());
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/README.md
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/README.md	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/README.md	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,52 @@
+# PHP wrapper notes
+
+This document describes the PHP language binding for the MapGuide API
+
+# Supported PHP version
+
+This binding targets the Windows release of PHP 7.1.19, the current PHP version in the 7.1 series.
+
+The binding is built against the *non-thread-safe* release of PHP, meaning it currently can only be used in Apache httpd server via FastCGI with the `mod_fcgid` module
+
+No binding is provided for PHP 7.1 on Linux at this point in time as none of the Linux distros where we provide MapGuide binaries for (CentOS 6.x / Ubuntu 14.04), provide an official PHP 7.x package.
+
+# Usage
+
+NOTE: 
+
+As MapGuide currently does not bundle PHP 7.1, these instructions assume you already have PHP 7.1 installed. How to install PHP 7.1 is beyond the scope of this document
+
+1. Copy `php_MapGuideApi.dll` and supporting dlls to the `ext` subdirectory of your PHP 7.1 installation.
+2. Modify `php.ini` and add the line `extension=php_MapGuideApi.dll` to the extensions section of the file.
+
+# Known issues
+
+ * Some APIs with many overloads may have incorrect wrapper code generated by vanilla SWIG. We've been working around this by monkey-patching away such problematic APIs. Please report any APIs that do not behave as they did in the official PHP 5.x binding.
+
+ * If you have multiple try/catch blocks within the same scope, make sure to use different exception variable names. For example:
+
+ ```
+ try {
+   //Some code that throws
+ } catch (MgException $ex) {
+   //Handle this exception
+ }
+
+ try {
+   //Some more code that throws
+ } catch (MgException $ex) {
+   //$ex from previous catch block is not properly cleaned up and will leak memory. You should use a different variable name here
+ }
+ ```
+
+# Differences from the official PHP binding
+
+ * Usage
+   * You will need to include `MapGuideApi.php` to access the MapGuide API.
+
+ * Removed classes/APIs
+   * `MgLayerBase::MgLayerBase`
+   * `MgMapBase::Open`
+   * `MgResource::Save`
+   * `MgConfigurationException::GetExceptionMessage`
+   * `MgSerializable` This is only a "marker" class for server/webtier serialization plumbing. Needed to be removed so that `MgException` can be rebased against PHP `Exception` to make it throwable.
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/catchall.code
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/catchall.code	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/catchall.code	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+
+void onCatchAll(const char* proxyfname TSRMLS_DC)
+{
+    string s = proxyfname;
+    wstring fname = MgUtil::MultiByteToWideChar(s);
+    MgUnclassifiedException* e = new MgUnclassifiedException(fname, __LINE__, __WFILE__, NULL, L"", NULL);
+    ThrowPHPExceptionWrapper(e TSRMLS_CC);
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/getclassid.code
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/getclassid.code	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/getclassid.code	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+
+int getClassId(void* ptrObj)
+{
+    if (ptrObj == NULL)
+        return 0;
+    else
+        return ((MgObject*)ptrObj)->GetClassId();
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/php.i
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/php.i	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Php/php.i	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,221 @@
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+%begin %{
+//HACK: ACE and PHP conflict on some typedefs. In this battle, PHP must give way through
+//select patched headers on the PHP end.
+//
+//Therefore, we need to make sure that ACE headers are pulled in before PHP
+//
+//So in order to beat it to the punch, this #include is from the SWIG %begin section, 
+//ensuring it will be rendered before the #include of php.h
+#include "MapGuideCommon.h"
+%}
+
+%include "../Common/Php/pointer.i"
+%include "../Common/Php/exception.i"
+%include "../Common/Php/monkey_patch.i"
+
+%runtime %{
+#if defined(_MSC_VER)
+#   pragma warning(disable : 4102) /* unreferenced label. SWIG is inserting these, not me */
+#endif
+#include "PhpLocalizer.cpp"
+#include "PhpClassMap.cpp"
+%}
+
+%include "../Common/refcount.i"
+
+// These methods have to be invoked C-style
+%ignore MgObject::GetClassId;
+%ignore MgObject::GetClassName;
+
+///////////////////////////////////////////////////////////
+// STRINGPARAM "typecheck" typemap
+//
+%typemap(typecheck, precedence=SWIG_TYPECHECK_UNISTRING) STRINGPARAM 
+{
+    $1 = Z_TYPE($input) == IS_STRING;
+}
+
+///////////////////////////////////////////////////////////
+// STRINGPARAM "in" typemap
+// Marshal a string from PHP to C++
+//
+%typemap(in) STRINGPARAM (char* str)
+{
+    convert_to_string(&$input);
+    str = (char*)Z_STRVAL($input);
+    try
+    {
+        MgUtil::MultiByteToWideChar(str, $1);
+    }
+    catch (int)
+    {
+        zend_error(E_ERROR, "Invalid string format");
+    }
+}
+
+///////////////////////////////////////////////////////////
+// STRING "out" typemap
+// Marshal a string returned by C++ to PHP
+//
+%typemap(out) STRING
+{
+    string pBuf;
+    try
+    {
+        MgUtil::WideCharToMultiByte(result, pBuf);
+    }
+    catch (int)
+    {
+        zend_error(E_ERROR, "Invalid string format");
+    }
+    char* zendBuf = (char*)emalloc(pBuf.length()+1);
+    if (zendBuf == NULL)
+        zend_error(E_ERROR, "Out of memory");
+    strcpy(zendBuf, pBuf.c_str());
+    ZVAL_STRINGL(return_value, zendBuf, pBuf.length());
+}
+
+///////////////////////////////////////////////////////////
+// BYTE_ARRAY_OUT "in" typemap
+// Marshal a byte array supplied by PHP
+// and filled in by C++
+//
+%typemap(in, byref=1) BYTE_ARRAY_OUT buffer (INT32 length)
+{
+    bool isNull = (Z_TYPE($input) == IS_NULL);
+    bool isRef = Z_ISREF($input);
+    if (isNull)
+    {
+        $1 = (BYTE_ARRAY_OUT)0;
+    }
+    else if (isRef)
+    {
+        /* use a stack allocated temp string */
+        convert_to_long(&args[1 + 1]);
+        length = (INT32)Z_LVAL((args[1 + 1]));
+        $1 = (BYTE_ARRAY_OUT)emalloc(length + 1);
+        if ($1 == NULL)
+            zend_error(E_ERROR, "Out of memory");
+    }
+    else
+    {
+        zend_error(E_ERROR, "Type error in argument %d of $symname. Expected %s or at least something looking vaguely like a string passed by reference", $argnum, $1_descriptor->name);
+    }
+}
+
+///////////////////////////////////////////////////////////
+// BYTE_ARRAY_OUT "argout" typemap
+// Post call processing of a byte array supplied by PHP
+// and filled in by C++
+//
+%typemap(argout) BYTE_ARRAY_OUT buffer
+{
+    if (Z_ISREF($input)) 
+    {
+        ZVAL_STRINGL(Z_REFVAL($input), (char*)$1, result);
+        efree($1);
+    }
+}
+
+///////////////////////////////////////////////////////////
+// BYTE_ARRAY_IN "in" typemap
+// Marshal a byte array supplied and filled in by PHP
+//
+%typemap(in) BYTE_ARRAY_IN
+{
+    if (Z_TYPE($input) == IS_STRING || Z_TYPE($input) == IS_NULL)
+    {
+        /* use the buffer directly */
+        $1= (unsigned char*)(Z_STRVAL($input));
+    }
+    else
+    {
+        zend_error(E_ERROR, "Type error in argument %d of $symname. Expected %s or at least something looking vaguely like a string passed by reference", $argnum, $1_descriptor->name);
+    }
+}
+
+///////////////////////////////////////////////////////////
+// INT64 "in" typemap
+// Marshal an INT64 parameter from PHP to a C++ 64 bits integer
+// The int64 parameter can be specified as an integer or as a string
+//
+%typemap(in) long long (const char* str, char* endptr, long long lvalue)
+{
+    if (Z_TYPE($input) == IS_STRING)
+    {
+        str = (char*)Z_STRVAL($input);
+#ifdef WIN32
+        lvalue = _strtoi64(str, &endptr, 10);
+#else
+        lvalue = strtoll(str, &endptr, 10);
+#endif
+        if (*endptr != '\0')
+            zend_error(E_ERROR, "Invalid string encoded number in argument %d", $argnum);
+    }
+    else if (Z_TYPE($input) == IS_LONG)
+    {
+        lvalue = Z_LVAL($input);
+    }
+    else
+    {
+        zend_error(E_ERROR, "Type error in argument %d of $symname. Expected a string or an integer", $argnum);
+    }
+
+    $1 = lvalue;
+}
+
+///////////////////////////////////////////////////////////
+// INT64 "out" typemap
+// Marshal a long long returned by C++ to PHP string
+//
+%typemap(out) long long
+{
+    char buf[25];       //can't actually be longer than 21 + 1 for the sign
+#ifdef WIN32
+    sprintf(buf, "%I64d", result);
+#else
+    sprintf(buf, "%lld", result);
+#endif
+
+    ZVAL_STRINGL(return_value, buf, strlen(buf));
+}
+
+///////////////////////////////////////////////////////////
+// CHAR_PTR_NOCOPY "out" typemap
+// returns a string from C++ without duplicating it
+//
+%typemap(out) CHAR_PTR_NOCOPY
+{
+    ZVAL_STRING(return_value, result);
+}
+typedef char* CHAR_PTR_NOCOPY;
+
+%pragma(php) phpinfo=
+"    php_info_print_table_start();\n"\
+"    php_info_print_table_row(2, \"MapGuideApi\", \"enabled\");\n"\
+"    php_info_print_table_end();"
+
+///////////////////////////////////////////////////////////
+// Global functions
+//
+void MgInitializeWebTier(STRINGPARAM configFile);
+void SetLocalizedFilesPath(const char* path);
+CHAR_PTR_NOCOPY Localize(const char* text, const char* locale, int os);
+CHAR_PTR_NOCOPY GetLocalizedString(char* id, char* locale);

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/mapadmin/README.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/mapadmin/README.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/mapadmin/README.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1 @@
+This directory is where PhpApi.vcxproj pre-build copies constants.php into
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Bindings/mapviewerphp/README.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/mapviewerphp/README.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/mapviewerphp/README.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1 @@
+This directory is where PhpApi.vcxproj pre-build copies constants.php into
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/CMakeLists.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/CMakeLists.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/CMakeLists.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,2 @@
+add_subdirectory(IMake)
+add_subdirectory(Bindings)
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/IMake/CMakeLists.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/IMake/CMakeLists.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/IMake/CMakeLists.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,19 @@
+project(IMake)
+include_directories(
+    ${TCLAP_INCLUDE}
+)
+
+set(IMake_SRCS
+    IMake.cpp
+    SimpleXmlParser.cpp
+    stdafx.cpp
+)
+
+add_executable(IMake ${IMake_SRCS})
+
+if(MSVC)
+    add_definitions(-D_CRT_SECURE_NO_WARNINGS)
+    set_target_properties(IMake PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )
+    set_target_properties(IMake PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR} )
+    set_target_properties(IMake PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR} )
+endif(MSVC)
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.cpp
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.cpp	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.cpp	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,2085 @@
+// IMake.cpp : Defines the entry point for the console application.
+//
+
+#include "stdafx.h"
+#include "SimpleXmlParser.h"
+#include <tclap/CmdLine.h>
+
+enum Language
+{
+    unknown,
+    php,
+    csharp,
+    java
+};
+
+static char version[] = "1.5.0";
+static char EXTERNAL_API_DOCUMENTATION[] = "(NOTE: This API is not officially supported and may be subject to removal in a future release without warning. Use with caution.)";
+
+static string module;
+static string customPath;
+static string target;
+static string docTarget;
+static string cppInline;
+static string swigInline;
+static string typedefs;
+static string nameSpace;
+static string package;
+static set<string> classesWithDocs;
+static map<string, string> typeReplacements;
+static map<string, bool> classes;
+static vector<string> headers;
+static map<string, int> rootObjectMethods;
+static FILE* outfile;
+static FILE* docOutFile;
+static char charbuf[2];
+static bool translateMode;
+static Language language;
+static bool verbose;
+static bool absPaths;
+
+static set<string> wroteDestructorsFor;
+
+#ifdef _WIN32
+#define FILESEP '\\'
+#else
+#define FILESEP '/'
+#endif
+
+void error(string msg)
+{
+    fprintf(stderr, "Error: %s\n", msg.c_str());
+    exit(1);
+}
+
+void warning(string msg)
+{
+    fprintf(stderr, "Warning: %s\n", msg.c_str());
+}
+
+string parseModule(XNode* elt)
+{
+    LPXAttr attr = elt->GetAttr("name");
+    if(attr == NULL)
+        error("Module element does not have a 'name' attribute");
+
+    return attr->value;
+}
+
+string parseCustom(XNode* elt)
+{
+    LPXAttr attr = elt->GetAttr("path");
+    if(attr == NULL)
+        error("Custom element does not have a 'path' attribute");
+
+    return attr->value;
+}
+
+string parseTarget(XNode* elt)
+{
+    LPXAttr attr = elt->GetAttr("path");
+    if(attr == NULL)
+        error("Target element does not have a 'path' attribute");
+
+    return attr->value;
+}
+
+string parseDocTarget(XNode* elt)
+{
+    LPXAttr attr = elt->GetAttr("path");
+    if(attr == NULL)
+        error("DocTarget element does not have a 'path' attribute");
+
+    return attr->value;
+}
+
+string parseCppInline(XNode* elt)
+{
+    string text = Trim(elt->GetText());
+    string uetext = XRef2Entity(text.c_str());
+    return uetext;
+}
+
+string parseSwigInline(XNode* elt)
+{
+    string text = Trim(elt->GetText());
+    string uetext = XRef2Entity(text.c_str());
+    return uetext;
+}
+
+string parseTypedefs(XNode* elt)
+{
+    string text = Trim(elt->GetText());
+    return text;
+}
+
+void parseHeaders(XNode* elt, vector<string>& headers)
+{
+    XNodes childs = elt->GetChilds();
+    for(int i = 0 ; i < (int)childs.size(); i++)
+    {
+        XNode* node = childs[i];
+        if(node->type != XNODE_ELEMENT)
+            continue;
+
+        LPXAttr attr = node->GetAttr("path");
+        if(attr == NULL)
+            error("Header element does not have a 'path' attribute");
+
+        string path = Trim(attr->value);
+
+        headers.push_back(path);
+    }
+
+}
+
+void parseTypeReplacements(XNode* elt, map<string, string>& types)
+{
+    XNodes childs = elt->GetChilds();
+    for(int i = 0 ; i < (int)childs.size(); i++)
+    {
+        XNode* node = childs[i];
+        if(node->type != XNODE_ELEMENT)
+            continue;
+
+        LPXAttr oldTypeattr = node->GetAttr("oldtype");
+        if(oldTypeattr == NULL)
+            error("TypeReplacement element does not have a 'oldtype' attribute");
+
+        LPXAttr newTypeattr = node->GetAttr("newtype");
+        if(newTypeattr == NULL)
+            error("TypeReplacement element does not have a 'newtype' attribute");
+
+        string oldType = Trim(oldTypeattr->value);
+        string newType = Trim(newTypeattr->value);
+        if(oldType.length() == 0 || (newType.length() == 0 && !translateMode))
+            error("TypeReplacement element have an empty 'oldtype' or 'newtype' attribute");
+
+        types[oldType] = newType;
+    }
+}
+
+void parseClasses(XNode* elt, map<string, bool>& classes)
+{
+    XNodes childs = elt->GetChilds();
+    for(int i = 0 ; i < (int)childs.size(); i++)
+    {
+        XNode* node = childs[i];
+        if(node->type != XNODE_ELEMENT)
+            continue;
+
+        LPXAttr attr = node->GetAttr("name");
+        if(attr == NULL)
+            error("Class element does not have a 'name' attribute");
+
+        string className = Trim(attr->value);
+
+        classes[className] = true;
+    }
+}
+
+string parseNamespace(XNode* elt)
+{
+    string text = Trim(elt->GetText());
+    return text;
+}
+
+string parsePackage(XNode* elt)
+{
+    string text = Trim(elt->GetText());
+    return text;
+}
+
+void findAndReplaceInString(string& subject, 
+                            const string& search,
+                            const string& replace)
+{
+    size_t pos = 0;
+    while((pos = subject.find(search, pos)) != string::npos)
+    {
+         subject.replace(pos, search.length(), replace);
+         pos += replace.length();
+    }
+}
+
+void parseParameterFile(char* xmlDef, const string& relRoot)
+{
+    XNode xml;
+    if(xml.Load(xmlDef) == NULL)
+        error("XML parsing error");
+
+    XNodes childs = xml.GetChilds();
+    for(int i = 0 ; i < (int)childs.size(); i++)
+    {
+        XNode* node = childs[i];
+        if(node->type != XNODE_ELEMENT)
+            continue;
+
+        if(node->name == "Module")
+        {
+            if(translateMode)
+                error("Module is not a valid section in translation mode");
+            module = parseModule(node);
+        }
+        else if(node->name == "CustomFile")
+        {
+            if(translateMode)
+                error("Module is not a valid section in translation mode");
+
+            customPath = parseCustom(node);
+        }
+        else if(node->name == "Target")
+        {
+            if(translateMode)
+                error("Target is not a valid section in translation mode");
+            target = parseTarget(node);
+        }
+        else if(node->name == "DocTarget")
+        {
+            if(translateMode)
+                error("DocTarget is not a valid section in translation mode");
+            docTarget = parseDocTarget(node);
+        }
+        else if(node->name == "CppInline")
+        {
+            if(translateMode)
+                error("CppInLine is not a valid section in translation mode");
+            cppInline = parseCppInline(node);
+        }
+        else if(node->name == "SwigInline" || (node->name == "Inline" && translateMode))
+        {
+            swigInline = parseSwigInline(node);
+
+            if (!relRoot.empty())
+            {
+                //Rewrite any relative %include statements in the inline section so they're relative to the
+                //custom root
+                string replace = "%include \"";
+                replace += relRoot;
+                replace += "/../";
+                findAndReplaceInString(swigInline, 
+                                       "%include \"../",
+                                       replace);
+            }
+        }
+        else if(node->name == "Typedefs")
+        {
+            if(translateMode)
+                error("Typedefs is not a valid section in translation mode");
+            typedefs = parseTypedefs(node);
+        }
+        else if(node->name == "TypeReplacements")
+        {
+            if(translateMode)
+                error("TypeReplacements is not a valid section in translation mode");
+            parseTypeReplacements(node, typeReplacements);
+        }
+        else if(node->name == "PHPTypeReplacements")
+        {
+            if(!translateMode)
+                error("PHPTypeReplacements is not a valid section in SWIG mode");
+            if(language == php)
+                parseTypeReplacements(node, typeReplacements);
+        }
+        else if(node->name == "JavaTypeReplacements")
+        {
+            if(!translateMode)
+                error("JavaTypeReplacements is not a valid section in SWIG mode");
+            if(language == java)
+                parseTypeReplacements(node, typeReplacements);
+        }
+        else if(node->name == "CSharpTypeReplacements")
+        {
+            if(!translateMode)
+                error("CSharpTypeReplacements is not a valid section in SWIG mode");
+            if(language == csharp)
+                parseTypeReplacements(node, typeReplacements);
+        }
+        else if(node->name == "Headers")
+        {
+            parseHeaders(node, headers);
+        }
+        else if(node->name == "Classes")
+        {
+            if(!translateMode)
+                error("Classes is not a valid section in SWIG generation mode");
+            parseClasses(node, classes);
+        }
+        else if(node->name == "Namespace")
+        {
+            if(!translateMode)
+                error("Namespace is not a valid section in SWIG generation mode");
+            nameSpace = parseNamespace(node);
+        }
+        else if(node->name == "Package")
+        {
+            if(!translateMode)
+                error("Package is not a valid section in SWIG generation mode");
+            package = parsePackage(node);
+        }
+        else
+        {
+            error(string("Unknow element: ") + node->name);
+        }
+    }
+
+}
+
+char* toChar(char c)
+{
+    charbuf[0] = c;
+    charbuf[1] = '\0';
+    return charbuf;
+}
+
+/// Tokenize a C input into a string array. Ignore preprocessing
+/// directives and removes comments. The tokenization is very loose and
+/// in no way similar to a real C scanner. For our need we just tokenize
+/// identifier, and a few operators and delimiters such as '{' '}' '(' ')' ',' ';' ':'
+void tokenize(string filename, vector<string>& tokens)
+{
+    FILE* f = fopen(filename.c_str(), "rb");
+    if(f == NULL)
+        error(string("Cannot open header file ") + filename);
+
+    //load file in memory
+    fseek(f, 0, SEEK_END);
+    int length = ftell(f);
+    fseek(f, 0, SEEK_SET);
+    char* data = new char[length + 1];
+    fread(data, length, 1, f);
+    data[length] = '\0';
+
+    char* line = strtok(data, "\n");
+    string comment;
+
+    if(line != NULL)
+    {
+        do
+        {
+            string input = Trim(string(line));
+
+            if(input.length() == 0)
+                continue;
+
+            if(input[0] == '#')
+                continue;   //ignore preprocessing directive
+
+            int i = 0;
+            for(bool done = false; !done && i < (int)input.length(); )
+            {
+                switch(input[i])
+                {
+                    case ' ':
+                    case '\t':
+                    case '\r':
+                        //skip whitespaces
+                        do
+                        {
+                            if(isspace(input[i]))
+                                break;
+                        } while(i < (int)input.length());
+                        if(i == (int)input.length())
+                            done = true;
+                        break;
+
+                    case '/':
+                        /*
+                        if(!translateMode)
+                        {
+                            if(i < (int)input.length() - 1 && input[i + 1] == '/')
+                                done = true;    //C++ comment, ignore the rest of the input
+                            else
+                                tokens.push_back("/");
+                        }
+                        else*/
+                        {
+                            if(input[i + 1] == '/')
+                            {
+                                if(input[i + 2] == '/')
+                                {
+                                    //documentation comment
+                                    comment = "";
+                                    while(i < (int)input.length())
+                                    {
+                                        if(input[i] == '\n')
+                                            break;
+                                        comment.push_back(input[i]);
+                                        i++;
+                                    }
+
+                                    tokens.push_back(comment);
+                                }
+                                done = true;
+                            }
+                            else
+                                tokens.push_back("/");
+                        }
+                        break;
+
+                    case '(':
+                        tokens.push_back("(");
+                        break;
+
+                    case ')':
+                        tokens.push_back(")");
+                        break;
+
+                    case '{':
+                        tokens.push_back("{");
+                        break;
+
+                    case '}':
+                        tokens.push_back("}");
+                        break;
+
+                    case ',':
+                        tokens.push_back(",");
+                        break;
+
+                    case ';':
+                        {
+                            //preserve doc comment at the end of the ; if any
+                            bool isComment = false;
+                            int j = i + 1;
+                            while(j < (int)input.length())
+                            {
+                                if(input[j] == '/')
+                                {
+                                    if(input[j+1] == '/' && input[j+2] == '/')
+                                    {
+                                        isComment = true;
+                                        comment = "";
+                                        while(i < (int)input.length())
+                                        {
+                                            if(input[i] == '\n')
+                                                break;
+                                            comment.push_back(input[i]);
+                                            i++;
+                                        }
+                                        tokens.push_back(comment);
+                                        done = true;
+                                        break;
+                                    }
+                                }
+                                else
+                                    if(!isspace(input[j]))
+                                        break;
+                                j++;
+                            }
+                            if(!isComment)
+                                tokens.push_back(";");
+                        }
+                        break;
+
+                    case ':':
+                        tokens.push_back(":");
+                        break;
+
+                    default:
+                        if(isalpha(input[i]) || input[i] == '_')
+                        {
+                            //identifier
+                            string id = toChar(input[i]);
+                            while(++ i < (int)input.length())
+                            {
+                                if(!isalpha(input[i]) &&
+                                    !isdigit(input[i]) &&
+                                    input[i] != '_')
+                                    break;
+                                id += toChar(input[i]);
+                            }
+                            tokens.push_back(id);
+                        }
+                        else
+                        {
+                            int beginStr = i;
+                            if(translateMode && input[i] == '"')
+                            {
+                                string str;
+                                while(i < (int)input.length())
+                                {
+                                    str.push_back(input[i]);
+                                    if(input[i] == '"' && (beginStr < i && input[i-1] != '\\'))
+                                        break;
+                                    i++;
+                                }
+                                ++ i;
+                                tokens.push_back(str);
+                            }
+                            else
+                            {
+                                //we don't care what is at this point of the input
+                                //we store all consecutive characters as a token
+                                //until we find a character known from us.
+                                string token = toChar(input[i]);
+                                while(++ i < (int)input.length())
+                                {
+                                    if(isalpha(input[i]) ||
+                                        input[i] == '_' ||
+                                        isspace(input[i]) ||
+                                        input[i] == '(' ||
+                                        input[i] == ')' ||
+                                        input[i] == '{' ||
+                                        input[i] == '}' ||
+                                        input[i] == ',' ||
+                                        input[i] == ';' ||
+                                        input[i] == '/' ||
+                                        input[i] == ':')
+                                        break;
+                                    token += toChar(input[i]);
+                                }
+                                tokens.push_back(token);
+                            }
+                        }
+                        continue;
+
+
+                }
+                ++ i;
+            }
+        } while((line = strtok(NULL, "\n")) != NULL);
+    }
+
+    delete data;
+    fclose(f);
+}
+
+void processClassIdSection(vector<string>& tokens, int begin, int end)
+{
+    fprintf(outfile, "\nprivate:\n  ");
+    for(int i = begin; i <= end; i++)
+    {
+        string token = tokens[i];
+        fprintf(outfile, "%s ", token.c_str());
+    }
+}
+
+bool isRootObjectMethod(const string& symbolName)
+{
+    map<string, int>::const_iterator itMethod = rootObjectMethods.find(symbolName);
+    return itMethod != rootObjectMethods.end();
+}
+
+bool isAllSlashes(const string& str)
+{
+    for (size_t i = 0; i < str.length(); i++) {
+        if (str[i] != '/')
+            return false;
+    }
+    return str.length() > 3; //A "///" does not count
+}
+
+bool stringReplace(string& str, const string& find, const string& replace)
+{
+    size_t start_pos = str.find(find);
+    if(start_pos == string::npos)
+        return false;
+    str.replace(start_pos, find.length(), replace);
+    return true;
+}
+
+void stripHtml(string& str)
+{
+    //NOTE: We're only stripping tags known to exist in some bits of API documentation
+    stringReplace(str, "<p>", "");
+    stringReplace(str, "<b>", "");
+    stringReplace(str, "<c>", "");
+    stringReplace(str, "</p>", "");
+    stringReplace(str, "</b>", "");
+    stringReplace(str, "</c>", "");
+}
+
+void xmlEscapeString(string& str)
+{
+    stringReplace(str, "&", "&");
+    stringReplace(str, "'", "'");
+    stringReplace(str, "\"", """);
+    stringReplace(str, "<", "<");
+    stringReplace(str, ">", ">");
+}
+
+string linkifyCSharpDocFragment(const string& str)
+{
+    // Explode the fragment into a space delimited list.
+    // Go through the delimited tokens and surround any
+    // token containing "Mg" with {@link <token>} 
+    //
+    // Re-combine each delimited token to form the linkified result
+    std::vector<std::string> elems;
+    std::stringstream ss(str);
+    std::string item;
+    while(std::getline(ss, item, ' ')) {
+        //We can't process these yet, so skip them as they may
+        //interfere with doxygen directive translation
+        if (item == "\\link" || item == "\\endlink")
+            continue;
+        stripHtml(item);
+        xmlEscapeString(item);
+        elems.push_back(item);
+    }
+
+    std::string nspace = nameSpace;
+    if (nspace.empty()) //Faulty logic if this is the case
+        nspace = "OSGeo.MapGuide";
+
+    std::string output;
+    for (size_t i = 0; i < elems.size(); i++) {
+        if (i != 0) {
+            output.append(" ");
+        }
+        //If it contains "Mg", assume it's a MapGuide class name and link-ify it
+        //TODO: Resolve :: to member links. Right now it just linkifies the Mg class name
+        size_t idx = elems[i].find("Mg");
+        if (idx != std::string::npos) {
+            std::string prefix;
+            std::string mgClassName;
+            std::string suffix;
+            //Collect characters before Mg
+            if (idx > 0) {
+                prefix = elems[i].substr(0, idx);
+            }
+            int cont = -1;
+            //Collect the characters in the MapGuide class name
+            for (size_t j = idx; j < elems[i].length(); j++) {
+                if (!isalnum(elems[i][j])) {
+                    cont = j;
+                    break;
+                } else {
+                    mgClassName += elems[i][j];
+                }
+            }
+            //Collect any characters afterwards
+            for (size_t j = cont; j < elems[i].length(); j++) {
+                suffix += elems[i][j];
+            }
+            output.append(prefix);
+            output.append("<see cref=\"");
+            output.append(nspace);
+            output.append(".");
+            output.append(mgClassName);
+            output.append("\" />");
+            output.append(suffix);
+        } else {
+            output.append(elems[i]);
+        }
+    }
+    return output;
+}
+
+string linkifyJavaDocFragment(const string& str)
+{
+    // Explode the fragment into a space delimited list.
+    // Go through the delimited tokens and surround any
+    // token containing "Mg" with {@link <token>} 
+    //
+    // Re-combine each delimited token to form the linkified result
+    std::vector<std::string> elems;
+    std::stringstream ss(str);
+    std::string item;
+    while(std::getline(ss, item, ' ')) {
+        //We can't process these yet, so skip them as they may
+        //interfere with doxygen directive translation
+        if (item == "\\link" || item == "\\endlink")
+            continue;
+        elems.push_back(item);
+    }
+    std::string output;
+    for (size_t i = 0; i < elems.size(); i++) {
+        if (i != 0) {
+            output.append(" ");
+        }
+        //If it contains "Mg", assume it's a MapGuide class name and link-ify it
+        //TODO: Resolve :: to member links. Right now it just linkifies the Mg class name
+        size_t idx = elems[i].find("Mg");
+        if (idx != std::string::npos) {
+            std::string prefix;
+            std::string mgClassName;
+            std::string suffix;
+            //Collect characters before Mg
+            if (idx > 0) {
+                prefix = elems[i].substr(0, idx);
+            }
+            int cont = -1;
+            //Collect the characters in the MapGuide class name
+            for (size_t j = idx; j < elems[i].length(); j++) {
+                if (!isalnum(elems[i][j])) {
+                    cont = j;
+                    break;
+                } else {
+                    mgClassName += elems[i][j];
+                }
+            }
+            //Collect any characters afterwards
+            for (size_t j = cont; j < elems[i].length(); j++) {
+                suffix += elems[i][j];
+            }
+            output.append(prefix);
+            output.append("{@link ");
+            output.append(mgClassName);
+            output.append("}");
+            output.append(suffix);
+        } else {
+            output.append(elems[i]);
+        }
+    }
+    return output;
+}
+
+string doxygenToJavaDoc(const string& commentStr, bool isPublished)
+{
+    // Doxygen documentation translation overview:
+    //
+    // What sorcery allows us to transplant doxygen documentation to our target language of choice?
+    //
+    // The answer lies in the swig directives %javamethodmodifiers and %typemap(javaclassmodifiers)
+    // (and its csharp equvialents %csmethodmodifiers and %typemap(csclassmodifiers))
+    //
+    // The official SWIG documentation says that these directives are used to modify the visibility modifier for
+    // <class declaration|class method name>. Although this directive is mainly used to modify
+    // class/method visibility, the content itself does not have to be public/protected/abstract/static/const/etc
+    // SWIG itself does not do any validation of the content in this directive.
+    //
+    // Given this directive
+    //
+    // %javamethodmodifiers MgMyClass::MyMethod() <content>
+    //
+    // SWIG generates this (Java):
+    //
+    // public class MgMyClass {
+    //    ...
+    //    <content> MyMethod() {
+    //    ...
+    //    }
+    //    ...
+    // }
+    //
+    // <content> should generally be public/protected/abstract/static/const/etc, but SWIG does not validate or enforce this
+    //
+    // Therefore we (ab)use these SWIG directives to prepend arbitrary content to the generated proxy class declaration/methods. In our
+    // case, the content being our doxygen commentary (collected by IMake) translated to the target-specific documentation format
+    //
+    // In fact, this technique is documented as a way of transplanting documentation:
+    //   http://www.swig.org/Doc1.3/Java.html#javadoc_comments
+
+    // Supported doxygen tags:
+    // \brief - Converted to main javadoc body
+    // \param - @param
+    // \return - @return
+    // \deprecated - Adds the @Deprecated annotation to the class/method declaration (after the converted javadoc)
+    // \since - @since
+    // \exception - @exception
+
+    //Re-tokenize the doxygen comments
+    std::vector<std::string> elems;
+    std::stringstream ss(commentStr);
+    std::string item;
+    while(std::getline(ss, item, '\n')) {
+        elems.push_back(item);
+    }
+    size_t i = 0;
+    
+    bool isDeprecated = false;
+    std::vector<std::string> descriptionParts;
+    std::vector<std::string> paramParts;
+    std::vector<std::string> returnParts;
+    std::string sinceVer;
+    std::vector<std::string> exceptionParts;
+
+    while(i < elems.size()) {
+        if (elems[i].find("<!-- Syntax in .Net, Java, and PHP -->") != std::string::npos ||
+            elems[i].find("<!-- Example (PHP) -->") != std::string::npos ||
+            elems[i].find("<!-- Example (Java) -->") != std::string::npos ||
+            elems[i].find("<!-- Example (C#) -->") != std::string::npos) {
+            i++;
+            continue;
+        }
+
+        if (elems[i].find("\\brief") != std::string::npos) {
+            i++;
+            //Keep going until we find the next doxygen directive or end of comments
+            while (i < elems.size() && elems[i].find("\\") == std::string::npos) {
+                if (elems[i].find("<!-- Syntax in .Net, Java, and PHP -->") != std::string::npos ||
+                    elems[i].find("<!-- Example (PHP) -->") != std::string::npos ||
+                    elems[i].find("<!-- Example (Java) -->") != std::string::npos ||
+                    elems[i].find("<!-- Example (C#) -->") != std::string::npos) {
+                    i++;
+                    continue;
+                }
+                std::string token = elems[i].substr(3);
+                if (!token.empty())
+                    descriptionParts.push_back(token);
+                i++;
+            }
+            
+            continue;
+        }
+        else if (elems[i].find("\\param") != std::string::npos) {
+            std::string paramPart = elems[i].substr(elems[i].find("\\param") + 6);
+            paramPart.append(" ");
+            i++;
+            //Keep going until we find the next doxygen directive or end of comments
+            while (i < elems.size() && elems[i].find("\\") == std::string::npos) {
+                std::string token = elems[i].substr(3);
+                if (!token.empty()) {
+                    paramPart.append(token);
+                    paramPart.append(" ");
+                }
+                i++;
+            }
+            paramParts.push_back(paramPart);
+            continue;
+        }
+        else if (elems[i].find("\\return") != std::string::npos) {
+            i++;
+            //Keep going until we find the next doxygen directive or end of comments
+            while (i < elems.size() && elems[i].find("\\") == std::string::npos) {
+                if (elems[i].find("<!-- Syntax in .Net, Java, and PHP -->") != std::string::npos ||
+                    elems[i].find("<!-- Example (PHP) -->") != std::string::npos ||
+                    elems[i].find("<!-- Example (Java) -->") != std::string::npos ||
+                    elems[i].find("<!-- Example (C#) -->") != std::string::npos) {
+                    i++;
+                    continue;
+                }
+                std::string token = elems[i].substr(3);
+                if (!token.empty())
+                    returnParts.push_back(token);
+                i++;
+            }
+            continue;
+        }
+        else if (elems[i].find("\\deprecated") != std::string::npos) {
+            i++;
+            isDeprecated = true;
+            continue;
+        }
+        else if (elems[i].find("\\since") != std::string::npos) {
+            sinceVer = elems[i].substr(elems[i].find("\\since") + 6);
+            i++;
+            continue;
+        }
+        else if (elems[i].find("\\exception") != std::string::npos) {
+            std::string except = elems[i].substr(elems[i].find("\\exception") + 10);
+            exceptionParts.push_back(except);
+            i++;
+            continue;
+        }
+        i++;
+    }
+
+    // ---------------------- JAVADOC START ------------------------ //
+    std::string javaDoc = "\n/**\n";
+
+    if (descriptionParts.size() > 0) {
+        if (!isPublished) {
+            javaDoc.append(" * ");
+            javaDoc.append(EXTERNAL_API_DOCUMENTATION);
+            javaDoc.append("\n");
+        }
+        for (size_t i = 0; i < descriptionParts.size(); i++) {
+            javaDoc.append(" *");
+            javaDoc.append(linkifyJavaDocFragment(descriptionParts[i]));    
+            javaDoc.append("\n");
+        }
+        javaDoc.append(" *\n");
+    } else {
+        if (!isPublished) {
+            javaDoc.append(" * ");
+            javaDoc.append(EXTERNAL_API_DOCUMENTATION);
+            javaDoc.append("\n");
+        } else {
+            javaDoc.append(" * TODO: API Documentation is missing or failed to translate doxygen brief directive (message inserted by IMake.exe)\n");
+        }
+    }
+
+    if (paramParts.size() > 0) {
+        for (size_t i = 0; i < paramParts.size(); i++) {
+            javaDoc.append(" * @param ");
+            javaDoc.append(linkifyJavaDocFragment(paramParts[i]));
+            javaDoc.append("\n");
+        }
+    }
+
+    if (returnParts.size() > 0) {
+        javaDoc.append(" * @return ");
+        for (size_t i = 0; i < returnParts.size(); i++) {
+            javaDoc.append(linkifyJavaDocFragment(returnParts[i]));
+            if (i < returnParts.size() - 1)
+                javaDoc.append("\n * ");
+        }
+        javaDoc.append("\n");
+    }
+
+    if (exceptionParts.size() > 0) {
+        for (size_t i = 0; i < exceptionParts.size(); i++) {
+            javaDoc.append(" * @exception ");
+            javaDoc.append(exceptionParts[i]);
+            javaDoc.append("\n");
+        }
+    }
+
+    if (!sinceVer.empty()) {
+        javaDoc.append(" * @since ");
+        javaDoc.append(sinceVer);
+    }
+
+    // ---------------------- JAVADOC END ------------------------ //
+    javaDoc.append(" */\n");
+    if (isDeprecated)
+        javaDoc.append("@Deprecated\n");
+    return javaDoc;
+}
+
+string doxygenToCsharpDoc(const string& commentStr, bool isPublished)
+{
+    //See doxygenToJavaDoc for how we do this sorcery
+
+    std::string nspace = nameSpace;
+    if (nspace.empty()) //Faulty logic if this is the case
+        nspace = "OSGeo.MapGuide";
+
+    //Re-tokenize the doxygen comments
+    std::vector<std::string> elems;
+    std::stringstream ss(commentStr);
+    std::string item;
+    while(std::getline(ss, item, '\n')) {
+        elems.push_back(item);
+    }
+    size_t i = 0;
+    
+    bool isDeprecated = false;
+    std::vector<std::string> descriptionParts;
+    std::vector<std::string> paramParts;
+    std::vector<std::string> returnParts;
+    std::vector<std::string> exceptionParts;
+
+    while(i < elems.size()) {
+        if (elems[i].find("<!-- Syntax in .Net, Java, and PHP -->") != std::string::npos ||
+            elems[i].find("<!-- Example (PHP) -->") != std::string::npos ||
+            elems[i].find("<!-- Example (Java) -->") != std::string::npos ||
+            elems[i].find("<!-- Example (C#) -->") != std::string::npos) {
+            i++;
+            continue;
+        }
+
+        if (elems[i].find("\\brief") != std::string::npos) {
+            i++;
+            //Keep going until we find the next doxygen directive or end of comments
+            while (i < elems.size() && elems[i].find("\\") == std::string::npos) {
+                if (elems[i].find("<!-- Syntax in .Net, Java, and PHP -->") != std::string::npos ||
+                    elems[i].find("<!-- Example (PHP) -->") != std::string::npos ||
+                    elems[i].find("<!-- Example (Java) -->") != std::string::npos ||
+                    elems[i].find("<!-- Example (C#) -->") != std::string::npos) {
+                    i++;
+                    continue;
+                }
+                std::string token = elems[i].substr(3);
+                if (!token.empty())
+                    descriptionParts.push_back(token);
+                i++;
+            }
+            
+            continue;
+        }
+        else if (elems[i].find("\\param") != std::string::npos) {
+            std::string paramPart = elems[i].substr(elems[i].find("\\param") + 6);
+            paramPart.append(" ");
+            i++;
+            //Keep going until we find the next doxygen directive or end of comments
+            while (i < elems.size() && elems[i].find("\\") == std::string::npos) {
+                std::string token = elems[i].substr(3);
+                if (!token.empty()) {
+                    paramPart.append(token);
+                    paramPart.append(" ");
+                }
+                i++;
+            }
+            paramParts.push_back(paramPart);
+            continue;
+        }
+        else if (elems[i].find("\\return") != std::string::npos) {
+            i++;
+            //Keep going until we find the next doxygen directive or end of comments
+            while (i < elems.size() && elems[i].find("\\") == std::string::npos) {
+                if (elems[i].find("<!-- Syntax in .Net, Java, and PHP -->") != std::string::npos ||
+                    elems[i].find("<!-- Example (PHP) -->") != std::string::npos ||
+                    elems[i].find("<!-- Example (Java) -->") != std::string::npos ||
+                    elems[i].find("<!-- Example (C#) -->") != std::string::npos) {
+                    i++;
+                    continue;
+                }
+                std::string token = elems[i].substr(3);
+                if (!token.empty())
+                    returnParts.push_back(token);
+                i++;
+            }
+            continue;
+        }
+        else if (elems[i].find("\\deprecated") != std::string::npos) {
+            i++;
+            isDeprecated = true;
+            continue;
+        }
+        else if (elems[i].find("\\exception") != std::string::npos) {
+            std::string except = elems[i].substr(elems[i].find("\\exception") + 10);
+            exceptionParts.push_back(except);
+            i++;
+            continue;
+        }
+        i++;
+    }
+
+    // ---------------------- csharpDoc START ------------------------ //
+    std::string csharpDoc = "\n///<summary>\n";
+
+    if (descriptionParts.size() > 0) {
+        if (!isPublished) {
+            csharpDoc.append("/// ");
+            csharpDoc.append(EXTERNAL_API_DOCUMENTATION);
+            csharpDoc.append("\n");
+        }
+        for (size_t i = 0; i < descriptionParts.size(); i++) {
+            csharpDoc.append("///");
+            csharpDoc.append(linkifyCSharpDocFragment(descriptionParts[i]));    
+            csharpDoc.append("\n");
+        }
+        csharpDoc.append("///</summary>\n");
+    } else {
+        if (!isPublished) {
+            csharpDoc.append("/// ");
+            csharpDoc.append(EXTERNAL_API_DOCUMENTATION);
+            csharpDoc.append("\n");
+        } else {
+            csharpDoc.append("///TODO: API Documentation is missing or failed to translate doxygen brief directive (message inserted by IMake.exe)\n///</summary>\n");
+        }
+    }
+
+    if (paramParts.size() > 0) {
+        for (size_t i = 0; i < paramParts.size(); i++) {
+            std::string paramPart = paramParts[i];
+            stripHtml(paramPart);
+            xmlEscapeString(paramPart);
+
+            std::vector<std::string> pelems;
+            std::stringstream pss(linkifyCSharpDocFragment(paramPart));
+            std::string pitem;
+            while(std::getline(pss, pitem, ' ')) {
+                if (!pitem.empty())
+                    pelems.push_back(pitem);
+            }
+    
+            if (pelems.size() > 1) { //Should be
+                csharpDoc.append("///<param name=\"");
+                csharpDoc.append(pelems[0]);
+                csharpDoc.append("\">");
+                csharpDoc.append("\n///");
+                for (size_t i = 1; i < pelems.size(); i++) {
+                    csharpDoc.append(" ");
+                    csharpDoc.append(pelems[i]);
+                }
+                csharpDoc.append("\n///</param>\n");
+            }
+        }
+    }
+
+    if (returnParts.size() > 0) {
+        csharpDoc.append("///<returns>");
+        for (size_t i = 0; i < returnParts.size(); i++) {
+            string retPart = returnParts[i];
+            stripHtml(retPart);
+            csharpDoc.append(linkifyCSharpDocFragment(retPart));
+            if (i < returnParts.size() - 1)
+                csharpDoc.append("\n/// ");
+        }
+        csharpDoc.append("\n///</returns>\n");
+    }
+
+    if (exceptionParts.size() > 0) {
+        for (size_t i = 0; i < exceptionParts.size(); i++) {
+
+            std::vector<std::string> eelems;
+            std::stringstream ess(exceptionParts[i]);
+            std::string eitem;
+            while(std::getline(ess, eitem, ' ')) {
+                if (!eitem.empty())
+                    eelems.push_back(eitem);
+            }
+
+            if (eelems.size() > 0)
+            {
+                //Skip anything that is not of the form:
+                //\exception MgExceptionType Description of cases when the exception is thrown
+                //
+                //So the first token must start with "Mg"
+                std::string t("Mg");
+                if (eelems[0].compare(0, t.length(), t) == 0)
+                {
+                    csharpDoc.append("///<exception cref=\"");
+                    csharpDoc.append(nspace);
+                    csharpDoc.append(".");
+                    if (eelems.size() > 1) {
+                        csharpDoc.append(eelems[0]);
+                        csharpDoc.append("\">");
+                        for (size_t j = 1; j < eelems.size(); j++) {
+                            csharpDoc.append(" ");
+                            csharpDoc.append(eelems[j]);
+                        }
+                        csharpDoc.append("</exception>\n");
+                    }
+                    else {
+                        csharpDoc.append(eelems[0]);
+                        csharpDoc.append("\"></exception>\n");
+                    }
+                }
+            }
+        }
+    }
+
+    // ---------------------- csharpDoc END ------------------------ //
+    //csharpDoc.append("///\n");
+    if (isDeprecated)
+        csharpDoc.append("[Obsolete(\"This method is deprecated\")]\n");
+    return csharpDoc;
+}
+
+void outputClassDoc(const string& className, const string& commentStr)
+{
+    //Nothing for PHP
+    if (language == php)
+        return;
+
+    string convertedDoc;
+
+    //NOTE: SWIG 3.0 doesn't implictly insert class in the modifier
+    string classKeyword = "class";
+    if (language == java) {
+        convertedDoc = doxygenToJavaDoc(commentStr, true); //EXTERNAL_API only applies to class members, so treat this fragment as PUBLISHED_API
+        fprintf(docOutFile, "\n%%typemap(javaclassmodifiers) %s %%{%s public %s%%}\n", className.c_str(), convertedDoc.c_str(), classKeyword.c_str());
+    } else if(language == csharp) {
+        convertedDoc = doxygenToCsharpDoc(commentStr, true); //EXTERNAL_API only applies to class members, so treat this fragment as PUBLISHED_API
+        fprintf(docOutFile, "\n%%typemap(csclassmodifiers) %s %%{%s public partial %s%%}\n", className.c_str(), convertedDoc.c_str(), classKeyword.c_str());
+    }
+}
+
+void outputMethodDoc(const string& className, const string& methodDecl, const string& commentStr, bool isPublished)
+{
+    //Nothing for PHP
+    if (language == php)
+        return;
+
+    //Skip destructors
+    if (methodDecl.find("~") != string::npos)
+        return;
+
+    string convertedDoc;
+    string swigMethodDecl;
+    swigMethodDecl = className;
+    swigMethodDecl += "::";
+    
+    //Re-tokenize the method declaration
+    //
+    //We want the bits between '(' and ')' (parentheses included) and the first token before the '('
+    std::vector<std::string> elems;
+    std::stringstream ss(methodDecl);
+    std::string item;
+    while(std::getline(ss, item, ' ')) {
+        elems.push_back(item);
+    }
+    std::string methodName;
+    for (size_t i = 0; i < elems.size(); i++) {
+        if (elems[i] == "" || elems[i] == "virtual")
+            continue;
+
+        if (elems[i] == "(") {
+            if (i > 0) //Should be
+                methodName += elems[i-1];
+            methodName += elems[i];
+            size_t j = i;
+            //Process parameters between the ( and )
+            while(j < elems.size()) {
+                j++;
+                if (elems[j] != ")") {
+                    methodName += " ";
+                    methodName += elems[j];
+                } else {
+                    methodName += " ";
+                    methodName += elems[j];
+                    break;
+                }
+            }
+            break;
+        }
+    }
+
+    if (methodName.empty())
+        return;
+
+    swigMethodDecl += methodName;
+
+    if (language == java) {
+        convertedDoc = doxygenToJavaDoc(commentStr, isPublished);
+        fprintf(docOutFile, "\n%%javamethodmodifiers %s %%{%s public%%}\n", swigMethodDecl.c_str(), convertedDoc.c_str());
+    } else if(language == csharp) {
+        convertedDoc = doxygenToCsharpDoc(commentStr, isPublished);
+        fprintf(docOutFile, "\n%%csmethodmodifiers %s %%{%s public%%}\n", swigMethodDecl.c_str(), convertedDoc.c_str());
+    }
+}
+
+void processExternalApiSection(string& className, vector<string>& tokens, int begin, int end, bool isPublished)
+{
+    //until we find a problem with that, we output whatever we find in this section. In the
+    //process we perform type substitution if required
+    fprintf(outfile, "   ");
+    int nesting = 0;
+    bool destructor = false;
+    bool firstToken = true;
+    bool assignmentAdded = false;
+
+    FILE* propertyFile = NULL;
+
+    string commentStr;
+    string methodDecl;
+
+    //NOTE: This can get called multiple times for a given class with various offsets, so use a std::set to guard
+    //against duplicate class documentation entries
+    if (!translateMode && classesWithDocs.find(className) == classesWithDocs.end() && begin > 0) {
+        
+        int slashesCount = 0;
+        for (int i = 0; i < begin; i++) {
+            string token = tokens[i];
+            if(token == "")
+                continue;
+            //pickup the doc comments for the class, if any.
+            //all contiguous doc comment will be considered part of the class comment
+            if(strncmp(token.c_str(), "///", 3) == 0)
+            {
+                if (isAllSlashes(token))
+                {
+                    slashesCount++;
+                }
+
+                if (slashesCount > 1) //Stop here, as this is generally the start of the documentation for the first method in the class
+                    break;
+
+                commentStr.append(token);
+                commentStr.append("\n");
+                continue;
+            }
+        }
+        if (!commentStr.empty()) {
+            outputClassDoc(className, commentStr);
+            commentStr.clear();
+            classesWithDocs.insert(className);
+        } else if (language == csharp) {
+            //We need to ensure the partial modifier is applied. However since we (ab)use csclassmodifiers to do this, if there's no class documentation
+            //then it won't apply the required partial modifier. So we need to insert some kind of comment string here, so take this opportunity to 
+            //note that this class has no documentation.
+            std::string cmnt = "///\n";
+            cmnt += "/// \\brief\n";
+            cmnt += "/// TODO: This class has no class documentation (message inserted by IMake.exe)\n";
+            cmnt += "///";
+            outputClassDoc(className, cmnt);
+            classesWithDocs.insert(className);
+        }
+    }
+    for(int i = begin; i <= end; i++)
+    {
+        assignmentAdded = false;
+        string token = tokens[i];
+        string nextToken = (i < (int)tokens.size() - 1) ? tokens[i + 1] : "";
+        if (token == "")
+            continue;
+
+        //Abstract classes really mess with how vanilla SWIG generates the bindings for PHP
+        //So let's just strip off all pure virtuals from our emitted class definitions (ie. Don't emit the "= 0" part)
+        if (!translateMode && language == php)
+        {
+            if (token[0] == '=')
+            {
+                if (nextToken[0] == '0')
+                {
+                    i++; //Advance to nextToken;
+                    continue; //Then skip
+                }
+                else if (token.length() > 1 && token[1] == '0') // "=0"
+                {
+                    continue; //Just skip
+                }
+            }
+        }
+
+        //pickup the doc comments for the class, if any.
+        //all contiguous doc comment will be considered part of the class comment
+        if(strncmp(token.c_str(), "///", 3) == 0)
+        {
+            commentStr.append(token);
+            commentStr.append("\n");
+            continue;
+        }
+
+        if(token[0] == '_' || isalpha(token[0]))
+        {
+            if(typeReplacements.find(token) != typeReplacements.end())
+                token = typeReplacements[token];
+        }
+        if(translateMode)
+        {
+            if(!strncmp(token.c_str(), "///", 3))
+            {
+                fprintf(outfile, "%s\n   ", token.c_str());
+                firstToken = true;
+                continue;
+            }
+            if (firstToken)
+            {
+                if (translateMode && !commentStr.empty()) 
+                {
+                    string convertedDoc;
+                    if (language == java) {
+                        convertedDoc = doxygenToJavaDoc(commentStr, isPublished);
+                        fprintf(outfile, "%s\n   ", convertedDoc.c_str());
+                    } else if (language == csharp) {
+                        convertedDoc = doxygenToCsharpDoc(commentStr, isPublished);
+                        fprintf(outfile, "%s\n   ", convertedDoc.c_str());
+                    }
+                    commentStr.clear();
+                }
+            }
+
+            if(firstToken && (language == java || language == csharp))
+            {
+                fprintf(outfile, "public ");
+                if(language == csharp)
+                {
+                    //Evolution in this tokenizer have gone to far for the original,
+                    //simplistic design. We should rewrite it at some point.
+                    //Here, we lookahead to see if the variable name is hidding
+                    //a known method of the class 'Object'. if it is, we want
+                    //to hide the keyword 'new' to the declaration
+                    for(int j= i + 1; j <= end; j++)
+                    {
+                        string tok = tokens[j];
+                        if(tok[0] == ';' || tok[0] == '=')
+                        {
+                            if(isRootObjectMethod(tokens[j - 1]))
+                            {
+                                fprintf(outfile, "new ");
+                            }
+                            break;
+                        }
+                    }
+                }
+                firstToken = false;
+            }
+            if(token[0] == ';')
+            {
+                // if there is a doc comment as part of this line, check if it
+                // contains the macro V(...) which indicates a string value to be assigned before the ;
+                size_t posComment = token.find_first_of("///");
+                if (posComment != string::npos)
+                {
+                    if (strstr(token.c_str(), "value("))
+                    {
+                        string comment = token.substr(posComment+3);
+                        size_t posBeginValue = comment.find("value(");
+                        size_t posEndValue = comment.find(")", posBeginValue + 1);
+                        if (posEndValue != string::npos)
+                        {
+                            size_t strLen = posEndValue - posBeginValue - 6;
+                            string expr = comment.substr(posBeginValue + 6, strLen);
+                            comment = comment.substr(0, posBeginValue) + comment.substr(posEndValue + 1);
+
+                            if(Trim(comment) == "")
+                            {
+                                //comment contained only the string value. remove comment at all
+                                token = " = " + expr + ";";
+                            }
+                            else
+                            {
+                                // insert
+                                token = " = " + expr + "; /// " + comment;
+                            }
+                            assignmentAdded = true;
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            // Doc comment may contain a directive for emitting .Net properties
+            if(language == csharp && 0 != strstr(token.c_str(), "///"))
+            {
+                bool setProp = false;
+                bool getProp = false;
+                bool inherited = false;
+                if (string::npos != token.find("__set")) { setProp = true; }
+                if (string::npos != token.find("__get")) { getProp = true; }
+                if (string::npos != token.find("__inherited")) { inherited = true; }
+
+                size_t methodStart = string::npos;
+                int j=0;
+                if (setProp || getProp)
+                {
+                    for (j = 3; j < 6; j++)
+                    {
+                        methodStart = i-j>=0 ? tokens[i-j].find("Get") : string::npos;
+                        if (string::npos != methodStart) break;
+                    }
+                }
+
+                bool firstProp = true;
+                if (string::npos != methodStart && (setProp || getProp))
+                {
+                    //NOTE: We could leverage the SWIG attribute system here to generate properties, but
+                    //for purposes of compatibility, we'll generate properties the "old fashioned way" as
+                    //pass-throughs to their respective Get/Set methods. Using the attribute system actually
+                    //replaces the Get/Set methods, which although is cleaner, it will cause lots of unnecessary
+                    //breakage
+
+                    if (NULL == propertyFile)
+                    {
+                    #ifdef _WIN32
+                        string fname = ".\\";
+                    #else
+                        string fname = "./";
+                    #endif
+                        if (!customPath.empty())
+                        {
+                            fname = customPath;
+                        #ifdef _WIN32
+                            if (fname[fname.size() - 1] != '\\')
+                                fname.append("\\");
+                        #else
+                            if (fname[fname.size() - 1] != '/')
+                                fname.append("/");
+                        #endif
+                        }
+                        fname.append("MapGuideApi_Properties.i");
+                        //fname.append(className);
+                        //fname.append("Prop");
+                        propertyFile = fopen(fname.c_str(),"a+");
+                        if (NULL == propertyFile)
+                        {
+                            printf("Unable to open autogen property file %s\n", fname.c_str());
+                        }
+                        else
+                        {
+                            printf("Appending autogen property file %s\n", fname.c_str());
+                            if (firstProp) {
+                                //Start SWIG typemap section for this class
+                                fprintf(propertyFile, "//BEGIN - Property typemaps for %s\n", className.c_str());
+                                fprintf(propertyFile, "%%typemap(cscode) %s %%{\n", className.c_str());
+                                firstProp = false;
+                            }
+                        }
+                    }
+
+                    if (NULL != propertyFile)
+                    {
+                        string propName = tokens[i-j].substr(3);
+                        string propType = tokens[i-j-1];
+                        if (propType == "*")
+                        {
+                            propType = tokens[i-j-2];
+                            propType.append(tokens[i-j-1]);
+                        }
+                        else if (propType == "BYTE") {propType = "byte"; }
+                        else if (propType == "INT8") {propType = "short"; }
+                        else if (propType == "INT16") {propType = "short"; }
+                        else if (propType == "INT32") {propType = "int"; }
+                        else if (propType == "UINT32") {propType = "uint"; }
+                        else if (propType == "INT64") {propType = "long"; }
+                        else if (propType == "STRING") {propType = "string"; }
+
+                        string::size_type pos = propType.find('*');
+                        if (string::npos != pos) propType[pos] = ' ';
+
+                        fprintf(propertyFile, "public %s%s %s\n{\n",
+                                        inherited? "new ": "",
+                                        propType.c_str(), propName.c_str());
+                        
+                        if (setProp) { fprintf(propertyFile, "   set { Set%s(value); }\n", propName.c_str()); }
+                        if (getProp) { fprintf(propertyFile, "   get { return Get%s(); }\n", propName.c_str()); }
+
+                        fprintf(propertyFile, "}\n");\
+                    }
+                }
+            }
+        }
+
+        if (token.length() > 0)
+        {
+            fprintf(outfile, "%s ", token.c_str());
+        }
+        if (token.find('~') != string::npos && nextToken.find(className) != string::npos)
+        {
+            //Register the fact that a destructor was written
+            if (wroteDestructorsFor.find(className) == wroteDestructorsFor.end())
+            {
+                wroteDestructorsFor.insert(className);
+            }
+        }
+
+        methodDecl.append(" ");
+        methodDecl.append(token);
+        if(token[0] == ';' || assignmentAdded)
+        {
+            if (!translateMode) {
+                outputMethodDoc(className, methodDecl, commentStr, isPublished);
+            }
+            commentStr.clear();
+            methodDecl.clear();
+            if(nesting == 0)
+            {
+                fprintf(outfile, "\n   ");
+                firstToken = true;
+            }
+        }
+        else if(tokens[i] == "{")
+            ++ nesting;
+        else if(tokens[i] == "}")
+        {
+            if(-- nesting == 0)
+                fprintf(outfile, "\n   ");
+        }
+    }
+
+    if (NULL != propertyFile)
+    {
+        //End SWIG typemap section for class
+        fprintf(propertyFile, "%%} //END - Properties typemap for %s\n", className.c_str());
+        fclose(propertyFile);
+    }
+}
+
+void processHeaderFile(string header, const string& relRoot)
+{
+    vector<string> tokens;
+
+    string theHeader;
+    if (absPaths)
+    {
+        theHeader = header;
+    }
+    else
+    {
+        if (relRoot.empty())
+        {
+            theHeader = header;
+        }
+        else
+        {
+            theHeader = relRoot;
+            theHeader += "/";
+            theHeader += header;
+        }
+    }
+    tokenize(theHeader, tokens);
+
+    if(!translateMode)
+    {
+        //short banner about this file
+        fprintf(outfile, "\n// Definitions from file %s\n//\n", theHeader.c_str());
+    }
+
+    //ignore every token outside of a class definition
+    for(int i = 0; i < (int)tokens.size(); i++)
+    {
+        if(tokens[i] != "class")
+            continue;
+
+        //look for the first '{' (class definition) or ';' (incomplete definition)
+        bool incompleteDef = false;
+        int colonPos = -1;
+        int j = ++i;
+        for(; j < (int)tokens.size(); j++)
+        {
+            if(tokens[j][0] == ';')
+            {
+                incompleteDef = true;
+                break;
+            }
+            else if(tokens[j] == "{")
+                break;
+            else if(tokens[j] == ":")
+                colonPos = j;
+
+        }
+
+        if(incompleteDef)
+            continue;
+
+        //get class name
+        string className = colonPos == -1? tokens[j - 1]: tokens[colonPos - 1];
+
+        //in translation mode, filters out clases which don't belong to the class list
+        bool ignore = translateMode && classes.find(className) == classes.end();
+        if (verbose)
+        {
+            printf("Processing header: %s (from: %s)\n", className.c_str(), theHeader.c_str());
+        }
+        if(!ignore)
+        {
+            if(translateMode)
+            {
+                if(language == java)
+                {
+                    string javaFile = target;
+                    char end = javaFile[javaFile.length() - 1];
+                    if(end != FILESEP && end != '/')
+                        javaFile.push_back(FILESEP);
+                    javaFile += className + ".java";
+                    outfile = fopen(javaFile.c_str(), "w");
+                    if(outfile == NULL)
+                        error(string("Cannot create java file ") + javaFile);
+
+                    if(package != "")
+                        fprintf(outfile, "package %s;\n\n", package.c_str());
+                }
+
+                //pickup the doc comments for the class, if any.
+                //all contiguous doc comment will be considered part of the class comment
+                string commentStr;
+                int commentStart = i - 2;
+                for(; commentStart >= 0; )
+                {
+                    const char* thisTok = tokens[commentStart].c_str();
+                    if(strncmp(tokens[commentStart].c_str(), "///", 3))
+                        break;
+                    commentStart--;
+                }
+                if(++commentStart < i - 2)
+                {
+                    for(commentStart = commentStart < 0? commentStart+1: commentStart; commentStart <= i - 2; commentStart++)
+                    {
+                        const char* thisTok = tokens[commentStart].c_str();
+                        commentStr.append(thisTok);
+                        commentStr.append("\n");
+                    }
+                }
+
+                if (!commentStr.empty()) 
+                {
+                    string convertedDoc;
+                    if (language == java) {
+                        convertedDoc = doxygenToJavaDoc(commentStr, true);
+                        fprintf(outfile, "%s", convertedDoc.c_str());
+                    } else if (language == csharp) {
+                        convertedDoc = doxygenToCsharpDoc(commentStr, true);
+                        fprintf(outfile, "%s", convertedDoc.c_str());
+                    }
+                }
+            }
+
+            //output the class header
+            if(translateMode && (language == java || language == csharp))
+                fprintf(outfile, "public ");
+
+            fprintf(outfile, "class %s", className.c_str());
+            if(colonPos != -1)
+            {
+                fprintf(outfile, " : ");
+                for(int k = colonPos + 1; k < j; k++)
+                    fprintf(outfile, "%s ", tokens[k].c_str());
+            }
+            if(!translateMode)
+                fprintf(outfile, "\n{\npublic:\n");
+            else
+                fprintf(outfile, "\n{\n");
+        }
+
+        //collect pointers to sections.
+        vector<int> sections;
+
+        ++j;
+        for (size_t nesting = 0; j < (int)tokens.size(); j++)
+        {
+            if(tokens[j] == ":")
+            {
+                if(nesting == 0 && !ignore)
+                {
+                    string sectionName = tokens[j - 1];
+                    if(sectionName == "EXTERNAL_API" || sectionName == "INTERNAL_API" || sectionName == "CLASS_ID" ||
+                        sectionName == "PUBLISHED_API" || sectionName == "public" || sectionName == "protected" || sectionName == "private")
+                        sections.push_back(j);
+                }
+            }
+            else if(tokens[j] == "{")
+                ++ nesting;
+            else if(tokens[j] == "}")
+            {
+                if(nesting > 0)
+                    nesting --;
+                else
+                    break;
+            }
+
+        }
+        sections.push_back(j);
+
+        // process EXTERNAL_API and CLASS_ID sections
+        for(int k = 0; k < (int)sections.size() - 1; k++)
+        {
+            string sectionName = tokens[sections[k] - 1];
+            if(sectionName == "EXTERNAL_API" || sectionName == "PUBLISHED_API")
+                processExternalApiSection(className, tokens, sections[k] + 1, sections[k + 1] - (k < (int)sections.size() - 2? 2: 1), (sectionName == "PUBLISHED_API"));
+            else if(sectionName == "CLASS_ID" && !translateMode)
+                processClassIdSection(tokens, sections[k] + 1, sections[k + 1] - (k < (int)sections.size() - 2? 2: 1));
+        }
+
+        // Write destructor if we didn't visit one and we're not generating constants
+        if (!translateMode && wroteDestructorsFor.find(className) == wroteDestructorsFor.end())
+        {
+            fprintf(outfile, "\r\npublic:\r\n   virtual ~%s(); //Destructor inserted by IMake", className.c_str());
+            wroteDestructorsFor.insert(className);
+        }
+
+        //end of class
+        if(!ignore)
+        {
+            fprintf(outfile, "\n}");
+            if(!translateMode) {
+                fprintf(outfile, ";\n\n");
+            }
+            else {
+                fprintf(outfile, "\n\n");
+            }
+        }
+
+    }
+}
+
+void createSWGInterfaceFile(const string& outDir, const string& relRoot)
+{
+    printf("\n\nGenerating interface file %s...\n", target.c_str());
+
+    //validate our mandatory sections
+    if(module.length() == 0)
+        error("Module section is missing");
+    if(target.length() == 0)
+        error("Target section is missing");
+
+    if(headers.size() == 0)
+    {
+        warning("No header files to process, no class generated.");
+        return;
+    }
+
+    if(!translateMode || language != java)
+    {
+        string swigTarget = target;
+        string swigDocTarget = docTarget;
+        if (!outDir.empty())
+        {
+            swigTarget = outDir;
+            swigTarget += "/";
+            swigTarget += target;
+            
+            swigDocTarget = outDir;
+            swigDocTarget += "/";
+            swigDocTarget += docTarget;
+        }
+        outfile = fopen(swigTarget.c_str(), "w");
+        if(outfile == NULL)
+            error(string("Cannot create target file ") + target);
+        docOutFile = fopen(swigDocTarget.c_str(), "w");
+        if(docOutFile == NULL)
+            error(string("Cannot create doctarget file ") + docTarget);
+    }
+
+    time_t now = time(NULL);
+
+    //write the banner
+    fprintf(outfile, "//======================================================\n");
+    fprintf(outfile, "// Generated with IMake version %s\n", version);
+    fprintf(outfile, "// %s\n", asctime(localtime(&now)));
+    fprintf(outfile, "//\n");
+
+    //write the module
+    fprintf(outfile, "%%module %s\n", module.c_str());
+
+    if(!translateMode)
+    {
+        //write the banner
+        fprintf(docOutFile, "//======================================================\n");
+        fprintf(docOutFile, "// Generated with IMake version %s\n", version);
+        fprintf(docOutFile, "// %s\n", asctime(localtime(&now)));
+        fprintf(docOutFile, "//\n");
+    }
+
+    if(!translateMode || language != java)
+    {
+        //write the C++ inline code
+        fprintf(outfile, "%%{\n%s\n%%}\n\n", cppInline.c_str());
+
+        //write the typedefs
+        fprintf(outfile, "%s\n", typedefs.c_str());
+
+        //write the SWIG inline code
+        fprintf(outfile, "%s\n", swigInline.c_str());
+    }
+
+    //process the headers
+    for(vector<string>::const_iterator it = headers.begin(); it != headers.end(); it++)
+        processHeaderFile(*it, relRoot);
+
+    if(!translateMode || language != java)
+    {
+        fclose(outfile);
+        if (docOutFile != NULL) {
+            fclose(docOutFile);
+        }
+    }
+}
+
+void createNativeFile(const string& outDir, const string& relRoot)
+{
+    if(target.length() == 0)
+        error("Target section is missing");
+
+    if (!translateMode && docTarget.length() == 0)
+        error("DocTarget section is missing");
+
+    if(language == unknown)
+        error("Unknown language");
+
+    if(language != java)
+    {
+        outfile = fopen(target.c_str(), "w");
+        if(outfile == NULL)
+            error(string("Cannot create file ") + target);
+        if (!translateMode) {
+            docOutFile = fopen(docTarget.c_str(), "w");
+            if (docOutFile == NULL)
+                error(string("Cannot create file ") + docTarget);
+        }
+    }
+
+    if(language == php)
+        fprintf(outfile, "<?php\n\n");
+    else
+    {
+        if(nameSpace != "")
+        {
+            if(language == csharp)
+                fprintf(outfile, "namespace %s {\n\n", nameSpace.c_str());
+        }
+    }
+
+    if(language != java)
+    {
+        //write the inline code
+        fprintf(outfile, "%s\n\n", swigInline.c_str());
+    }
+
+    //process the headers
+    for(vector<string>::const_iterator it = headers.begin(); it != headers.end(); it++)
+        processHeaderFile(*it, relRoot);
+
+    if(language == php)
+        fprintf(outfile, "?>");
+    else
+    {
+        if(nameSpace != "")
+        {
+            if(language == csharp)
+                fprintf(outfile, "}\n");
+        }
+    }
+
+    if(language != java)
+        fclose(outfile);
+    if (docOutFile != NULL)
+        fclose(docOutFile);
+}
+
+void createInterfaceFile(const char* paramFile, const string& outDir, const string& relRoot)
+{
+    FILE* file = fopen(paramFile, "r");
+    if(file == NULL)
+    {
+        error(string("Cannot open parameter file ") + paramFile);
+    }
+
+    fseek(file, 0, SEEK_END);
+    int length = ftell(file);
+    fseek(file, 0, SEEK_SET);
+    char* data = new char[length + 1];
+    memset(data, 255, length + 1);
+    fread(data, length, 1, file);
+    char* end = strchr(data, 255);
+    *end = '\0';
+
+    parseParameterFile(data, relRoot);
+
+    if(!translateMode)
+        createSWGInterfaceFile(outDir, relRoot);
+    else
+        createNativeFile(outDir, relRoot);
+
+}
+
+void usage(TCLAP::CmdLineInterface& cmd)
+{
+    TCLAP::CmdLineOutput* cmdOutput = cmd.getOutput();
+    cmdOutput->usage(cmd);
+    exit(1);
+}
+
+int main(int argc, char** argv)
+{
+    printf("\nIMake - SWIG Interface generator");
+    printf("\nVersion %s\n\n", version);
+
+    try 
+    {
+        string msg = "IMake - SWIG Interface generator";
+        TCLAP::CmdLine cmd(msg, ' ', version);
+
+        TCLAP::ValueArg<std::string> argInputFile("p", "param-file", "The path to the input parameter file", true, "Constants.xml", "string");
+        TCLAP::ValueArg<std::string> argLanguage("l", "language", "The language to generate for", true, "PHP|C#|Java", "string");
+        TCLAP::ValueArg<std::string> argOutput("o", "output", "The file or directory where generated files are output to", false, ".", "string");
+        TCLAP::ValueArg<std::string> argRelRoot("r", "rel-root", "Defines where headers will be resolved relative to", false, ".", "string");
+
+        cmd.add(argInputFile);
+        cmd.add(argLanguage);
+        cmd.add(argOutput);
+        cmd.add(argRelRoot);
+
+        TCLAP::SwitchArg argTranslateMode("t", "translate-mode", "Enable translate (generate constants) mode", cmd, false);
+        TCLAP::SwitchArg argAbsPaths("a", "absolute-paths", "Disable relative path resolution in headers. Header paths are assumed to be absolute", cmd, false);
+
+        cmd.parse(argc, argv);
+
+        string pFile;
+        string relRoot;
+        string outDir;
+        translateMode = false;
+        absPaths = false;
+        verbose = true;
+        language = unknown;
+
+        if (argTranslateMode.getValue())
+        {
+            translateMode = true;
+        }
+        if (argAbsPaths.getValue())
+        {
+            absPaths = true;
+        }
+        pFile = argInputFile.getValue();
+        string sLang = argLanguage.getValue();
+        if (sLang == "PHP")
+        {
+            language = php;
+        }
+        else if (sLang == "C#")
+        {
+            language = csharp;
+            rootObjectMethods["Equals"] = 1;
+            rootObjectMethods["GetHashCode"] = 1;
+            rootObjectMethods["GetType"] = 1;
+            rootObjectMethods["ReferenceEquals"] = 1;
+            rootObjectMethods["ToString"] = 1;
+        }
+        else if (sLang == "Java")
+        {
+            language = java;
+        }
+        outDir = argOutput.getValue();
+        relRoot = argRelRoot.getValue();
+
+        //Basic validation
+        if (language == unknown)
+        {
+            printf("ERROR: Invalid language or no language specified\n");
+            usage(cmd);
+        }
+        else
+        {
+            switch (language)
+            {
+                case csharp:
+                    printf("INFO: Language mode: C#\n");
+                    break;
+                case php:
+                    printf("INFO: Language mode: PHP\n");
+                    break;
+                case java:
+                    printf("INFO: Language mode: Java\n");
+                    break;
+            }
+        }
+
+        if (verbose)
+        {
+            printf("INFO: Verbose mode is ON\n");
+        }
+        else
+        {
+            printf("INFO: Verbose mode is OFF\n");
+        }
+
+        if (pFile.empty())
+        {
+            printf("ERROR: No parameter file specified\n");
+            usage(cmd);
+        }
+        else
+        {
+            printf("INFO: Parameter file: %s\n", pFile.c_str());
+        }
+
+        if (translateMode)
+        {
+            printf("INFO: Translate (generate constants) mode is ON\n");
+        }
+        else
+        {
+            printf("INFO: Translate (generate constants) mode is OFF. IMake will be generating the SWIG input file\n");
+        }
+
+        if (!outDir.empty())
+        {
+            printf("INFO: Auto-generated files will be output to: %s\n", outDir.c_str());
+        }
+        else
+        {
+            printf("INFO: Auto-generated files will be output to this directory\n");
+        }
+
+        if (absPaths)
+        {
+            printf("INFO: No relative path resolution will be performed on header paths. Header paths assumed to be absolute\n");
+        }
+        else
+        {
+            if (!relRoot.empty())
+            {
+                printf("INFO: Headers will be resolved relative to: %s\n", relRoot.c_str());
+            }
+            else
+            {
+                printf("INFO: Headers will be resolved relative to this directory\n");
+            }
+        }
+
+        if (translateMode)
+        {
+            if (!outDir.empty())
+                target += outDir;
+            else
+                target += ".";
+            if (verbose)
+                printf("INFO: Target is set to: %s\n", target.c_str());
+        }
+
+        createInterfaceFile(pFile.c_str(), outDir, relRoot);
+    }
+    catch (TCLAP::ArgException &e)  // catch any exceptions
+    {
+        std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
+    }
+    return 0;
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.sln
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.sln	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.sln	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,19 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Express 2012 for Windows Desktop
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IMake", "IMake.vcxproj", "{B601F04C-0D42-4AFC-A092-B31185E2EA8C}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug|Win32.ActiveCfg = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Debug|Win32.Build.0 = Debug|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release|Win32.ActiveCfg = Release|Win32
+		{B601F04C-0D42-4AFC-A092-B31185E2EA8C}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

Added: sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.vcxproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.vcxproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.vcxproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{B601F04C-0D42-4AFC-A092-B31185E2EA8C}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>11.0.61030.0</_ProjectFileVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>Win32\</OutDir>
+    <IntDir>Debug\</IntDir>
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <OutDir>Win32\</OutDir>
+    <IntDir>Release\</IntDir>
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <AdditionalIncludeDirectories>..\..\thirdparty\tclap-1.2.2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <OutputFile>$(OutDir)IMake.exe</OutputFile>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)IMake.pdb</ProgramDatabaseFile>
+      <SubSystem>Console</SubSystem>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention />
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <AdditionalIncludeDirectories>..\..\thirdparty\tclap-1.2.2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <OutputFile>$(OutDir)IMake.exe</OutputFile>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <OptimizeReferences>true</OptimizeReferences>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention />
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="IMake.cpp" />
+    <ClCompile Include="SimpleXmlParser.cpp" />
+    <ClCompile Include="stdafx.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="SimpleXmlParser.h" />
+    <ClInclude Include="stdafx.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.xsd
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.xsd	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/IMake/IMake.xsd	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+  <xs:element name="Parameters">
+    <xs:annotation>
+      <xs:documentation>Parameters for SWIG interface file generation</xs:documentation>
+    </xs:annotation>
+    <xs:complexType>
+      <xs:all>
+        <xs:element name="Module">
+          <xs:complexType>
+            <xs:attribute name="name" type="xs:string" use="required" />
+          </xs:complexType>
+        </xs:element>
+        <xs:element name="CustomFile">
+          <xs:complexType>
+            <xs:attribute name="path" type="xs:string" use="required" />
+          </xs:complexType>
+        </xs:element>
+        <xs:element name="Target">
+          <xs:complexType>
+            <xs:attribute name="path" type="xs:string" use="required" />
+          </xs:complexType>
+        </xs:element>
+        <xs:element name="DocTarget">
+          <xs:complexType>
+            <xs:attribute name="path" type="xs:string" use="required" />
+          </xs:complexType>
+        </xs:element>
+        <xs:element name="CppInline" minOccurs="0" />
+        <xs:element name="TypeReplacements" minOccurs="0">
+          <xs:complexType>
+            <xs:sequence>
+              <xs:element name="TypeReplacement" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                  <xs:attribute name="oldtype" type="xs:string" />
+                  <xs:attribute name="newtype" type="xs:string" />
+                </xs:complexType>
+              </xs:element>
+            </xs:sequence>
+          </xs:complexType>
+        </xs:element>
+        <xs:element name="Typedefs" minOccurs="0" />
+        <xs:element name="SwigInline" minOccurs="0" />
+        <xs:element name="Headers" minOccurs="0">
+          <xs:complexType>
+            <xs:sequence>
+              <xs:element name="Header" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                  <xs:attribute name="path" type="xs:string" />
+                </xs:complexType>
+              </xs:element>
+            </xs:sequence>
+          </xs:complexType>
+        </xs:element>
+      </xs:all>
+    </xs:complexType>
+  </xs:element>
+</xs:schema>

Added: sandbox/jng/vanilla_swig/Bindings/src/IMake/Makefile
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/IMake/Makefile	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/IMake/Makefile	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,130 @@
+#############################################################################
+# Makefile for building: Linux/IMake
+# Generated by qmake (1.05a) (Qt 3.1.2) on: Mon Dec  5 17:05:33 2005
+# Project:  IMake.pro
+# Template: app
+# Command: $(QMAKE) -o Makefile IMake.pro
+#############################################################################
+
+####### Compiler, tools and options
+
+CC       = gcc
+CXX      = g++
+LEX      = flex
+YACC     = yacc
+CFLAGS   = -pipe -UZTS -Wall -W -O2 -g -pipe -fno-use-cxa-atexit -fexceptions  -DNDEBUG
+CXXFLAGS = -pipe -UZTS -Wall -W -O2 -g -pipe -fno-use-cxa-atexit -fexceptions  -DNDEBUG
+LEXFLAGS = 
+YACCFLAGS= -d
+INCPATH  = -I$(QTDIR)/mkspecs/default -I.
+LINK     = g++
+LFLAGS   = 
+LIBS     = $(SUBLIBS)  
+AR       = ar cqs
+RANLIB   = 
+MOC      = $(QTDIR)/bin/moc
+UIC      = $(QTDIR)/bin/uic
+QMAKE    = qmake
+TAR      = tar -cf
+GZIP     = gzip -9f
+COPY     = cp -f
+COPY_FILE= $(COPY) -p
+COPY_DIR = $(COPY) -pR
+DEL_FILE = rm -f
+SYMLINK  = ln -sf
+DEL_DIR  = rmdir
+MOVE     = mv -f
+CHK_DIR_EXISTS= test -d
+MKDIR    = mkdir -p
+
+####### Output directory
+
+OBJECTS_DIR = Linux/obj/debug/
+
+####### Files
+
+HEADERS = SimpleXmlParser.h \
+		stdafx.h
+SOURCES = IMake.cpp \
+		SimpleXmlParser.cpp
+OBJECTS = Linux/obj/debug/IMake.o \
+		Linux/obj/debug/SimpleXmlParser.o
+FORMS = 
+UICDECLS = 
+UICIMPLS = 
+SRCMOC   = 
+OBJMOC = 
+DIST	   = IMake.pro
+QMAKE_TARGET = IMake
+DESTDIR  = Linux/
+TARGET   = Linux/IMake
+
+first: all
+####### Implicit rules
+
+.SUFFIXES: .c .cpp .cc .cxx .C
+
+.cpp.o:
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
+
+.cc.o:
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
+
+.cxx.o:
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
+
+.C.o:
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
+
+.c.o:
+	$(CC) -c $(CFLAGS) $(INCPATH) -o $@ $<
+
+####### Build rules
+
+all: $(TARGET)
+
+$(TARGET):  $(UICDECLS) $(OBJECTS) $(OBJMOC) 
+	test -d Linux/ || mkdir -p Linux/
+	$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJMOC) $(LIBS)
+
+mocables: $(SRCMOC)
+
+$(MOC): 
+	( cd $(QTDIR)/src/moc ; $(MAKE) )
+
+dist: 
+	@mkdir -p Linux/obj/debug/IMake && $(COPY_FILE) --parents $(SOURCES) $(HEADERS) $(FORMS) $(DIST) Linux/obj/debug/IMake/ && ( cd `dirname Linux/obj/debug/IMake` && $(TAR) IMake.tar IMake && $(GZIP) IMake.tar ) && $(MOVE) `dirname Linux/obj/debug/IMake`/IMake.tar.gz . && $(DEL_FILE) -r Linux/obj/debug/IMake
+
+uiclean:
+
+yaccclean:
+lexclean:
+clean:
+	-$(DEL_FILE) $(OBJECTS)
+	-$(DEL_FILE) *~ core *.core
+
+
+####### Sub-libraries
+
+distclean: clean
+	-$(DEL_FILE) Linux/$(TARGET) $(TARGET)
+
+
+FORCE:
+
+####### Compile
+
+Linux/obj/debug/IMake.o: IMake.cpp stdafx.h \
+		SimpleXmlParser.h
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o Linux/obj/debug/IMake.o IMake.cpp
+
+Linux/obj/debug/SimpleXmlParser.o: SimpleXmlParser.cpp stdafx.h \
+		SimpleXmlParser.h
+	$(CXX) -c $(CXXFLAGS) $(INCPATH) -o Linux/obj/debug/SimpleXmlParser.o SimpleXmlParser.cpp
+
+####### Install
+
+install: all 
+
+uninstall: 
+

Added: sandbox/jng/vanilla_swig/Bindings/src/IMake/SimpleXmlParser.cpp
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/IMake/SimpleXmlParser.cpp	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/IMake/SimpleXmlParser.cpp	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,1396 @@
+#include "stdafx.h"
+#include "SimpleXmlParser.h"
+
+#ifdef _T
+#undef _T
+#endif
+
+#define _T(x) x
+
+static const char chXMLTagOpen  = '<';
+static const char chXMLTagClose = '>';
+static const char chXMLTagPre   = '/';
+static const char chXMLEscape   = '\\';   // for value field escape
+
+static const char szXMLPIOpen[] = _T("<?");
+static const char szXMLPIClose[] = _T("?>");
+static const char szXMLCommentOpen[] = _T("<!--");
+static const char szXMLCommentClose[] = _T("-->");
+static const char szXMLCDATAOpen[] = _T("<![CDATA[");
+static const char szXMLCDATAClose[] = _T("]]>");
+
+static const XENTITY x_EntityTable[] = {
+        { '&', _T("&"), 5 } ,
+        { '\"', _T("""), 6 } ,
+        { '\'', _T("'"), 6 } ,
+        { '<', _T("<"), 4 } ,
+        { '>', _T(">"), 4 }
+    };
+
+PARSEINFO piDefault;
+DISP_OPT optDefault;
+XENTITYS entityDefault((LPXENTITY)x_EntityTable, sizeof(x_EntityTable)/sizeof(x_EntityTable[0]) );
+
+static XNodes::iterator emptyNodesIterator;
+static XAttrs::iterator emptyAttrsIterator;
+
+const char* _tcschrs( const char* psz, const char* pszchs )
+{
+    while( psz && *psz )
+    {
+        if( strchr( pszchs, *psz ) )
+            return (const char*)psz;
+        psz++;
+    }
+    return NULL;
+}
+
+const char* _tcsskip( const char* psz )
+{
+    //while( psz && *psz == ' ' && *psz == 13 && *psz == 10 ) psz++;
+    while( psz && isspace(*psz) ) psz++;
+
+    return (const char*)psz;
+}
+
+const char* _tcsechr( const char* psz, int ch, int escape )
+{
+    const char* pch = (const char*)psz;
+
+    while( pch && *pch )
+    {
+        if( escape != 0 && *pch == escape )
+            pch++;
+        else
+        if( *pch == ch )
+            return (const char*)pch;
+        pch++;
+    }
+    return pch;
+}
+
+int _tcselen( int escape, const char* srt, const char* end = NULL )
+{
+    int len = 0;
+    const char* pch = srt;
+    if( end==NULL ) end = (const char*)sizeof(long);
+    const char* prev_escape = NULL;
+    while( pch && *pch && pch<end )
+    {
+        if( escape != 0 && *pch == escape && prev_escape == NULL )
+            prev_escape = pch;
+        else
+        {
+            prev_escape = NULL;
+            len++;
+        }
+        pch++;
+    }
+    return len;
+}
+
+void _tcsecpy( char* psz, int escape, const char* srt, const char* end = NULL )
+{
+    const char* pch = srt;
+    if( end==NULL ) end = (const char*)sizeof(long);
+    const char* prev_escape = NULL;
+    while( pch && *pch && pch<end )
+    {
+        if( escape != 0 && *pch == escape && prev_escape == NULL )
+            prev_escape = pch;
+        else
+        {
+            prev_escape = NULL;
+            *psz++ = *pch;
+        }
+
+        pch++;
+    }
+
+    *psz = '\0';
+}
+
+const char* _tcsepbrk( const char* psz, const char* chset, int escape )
+{
+    const char* pch = (const char*)psz;
+    const char* prev_escape = NULL;
+    while( pch && *pch )
+    {
+        if( escape != 0 && *pch == escape && prev_escape == NULL )
+            prev_escape = pch;
+        else
+        {
+            prev_escape = NULL;
+            if( strchr( chset, *pch ) )
+                return (const char*)pch;
+        }
+        pch++;
+    }
+    return pch;
+}
+
+int _tcsenicmp( const char* psz, const char* str, int len, int escape )
+{
+    const char* pch = (const char*)psz;
+    const char* prev_escape = NULL;
+    const char* des = (const char*)str;
+    int i = 0;
+
+    while( pch && *pch && i < len )
+    {
+        if( escape != 0 && *pch == escape && prev_escape == NULL )
+            prev_escape = pch;
+        else
+        {
+            prev_escape = NULL;
+            if( tolower(*pch) != tolower(des[i]) )
+                break;
+            i++;
+        }
+        pch ++;
+    }
+
+    // find
+    if( i == len )
+        return 0;
+    if( psz[i] > des[i] )
+        return 1;
+    return -1;
+}
+
+const char* _tcsenistr( const char* psz, const char* str, int len, int escape )
+{
+    const char* pch = (const char*)psz;
+    const char* prev_escape = NULL;
+
+    while( pch && *pch )
+    {
+        if( escape != 0 && *pch == escape && prev_escape == NULL )
+            prev_escape = pch;
+        else
+        {
+            prev_escape = NULL;
+            if( _tcsenicmp( pch, str, len, escape ) == 0 )
+                return (const char*)pch;
+        }
+        pch++;
+    }
+    return pch;
+}
+
+const char* _tcseistr( const char* psz, const char* str, int escape )
+{
+    size_t len = strlen( str );
+    return _tcsenistr( psz, str, len, escape );
+}
+
+void _SetString( const char* psz, const char* end, string* ps, bool trim = false, int escape = 0 )
+{
+    //trim
+    if( trim )
+    {
+        while( psz && psz < end && isspace(*psz) ) psz++;
+        while( (end-1) && psz < (end-1) && isspace(*(end-1)) ) end--;
+    }
+    int len = end - psz;
+    if( len <= 0 ) return;
+    if( escape )
+    {
+        len = _tcselen( escape, psz, end );
+        char* pss = new char[len + 1];
+        _tcsecpy( pss, escape, psz, end );
+        *ps = pss;
+    }
+    else
+    {
+        char* pss = new char[len + 1];
+        memcpy( pss, psz, len );
+        pss[len] = '\0';
+        *ps = pss;
+    }
+}
+
+_tagXMLNode::~_tagXMLNode()
+{
+    Close();
+}
+
+void _tagXMLNode::Close()
+{
+    int i;
+    for(i = 0 ; i < (int)childs.size(); i ++)
+    {
+        LPXNode p = childs[i];
+        if( p )
+        {
+            delete p; childs[i] = NULL;
+        }
+    }
+    childs.clear();
+
+    for(i = 0 ; i < (int)attrs.size(); i ++)
+    {
+        LPXAttr p = attrs[i];
+        if( p )
+        {
+            delete p; attrs[i] = NULL;
+        }
+    }
+    attrs.clear();
+}
+
+const char* _tagXMLNode::LoadAttributes( const char* pszAttrs , LPPARSEINFO pi /*= &piDefault*/)
+{
+    const char* xml = (const char*)pszAttrs;
+
+    while( xml && *xml )
+    {
+        if((xml = _tcsskip( xml )) != NULL)
+        {
+            // close tag
+            if( *xml == chXMLTagClose || *xml == chXMLTagPre )
+                // wel-formed tag
+                return xml;
+
+            // XML Attr Name
+            const char* pEnd = strpbrk( xml, " =" );
+            if( pEnd == NULL )
+            {
+                // error
+                if( pi->erorr_occur == false )
+                {
+                    pi->erorr_occur = true;
+                    pi->error_pointer = xml;
+                    pi->error_code = PIE_ATTR_NO_VALUE;
+
+                    pi->error_string = name + " attribute has error ";
+                }
+                return NULL;
+            }
+
+            LPXAttr attr = new XAttr;
+            attr->parent = this;
+
+            // XML Attr Name
+            _SetString( xml, pEnd, &attr->name );
+
+            // add new attribute
+            attrs.push_back( attr );
+            xml = pEnd;
+
+            // XML Attr Value
+            if((xml = _tcsskip( xml )) != NULL)
+            {
+                //if( xml = strchr( xml, '=' ) )
+                if( *xml == '=' )
+                {
+                    if((xml = _tcsskip(++xml)) != NULL)
+                    {
+                        // if " or '
+                        // or none quote
+                        int quote = *xml;
+                        if( quote == '"' || quote == '\'' )
+                            pEnd = _tcsechr( ++xml, quote, chXMLEscape );
+                        else
+                        {
+                            //attr= value>
+                            // none quote mode
+                            //pEnd = _tcsechr( xml, ' ', '\\' );
+                            pEnd = _tcsepbrk( xml, _T(" >"), chXMLEscape );
+                        }
+
+                        bool trim = pi->trim_value;
+                        char escape = pi->escape_value;
+                        //_SetString( xml, pEnd, &attr->value, trim, chXMLEscape );
+                        _SetString( xml, pEnd, &attr->value, trim, escape );
+                        xml = pEnd;
+                        // ATTRVALUE
+                        if( pi->entity_value && pi->entitys )
+                            attr->value = pi->entitys->Ref2Entity(attr->value.c_str());
+
+                        if( quote == '"' || quote == '\'' )
+                            xml++;
+                    }
+                }
+            }
+        }
+    }
+
+    // not wel-formed tag
+    return NULL;
+}
+
+const char* _tagXMLNode::LoadAttributes( const char* pszAttrs, const char* pszEnd, LPPARSEINFO pi /*= &piDefault*/ )
+{
+    const char* xml = (const char*)pszAttrs;
+
+    while( xml && *xml )
+    {
+        if((xml = _tcsskip(xml)) != NULL)
+        {
+            // close tag
+            if( xml >= pszEnd )
+                // wel-formed tag
+                return xml;
+
+            // XML Attr Name
+            const char* pEnd = strpbrk( xml, " =" );
+            if( pEnd == NULL )
+            {
+                // error
+                if( pi->erorr_occur == false )
+                {
+                    pi->erorr_occur = true;
+                    pi->error_pointer = xml;
+                    pi->error_code = PIE_ATTR_NO_VALUE;
+                    pi->error_string = name + " attribute has error ";
+                }
+                return NULL;
+            }
+
+            LPXAttr attr = new XAttr;
+            attr->parent = this;
+
+            // XML Attr Name
+            _SetString( xml, pEnd, &attr->name );
+
+            // add new attribute
+            attrs.push_back( attr );
+            xml = pEnd;
+
+            // XML Attr Value
+            if((xml = _tcsskip(xml)) != NULL)
+            {
+                //if( xml = strchr( xml, '=' ) )
+                if( *xml == '=' )
+                {
+                    if((xml = _tcsskip( ++xml )) != NULL)
+                    {
+                        // if " or '
+                        // or none quote
+                        int quote = *xml;
+                        if( quote == '"' || quote == '\'' )
+                            pEnd = _tcsechr( ++xml, quote, chXMLEscape );
+                        else
+                        {
+                            //attr= value>
+                            // none quote mode
+                            //pEnd = _tcsechr( xml, ' ', '\\' );
+                            pEnd = _tcsepbrk( xml, _T(" >"), chXMLEscape );
+                        }
+
+                        bool trim = pi->trim_value;
+                        char escape = pi->escape_value;
+                        //_SetString( xml, pEnd, &attr->value, trim, chXMLEscape );
+                        _SetString( xml, pEnd, &attr->value, trim, escape );
+                        xml = pEnd;
+                        // ATTRVALUE
+                        if( pi->entity_value && pi->entitys )
+                            attr->value = pi->entitys->Ref2Entity(attr->value.c_str());
+
+                        if( quote == '"' || quote == '\'' )
+                            xml++;
+                    }
+                }
+            }
+        }
+    }
+
+    // not wel-formed tag
+    return NULL;
+}
+
+const char* _tagXMLNode::LoadProcessingInstrunction( const char* pszXml, LPPARSEINFO pi /*= &piDefault*/ )
+{
+    // find the end of pi
+    const char* end = _tcsenistr( pszXml, szXMLPIClose, sizeof(szXMLPIClose)-1, pi ? pi->escape_value : 0 );
+    if( end == NULL )
+        return NULL;
+
+    // process pi
+    if( doc )
+    {
+        const char* xml = (const char*)pszXml;
+
+        LPXNode node = new XNode;
+        node->parent = this;
+        node->doc = doc;
+        node->type = XNODE_PI;
+
+        xml += sizeof(szXMLPIOpen)-1;
+        const char* pTagEnd = strpbrk( xml, " ?>" );
+        _SetString( xml, pTagEnd, &node->name );
+        xml = pTagEnd;
+
+        node->LoadAttributes( xml, end, pi );
+
+        doc->childs.push_back( node );
+    }
+
+    end += sizeof(szXMLPIClose)-1;
+    return end;
+}
+
+const char* _tagXMLNode::LoadComment( const char* pszXml, LPPARSEINFO pi /*= &piDefault*/ )
+{
+    // find the end of comment
+    const char* end = _tcsenistr( pszXml, szXMLCommentClose, sizeof(szXMLCommentClose)-1, pi ? pi->escape_value : 0 );
+    if( end == NULL )
+        return NULL;
+
+    // process comment
+    LPXNode par = parent;
+    if( parent == NULL && doc )
+        par = (LPXNode)&doc;
+    if( par )
+    {
+        const char* xml = (const char*)pszXml;
+        xml += sizeof(szXMLCommentOpen)-1;
+
+        LPXNode node = new XNode;
+        node->parent = this;
+        node->doc = doc;
+        node->type = XNODE_COMMENT;
+        node->name = _T("#COMMENT");
+        _SetString( xml, end, &node->value, false );
+
+        par->childs.push_back( node );
+    }
+
+    end += sizeof(szXMLCommentClose)-1;
+    return end;
+}
+
+const char* _tagXMLNode::LoadCDATA( const char* pszXml, LPPARSEINFO pi /*= &piDefault*/ )
+{
+    // find the end of CDATA
+    const char* end = _tcsenistr( pszXml, szXMLCDATAClose, sizeof(szXMLCDATAClose)-1, pi ? pi->escape_value : 0 );
+    if( end == NULL )
+        return NULL;
+
+    // process CDATA
+    LPXNode par = parent;
+    if( parent == NULL && doc )
+        par = (LPXNode)&doc;
+    if( par )
+    {
+        const char* xml = (const char*)pszXml;
+        xml += sizeof(szXMLCDATAOpen)-1;
+
+        LPXNode node = new XNode;
+        node->parent = this;
+        node->doc = doc;
+        node->type = XNODE_CDATA;
+        node->name = _T("#CDATA");
+        _SetString( xml, end, &node->value, false );
+
+        par->childs.push_back( node );
+    }
+
+    end += sizeof(szXMLCDATAClose)-1;
+    return end;
+}
+
+const char* LoadOtherNodes( LPXNode node, bool* pbRet, const char* pszXml, LPPARSEINFO pi /*= &piDefault*/ )
+{
+    const char* xml = (const char*)pszXml;
+    bool do_other_type = true;
+    *pbRet = false;
+
+    while( xml && do_other_type )
+    {
+        do_other_type = false;
+
+        xml = _tcsskip( xml );
+        const char* prev = xml;
+        // is PI( Processing Instruction ) Node?
+        if( strncmp( xml, szXMLPIOpen, sizeof(szXMLPIOpen)-1 ) == 0 )
+        {
+            // processing instrunction parse
+            // return pointer is next node of pi
+            xml = node->LoadProcessingInstrunction( xml, pi );
+            //if( xml == NULL )
+            //    return NULL;
+            // restart xml parse
+        }
+
+        if( xml != prev )
+            do_other_type = true;
+        xml = _tcsskip( xml );
+        prev = xml;
+
+        // is comment Node?
+        if( strncmp( xml, szXMLCommentOpen, sizeof(szXMLCommentOpen)-1 ) == 0 )
+        {
+            // processing comment parse
+            // return pointer is next node of comment
+            xml = node->LoadComment( xml, pi );
+            // comment node is terminal node
+            if( node->parent && node->parent->type != XNODE_DOC
+                && xml != prev )
+            {
+                *pbRet = true;
+                return xml;
+            }
+            // restart xml parse when this node is root doc node
+        }
+
+        if( xml != prev )
+            do_other_type = true;
+
+        xml = _tcsskip( xml );
+        prev = xml;
+        // is CDATA Node?
+        if( strncmp( xml, szXMLCDATAOpen, sizeof(szXMLCDATAOpen)-1 ) == 0 )
+        {
+            // processing CDATA parse
+            // return pointer is next node of CDATA
+            xml = node->LoadCDATA( xml, pi );
+            // CDATA node is terminal node
+            if( node->parent && node->parent->type != XNODE_DOC
+                && xml != prev )
+            {
+                *pbRet = true;
+                return xml;
+            }
+            // restart xml parse when this node is root doc node
+        }
+
+        if( xml != prev )
+            do_other_type = true;
+    }
+
+    return xml;
+}
+
+const char* _tagXMLNode::Load( const char* pszXml, LPPARSEINFO pi /*= &piDefault*/ )
+{
+    // Close it
+    Close();
+
+    const char* xml = (const char*)pszXml;
+
+    xml = strchr( xml, chXMLTagOpen );
+    if( xml == NULL )
+        return NULL;
+
+    // Close Tag
+    if( *(xml+1) == chXMLTagPre ) // </Close
+        return xml;
+
+    // Load Other Node before <Tag>(pi, comment, CDATA etc)
+    bool bRet = false;
+    const char* ret = NULL;
+    ret = LoadOtherNodes( this, &bRet, xml, pi );
+    if( ret != NULL )
+        xml = ret;
+    if( bRet )
+        return xml;
+
+    // XML Node Tag Name Open
+    xml++;
+    const char* pTagEnd = strpbrk( xml, " />\t\r\n" );
+    _SetString( xml, pTagEnd, &name );
+    xml = pTagEnd;
+    // Generate XML Attributte List
+    if((xml = LoadAttributes( xml, pi )) != NULL)
+    {
+        // alone tag <TAG ... />
+        if( *xml == chXMLTagPre )
+        {
+            xml++;
+            if( *xml == chXMLTagClose )
+                // wel-formed tag
+                return ++xml;
+            else
+            {
+                // error: <TAG ... / >
+                if( pi->erorr_occur == false )
+                {
+                    pi->erorr_occur = true;
+                    pi->error_pointer = xml;
+                    pi->error_code = PIE_ALONE_NOT_CLOSED;
+                    pi->error_string = _T("Element must be closed.");
+                }
+                // not wel-formed tag
+                return NULL;
+            }
+        }
+        else
+        // open/close tag <TAG ..> ... </TAG>
+        //                        ^- current pointer
+        {
+            // if text value is not exist, then assign value
+            //if( this->value.IsEmpty() || this->value == _T("") )
+            if( XIsEmptyString( value.c_str() ) )
+            {
+                // Text Value
+                const char* pEnd = _tcsechr( ++xml, chXMLTagOpen, chXMLEscape );
+                if( pEnd == NULL )
+                {
+                    if( pi->erorr_occur == false )
+                    {
+                        pi->erorr_occur = true;
+                        pi->error_pointer = xml;
+                        pi->error_code = PIE_NOT_CLOSED;
+                        pi->error_string = name + " must be closed with </" + name + ">";
+                    }
+                    // error cos not exist CloseTag </TAG>
+                    return NULL;
+                }
+
+                bool trim = pi->trim_value;
+                char escape = pi->escape_value;
+                //_SetString( xml, pEnd, &value, trim, chXMLEscape );
+                _SetString( xml, pEnd, &value, trim, escape );
+
+                xml = pEnd;
+                // TEXTVALUE reference
+                if( pi->entity_value && pi->entitys )
+                    value = pi->entitys->Ref2Entity(value.c_str());
+            }
+
+            // generate child nodes
+            while( xml && *xml )
+            {
+                LPXNode node = new XNode;
+                node->parent = this;
+                node->doc = doc;
+                node->type = type;
+
+                xml = node->Load( xml,pi );
+                if( XIsEmptyString(node->name.c_str()) == false )
+                {
+                    childs.push_back( node );
+
+                }
+                else
+                {
+                    delete node;
+                }
+
+                // open/close tag <TAG ..> ... </TAG>
+                //                             ^- current pointer
+                // CloseTag case
+                if( xml && *xml && *(xml+1) && *xml == chXMLTagOpen && *(xml+1) == chXMLTagPre )
+                {
+                    // </Close>
+                    xml+=2; // C
+
+                    if((xml = _tcsskip( xml )) != NULL)
+                    {
+                        string closename;
+                        const char* pEnd = strpbrk( xml, " >" );
+                        if( pEnd == NULL )
+                        {
+                            if( pi->erorr_occur == false )
+                            {
+                                pi->erorr_occur = true;
+                                pi->error_pointer = xml;
+                                pi->error_code = PIE_NOT_CLOSED;
+                                pi->error_string = "it must be closed with " + name;
+                            }
+                            // error
+                            return NULL;
+                        }
+                        _SetString( xml, pEnd, &closename );
+                        if( closename == this->name )
+                        {
+                            // wel-formed open/close
+                            xml = pEnd+1;
+                            // return '>' or ' ' after pointer
+                            return xml;
+                        }
+                        else
+                        {
+                            xml = pEnd+1;
+                            // 2004.6.15 - example <B> alone tag
+                            // now it can parse with attribute 'force_arse'
+                            if( pi->force_parse == false )
+                            {
+                                // not welformed open/close
+                                if( pi->erorr_occur == false )
+                                {
+                                    pi->erorr_occur = true;
+                                    pi->error_pointer = xml;
+                                    pi->error_code = PIE_NOT_NESTED;
+                                    //pi->error_string.Format(_T("'<%s> ... </%s>' is not wel-formed."), name, closename );
+                                }
+                                return NULL;
+                            }
+                        }
+                    }
+                }
+                else    // Alone child Tag Loaded
+                        // else ÇؾßÇÏ´ÂÁö ¸»¾Æ¾ßÇÏ´ÂÁö Àǽɰ£´Ù.
+                {
+
+                    //if( xml && this->value.IsEmpty() && *xml !=chXMLTagOpen )
+                    if( xml && XIsEmptyString( value.c_str() ) && *xml !=chXMLTagOpen )
+                    {
+                        // Text Value
+                        const char* pEnd = _tcsechr( xml, chXMLTagOpen, chXMLEscape );
+                        if( pEnd == NULL )
+                        {
+                            // error cos not exist CloseTag </TAG>
+                            if( pi->erorr_occur == false )
+                            {
+                                pi->erorr_occur = true;
+                                pi->error_pointer = xml;
+                                pi->error_code = PIE_NOT_CLOSED;
+                                pi->error_string = "it must be closed with </" + name;
+                            }
+                            return NULL;
+                        }
+
+                        bool trim = pi->trim_value;
+                        char escape = pi->escape_value;
+                        //_SetString( xml, pEnd, &value, trim, chXMLEscape );
+                        _SetString( xml, pEnd, &value, trim, escape );
+
+                        xml = pEnd;
+                        //TEXTVALUE
+                        if( pi->entity_value && pi->entitys )
+                            value = pi->entitys->Ref2Entity(value.c_str());
+                    }
+                }
+            }
+        }
+    }
+
+    return xml;
+}
+
+const char* _tagXMLDocument::Load( const char* pszXml, LPPARSEINFO pi /*= NULL*/ )
+{
+    LPXNode node = new XNode;
+    node->parent = (LPXNode)this;
+    node->type = XNODE_ELEMENT;
+    node->doc = this;
+    const char* end;
+
+    if( pi == NULL )
+        pi = &parse_info;
+
+    if( (end = node->Load( pszXml, pi )) == NULL )
+    {
+        delete node;
+        return NULL;
+    }
+
+    childs.push_back( node );
+
+    // Load Other Node after </Tag>(pi, comment, CDATA etc)
+    const char* ret;
+    bool bRet = false;
+    ret = LoadOtherNodes( node, &bRet, end, pi );
+    if( ret != NULL )
+        end = ret;
+
+    return end;
+}
+
+LPXNode _tagXMLDocument::GetRoot()
+{
+    XNodes::iterator it = childs.begin();
+    for( ; it != childs.end() ; ++(it) )
+    {
+        LPXNode node = *it;
+        if( node->type == XNODE_ELEMENT )
+            return node;
+    }
+    return NULL;
+}
+
+string _tagXMLAttr::GetXML( LPDISP_OPT opt /*= &optDefault*/ )
+{
+    std::ostringstream os;
+    //os << (const char*)name << "='" << (const char*)value << "' ";
+
+    os << name.c_str() << "=" << (char)opt->value_quotation_mark
+        << (opt->reference_value&&opt->entitys?opt->entitys->Entity2Ref(value.c_str()):value).c_str()
+        << (char)opt->value_quotation_mark << " ";
+    return os.str().c_str();
+}
+
+string _tagXMLNode::GetXML( LPDISP_OPT opt /*= &optDefault*/ )
+{
+    std::ostringstream os;
+
+    // tab
+    if( opt && opt->newline )
+    {
+        if( opt && opt->newline )
+            os << "\r\n";
+        for( int i = 0 ; i < opt->tab_base ; i++)
+            os << '\t';
+    }
+
+    if( type == XNODE_DOC )
+    {
+        for( int i = 0 ; i < (int)childs.size(); i++ )
+            os << childs[i]->GetXML( opt ).c_str();
+        return os.str().c_str();
+    }
+    else
+    if( type == XNODE_PI )
+    {
+        // <?TAG
+        os << szXMLPIOpen << name.c_str();
+        // <?TAG Attr1="Val1"
+        if( attrs.empty() == false ) os << ' ';
+        for( int i = 0 ; i < (int)attrs.size(); i++ )
+        {
+            os << (const char*)attrs[i]->GetXML(opt).c_str();
+        }
+        //?>
+        os << szXMLPIClose;
+        return os.str().c_str();
+    }
+    else
+    if( type == XNODE_COMMENT )
+    {
+        // <--comment
+        os << szXMLCommentOpen << value.c_str();
+        //-->
+        os << szXMLCommentClose;
+        return os.str().c_str();
+    }
+    else
+    if( type == XNODE_CDATA )
+    {
+        // <--comment
+        os << szXMLCDATAOpen << value.c_str();
+        //-->
+        os << szXMLCDATAClose;
+        return os.str().c_str();
+    }
+
+    // <TAG
+    os << '<' << name.c_str();
+
+    // <TAG Attr1="Val1"
+    if( attrs.empty() == false ) os << ' ';
+    for( int i = 0 ; i < (int)attrs.size(); i++ )
+    {
+        os << attrs[i]->GetXML(opt).c_str();
+    }
+
+    if( childs.empty() && XIsEmptyString(value.c_str()) )
+    {
+        // <TAG Attr1="Val1"/> alone tag
+        os << "/>";
+    }
+    else
+    {
+        // <TAG Attr1="Val1"> and get child
+        os << '>';
+        if( opt && opt->newline && !childs.empty() )
+        {
+            opt->tab_base++;
+        }
+
+        for( int i = 0 ; i < (int)childs.size(); i++ )
+            os << childs[i]->GetXML( opt ).c_str();
+
+        // Text Value
+        if( value != _T("") )
+        {
+            if( opt && opt->newline && !childs.empty() )
+            {
+                if( opt && opt->newline )
+                    os << "\r\n";
+                for( int i = 0 ; i < opt->tab_base ; i++)
+                    os << '\t';
+            }
+            os << (opt->reference_value&&opt->entitys?opt->entitys->Entity2Ref(value.c_str()).c_str():value.c_str());
+        }
+
+        // </TAG> CloseTag
+        if( opt && opt->newline && !childs.empty() )
+        {
+            os << "\r\n";
+            for( int i = 0 ; i < opt->tab_base-1 ; i++)
+                os << '\t';
+        }
+        os << "</" << name.c_str() << '>';
+
+        if( opt && opt->newline )
+        {
+            if( !childs.empty() )
+                opt->tab_base--;
+        }
+    }
+
+    return os.str().c_str();
+}
+
+string _tagXMLNode::GetText( LPDISP_OPT opt /*= &optDefault*/ )
+{
+    std::ostringstream os;
+
+    if( type == XNODE_DOC )
+    {
+        for( int i = 0 ; i < (int)childs.size(); i++ )
+            os << (const char*)childs[i]->GetText( opt ).c_str();
+    }
+    else
+    if( type == XNODE_PI )
+    {
+        // no text
+    }
+    else
+    if( type == XNODE_COMMENT )
+    {
+        // no text
+    }
+    else
+    if( type == XNODE_CDATA )
+    {
+        os << (const char*)value.c_str();
+    }
+    else
+    if( type == XNODE_ELEMENT )
+    {
+        if( childs.empty() && XIsEmptyString(value.c_str()) )
+        {
+            // no text
+        }
+        else
+        {
+            // childs text
+            for( int i = 0 ; i < (int)childs.size(); i++ )
+                os << (const char*)childs[i]->GetText().c_str();
+
+            // Text Value
+            os << (opt->reference_value&&opt->entitys?opt->entitys->Entity2Ref(value.c_str()).c_str():value.c_str());
+        }
+    }
+
+    return os.str().c_str();
+}
+
+LPXAttr _tagXMLNode::GetAttr( const char* attrname )
+{
+    for( int i = 0 ; i < (int)attrs.size(); i++ )
+    {
+        LPXAttr attr = attrs[i];
+        if( attr )
+        {
+            if( attr->name == attrname )
+                return attr;
+        }
+    }
+    return NULL;
+}
+
+XAttrs _tagXMLNode::GetAttrs( const char* name )
+{
+    XAttrs attrs;
+    for( int i = 0 ; i < (int)attrs.size(); i++ )
+    {
+        LPXAttr attr = attrs[i];
+        if( attr )
+        {
+            if( attr->name == name )
+                attrs.push_back( attr );
+        }
+    }
+    return attrs;
+}
+
+const char* _tagXMLNode::GetAttrValue( const char* attrname )
+{
+    LPXAttr attr = GetAttr( attrname );
+    return attr ? (const char*)attr->value.c_str() : NULL;
+}
+
+XNodes _tagXMLNode::GetChilds()
+{
+    return childs;
+}
+
+XNodes _tagXMLNode::GetChilds( const char* name )
+{
+    XNodes nodes;
+    for( int i = 0 ; i < (int)childs.size(); i++ )
+    {
+        LPXNode node = childs[i];
+        if( node )
+        {
+            if( node->name == name )
+                nodes.push_back( node );
+        }
+    }
+    return nodes;
+}
+
+LPXNode _tagXMLNode::GetChild( int i )
+{
+    if( i >= 0 && i < (int)childs.size() )
+        return childs[i];
+    return NULL;
+}
+
+int _tagXMLNode::GetChildCount()
+{
+    return childs.size();
+}
+
+LPXNode _tagXMLNode::GetChild( const char* name )
+{
+    for( int i = 0 ; i < (int)childs.size(); i++ )
+    {
+        LPXNode node = childs[i];
+        if( node )
+        {
+            if( node->name == name )
+                return node;
+        }
+    }
+    return NULL;
+}
+
+const char* _tagXMLNode::GetChildValue( const char* name )
+{
+    LPXNode node = GetChild( name );
+    return (node != NULL)? (const char*)node->value.c_str() : NULL;
+}
+
+string _tagXMLNode::GetChildText( const char* name, LPDISP_OPT opt /*= &optDefault*/ )
+{
+    LPXNode node = GetChild( name );
+    return (node != NULL)? node->GetText(opt) : _T("");
+}
+
+LPXAttr _tagXMLNode::GetChildAttr( const char* name, const char* attrname )
+{
+    LPXNode node = GetChild(name);
+    return node ? node->GetAttr(attrname) : NULL;
+}
+
+const char* _tagXMLNode::GetChildAttrValue( const char* name, const char* attrname )
+{
+    LPXAttr attr = GetChildAttr( name, attrname );
+    return attr ? (const char*)attr->value.c_str() : NULL;
+}
+
+LPXNode _tagXMLNode::Find( const char* name )
+{
+    XNodes::iterator it = childs.begin();
+    for( ; it != childs.end(); ++(it))
+    {
+        LPXNode child = *it;
+        if( child->name == name )
+            return child;
+
+        XNodes::iterator it = child->childs.begin();
+        for( ; it != child->childs.end(); ++(it))
+        {
+            LPXNode find = child->Find( name );
+            if( find != NULL )
+                return find;
+        }
+    }
+
+    return NULL;
+}
+
+XNodes::iterator _tagXMLNode::GetChildIterator( LPXNode node )
+{
+    XNodes::iterator it = childs.begin();
+    for( ; it != childs.end() ; ++(it) )
+    {
+        if( *it == node )
+            return it;
+    }
+    return emptyNodesIterator;
+}
+
+LPXNode _tagXMLNode::AppendChild( const char* name /*= NULL*/, const char* value /*= NULL*/ )
+{
+    return AppendChild( CreateNode( name, value ) );
+}
+
+LPXNode _tagXMLNode::AppendChild( LPXNode node )
+{
+    node->parent = this;
+    node->doc = doc;
+    childs.push_back( node );
+    return node;
+}
+
+bool _tagXMLNode::RemoveChild( LPXNode node )
+{
+    XNodes::iterator it = GetChildIterator( node );
+    if(*it != NULL)
+    {
+        delete *it;
+        childs.erase( it );
+        return true;
+    }
+    return false;
+}
+
+LPXAttr _tagXMLNode::GetAttr( int i )
+{
+    if( i >= 0 && i < (int)attrs.size() )
+        return attrs[i];
+    return NULL;
+}
+
+XAttrs::iterator _tagXMLNode::GetAttrIterator( LPXAttr attr )
+{
+    XAttrs::iterator it = attrs.begin();
+    for( ; it != attrs.end() ; ++(it) )
+    {
+        if( *it == attr )
+            return it;
+    }
+    return emptyAttrsIterator;
+}
+
+LPXAttr _tagXMLNode::AppendAttr( LPXAttr attr )
+{
+    attr->parent = this;
+    attrs.push_back( attr );
+    return attr;
+}
+
+bool _tagXMLNode::RemoveAttr( LPXAttr attr )
+{
+    XAttrs::iterator it = GetAttrIterator( attr );
+    if(*it != NULL)
+    {
+        delete *it;
+        attrs.erase( it );
+        return true;
+    }
+    return false;
+}
+
+LPXNode _tagXMLNode::CreateNode( const char* name /*= NULL*/, const char* value /*= NULL*/ )
+{
+    LPXNode node = new XNode;
+    node->name = name;
+    node->value = value;
+    return node;
+}
+
+LPXAttr _tagXMLNode::CreateAttr( const char* name /*= NULL*/, const char* value /*= NULL*/ )
+{
+    LPXAttr attr = new XAttr;
+    attr->name = name;
+    attr->value = value;
+    return attr;
+}
+
+LPXAttr _tagXMLNode::AppendAttr( const char* name /*= NULL*/, const char* value /*= NULL*/ )
+{
+    return AppendAttr( CreateAttr( name, value ) );
+}
+
+LPXNode _tagXMLNode::DetachChild( LPXNode node )
+{
+    XNodes::iterator it = GetChildIterator( node );
+    if(*it != NULL)
+    {
+        childs.erase( it );
+        return node;
+    }
+    return NULL;
+}
+
+LPXAttr _tagXMLNode::DetachAttr( LPXAttr attr )
+{
+    XAttrs::iterator it = GetAttrIterator( attr );
+    if(*it != NULL)
+    {
+        attrs.erase( it );
+        return attr;
+    }
+    return NULL;
+}
+
+void _tagXMLNode::CopyNode( LPXNode node )
+{
+    Close();
+
+    doc = node->doc;
+    parent = node->parent;
+    name = node->name;
+    value = node->value;
+    type = node->type;
+
+    // copy attributes
+    for( int i = 0 ; i < (int)node->attrs.size(); i++)
+    {
+        LPXAttr attr = node->attrs[i];
+        if( attr )
+            AppendAttr( attr->name.c_str(), attr->value.c_str() );
+    }
+}
+
+void _tagXMLNode::_CopyBranch( LPXNode node )
+{
+    CopyNode( node );
+
+    for( int i = 0 ; i < (int)node->childs.size(); i++)
+    {
+        LPXNode child = node->childs[i];
+        if( child )
+        {
+            LPXNode mychild = new XNode;
+            mychild->CopyNode( child );
+            AppendChild( mychild );
+
+            mychild->_CopyBranch( child );
+        }
+    }
+}
+
+LPXNode _tagXMLNode::AppendChildBranch( LPXNode node )
+{
+    LPXNode child = new XNode;
+    child->CopyBranch( node );
+
+    return AppendChild( child );
+}
+
+void _tagXMLNode::CopyBranch( LPXNode branch )
+{
+    Close();
+
+    _CopyBranch( branch );
+}
+
+
+_tagXMLEntitys::_tagXMLEntitys( LPXENTITY entities, int count )
+{
+    for( int i = 0; i < count; i++)
+        push_back( entities[i] );
+}
+
+LPXENTITY _tagXMLEntitys::GetEntity( int entity )
+{
+    for( int i = 0 ; i < (int)size(); i ++ )
+    {
+        if( at(i).entity == entity )
+            return LPXENTITY(&at(i));
+    }
+    return NULL;
+}
+
+LPXENTITY _tagXMLEntitys::GetEntity( const char* entity )
+{
+    for( int i = 0 ; i < (int)size(); i ++ )
+    {
+        const char* ref = (const char*)at(i).ref;
+        const char* ps = entity;
+        while( ref && *ref )
+            if( *ref++ != *ps++ )
+                break;
+        if( ref && !*ref )  // found!
+            return LPXENTITY(&at(i));
+    }
+    return NULL;
+}
+
+int _tagXMLEntitys::GetEntityCount( const char* str )
+{
+    int nCount = 0;
+    const char* ps = (const char*)str;
+    while( ps && *ps )
+        if( GetEntity( *ps++ ) ) nCount ++;
+    return nCount;
+}
+
+int _tagXMLEntitys::Ref2Entity( const char* estr, const char* str, int strlen )
+{
+    const char* pes = (const char*)estr;
+    char* ps = (char*)str;
+    const char* ps_end = ps+strlen;
+    while( pes && *pes && ps < ps_end )
+    {
+        LPXENTITY ent = GetEntity( pes );
+        if( ent )
+        {
+            // copy entity meanning char
+            *ps = ent->entity;
+            pes += ent->ref_len;
+        }
+        else
+            *ps = *pes++;   // default character copy
+        ps++;
+    }
+    *ps = '\0';
+
+    // total copied characters
+    return ps-str;
+}
+
+int _tagXMLEntitys::Entity2Ref( const char* str, const char* estr, int estrlen )
+{
+    const char* ps = (const char*)str;
+    char* pes = (char*)estr;
+    const char* pes_end = pes+estrlen;
+    while( ps && *ps && pes < pes_end )
+    {
+        LPXENTITY ent = GetEntity( *ps );
+        if( ent )
+        {
+            // copy entity string
+            const char* ref = (const char*)ent->ref;
+            while( ref && *ref )
+                *pes++ = *ref++;
+        }
+        else
+            *pes++ = *ps;   // default character copy
+        ps++;
+    }
+    *pes = '\0';
+
+    // total copied characters
+    return pes-estr;
+}
+
+string _tagXMLEntitys::Ref2Entity( const char* estr )
+{
+    string es;
+    if( estr )
+    {
+        int len = strlen(estr);
+        char* esbuf = new char[len+1];
+        if( esbuf )
+            Ref2Entity( estr, esbuf, len );
+        es = esbuf;
+    }
+    return es;
+}
+
+string _tagXMLEntitys::Entity2Ref( const char* str )
+{
+    string s;
+    if( str )
+    {
+        int nEntityCount = GetEntityCount(str);
+        if( nEntityCount == 0 )
+            return string(str);
+        int len = strlen(str) + nEntityCount*10 ;
+        const char* sbuf = new char[len+1];
+        if( sbuf )
+            Entity2Ref( str, sbuf, len );
+        s = sbuf;
+    }
+    return s;
+}
+
+string XRef2Entity( const char* estr )
+{
+    return entityDefault.Ref2Entity( estr );
+}
+
+string XEntity2Ref( const char* str )
+{
+    return entityDefault.Entity2Ref( str );
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/IMake/SimpleXmlParser.h
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/IMake/SimpleXmlParser.h	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/IMake/SimpleXmlParser.h	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,227 @@
+#ifndef _SIMPLE_XML_PARSER_H_
+#define _SIMPLE_XML_PARSER_H_
+
+using namespace std;
+
+struct _tagXMLAttr;
+typedef _tagXMLAttr XAttr, *LPXAttr;
+typedef std::vector<LPXAttr> XAttrs;
+
+struct _tagXMLNode;
+typedef _tagXMLNode XNode, *LPXNode;
+typedef std::vector<LPXNode> XNodes, *LPXNodes;
+
+struct _tagXMLDocument;
+typedef struct _tagXMLDocument XDoc, *LPXDoc;
+
+// Entity Encode/Decode Support
+typedef struct _tagXmlEntity
+{
+    char entity;                    // entity ( & " ' < > )
+    char ref[10];                   // entity reference ( & " etc )
+    int ref_len;                    // entity reference length
+}XENTITY,*LPXENTITY;
+
+typedef struct _tagXMLEntitys : public std::vector<XENTITY>
+{
+    LPXENTITY GetEntity( int entity );
+    LPXENTITY GetEntity( const char* entity );
+    int GetEntityCount( const char* str );
+    int Ref2Entity( const char* estr, const char* str, int strlen );
+    int Entity2Ref( const char* str, const char* estr, int estrlen );
+    string Ref2Entity( const char* estr );
+    string Entity2Ref( const char* str );
+
+    _tagXMLEntitys(){};
+    _tagXMLEntitys( LPXENTITY entities, int count );
+}XENTITYS,*LPXENTITYS;
+extern XENTITYS entityDefault;
+string XRef2Entity( const char* estr );
+string XEntity2Ref( const char* str );
+
+typedef enum
+{
+    PIE_PARSE_WELFORMED = 0,
+    PIE_ALONE_NOT_CLOSED,
+    PIE_NOT_CLOSED,
+    PIE_NOT_NESTED,
+    PIE_ATTR_NO_VALUE
+}PCODE;
+
+// Parse info.
+typedef struct _tagParseInfo
+{
+    bool        trim_value;         // [set] do trim when parse?
+    bool        entity_value;       // [set] do convert from reference to entity? ( < -> < )
+    LPXENTITYS  entitys;            // [set] entity table for entity decode
+    char        escape_value;       // [set] escape value (default '\\')
+    bool        force_parse;        // [set] force parse even if xml is not welformed
+
+    const char* xml;                // [get] xml source
+    bool        erorr_occur;        // [get] is occurance of error?
+    const char* error_pointer;      // [get] error position of xml source
+    PCODE       error_code;         // [get] error code
+    string      error_string;       // [get] error string
+
+    LPXDoc      doc;
+    _tagParseInfo() { trim_value = false; entity_value = true; force_parse = false; entitys = &entityDefault; xml = NULL; erorr_occur = false; error_pointer = NULL; error_code = PIE_PARSE_WELFORMED; escape_value = '\\'; }
+}PARSEINFO,*LPPARSEINFO;
+extern PARSEINFO piDefault;
+
+// display optional environment
+typedef struct _tagDispOption
+{
+    bool newline;               // newline when new tag
+    bool reference_value;       // do convert from entity to reference ( < -> < )
+    char value_quotation_mark;  // val="" (default value quotation mark "
+    LPXENTITYS entitys;         // entity table for entity encode
+
+    int tab_base;               // internal usage
+    _tagDispOption() { newline = true; reference_value = true; entitys = &entityDefault; tab_base = 0; value_quotation_mark = '"'; }
+}DISP_OPT, *LPDISP_OPT;
+extern DISP_OPT optDefault;
+
+// XAttr : Attribute Implementation
+typedef struct _tagXMLAttr
+{
+    string name;
+    string value;
+
+    _tagXMLNode* parent;
+
+    string GetXML( LPDISP_OPT opt = &optDefault );
+}XAttr, *LPXAttr;
+
+typedef enum
+{
+    XNODE_ELEMENT,              // general node '<element>...</element>' or <element/>
+    XNODE_PI,                   // <?xml version="1.0" ?>
+    XNODE_COMMENT,              // <!-- comment -->
+    XNODE_CDATA,                // <![CDATA[ cdata ]]>
+    XNODE_DOC,                  // internal virtual root
+}NODE_TYPE;
+
+// XMLNode structure
+typedef struct _tagXMLNode
+{
+    // name and value
+    string name;
+    string value;
+
+    // internal variables
+    LPXNode parent;     // parent node
+    XNodes childs;      // child node
+    XAttrs attrs;       // attributes
+    NODE_TYPE type;     // node type
+    LPXDoc doc;         // document
+
+    // Load/Save XML
+    const char* Load( const char* pszXml, LPPARSEINFO pi = &piDefault );
+    string GetXML( LPDISP_OPT opt = &optDefault );
+    string GetText( LPDISP_OPT opt = &optDefault );
+
+    // internal load functions
+    const char* LoadAttributes( const char* pszAttrs, LPPARSEINFO pi = &piDefault );
+    const char* LoadAttributes( const char* pszAttrs, const char* pszEnd, LPPARSEINFO pi = &piDefault );
+    const char* LoadProcessingInstrunction( const char* pszXml, LPPARSEINFO pi = &piDefault );
+    const char* LoadComment( const char* pszXml, LPPARSEINFO pi = &piDefault );
+    const char* LoadCDATA( const char* pszXml, LPPARSEINFO pi = &piDefault );
+
+    // in own attribute list
+    LPXAttr GetAttr( const char* attrname );
+    const char* GetAttrValue( const char* attrname );
+    XAttrs GetAttrs( const char* name );
+
+    // in one level child nodes
+    LPXNode GetChild( const char* name );
+    const char* GetChildValue( const char* name );
+    string GetChildText( const char* name, LPDISP_OPT opt = &optDefault );
+    XNodes GetChilds( const char* name );
+    XNodes GetChilds();
+
+    LPXAttr GetChildAttr( const char* name, const char* attrname );
+    const char* GetChildAttrValue( const char* name, const char* attrname );
+
+    // search node
+    LPXNode Find( const char* name );
+
+    // modify DOM
+    int     GetChildCount();
+    LPXNode GetChild( int i );
+    XNodes::iterator GetChildIterator( LPXNode node );
+    LPXNode CreateNode( const char* name = NULL, const char* value = NULL );
+    LPXNode AppendChild( const char* name = NULL, const char* value = NULL );
+    LPXNode AppendChild( LPXNode node );
+    bool    RemoveChild( LPXNode node );
+    LPXNode DetachChild( LPXNode node );
+
+    // node/branch copy
+    void    CopyNode( LPXNode node );
+    void    CopyBranch( LPXNode branch );
+    void    _CopyBranch( LPXNode node );
+    LPXNode AppendChildBranch( LPXNode node );
+
+    // modify attribute
+    LPXAttr GetAttr( int i );
+    XAttrs::iterator GetAttrIterator( LPXAttr node );
+    LPXAttr CreateAttr( const char* anem = NULL, const char* value = NULL );
+    LPXAttr AppendAttr( const char* name = NULL, const char* value = NULL );
+    LPXAttr AppendAttr( LPXAttr attr );
+    bool    RemoveAttr( LPXAttr attr );
+    LPXAttr DetachAttr( LPXAttr attr );
+
+    // operator overloads
+    LPXNode operator [] ( int i ) { return GetChild(i); }
+    XNode& operator = ( XNode& node ) { CopyBranch(&node); return *this; }
+
+    _tagXMLNode() { parent = NULL; doc = NULL; type = XNODE_ELEMENT; }
+    ~_tagXMLNode();
+
+    void Close();
+}XNode, *LPXNode;
+
+// XMLDocument structure
+typedef struct _tagXMLDocument : public XNode
+{
+    PARSEINFO parse_info;
+
+    _tagXMLDocument() { parent = NULL; doc = this; type = XNODE_DOC; }
+
+    const char* Load( const char* pszXml, LPPARSEINFO pi = NULL );
+    LPXNode GetRoot();
+
+}XDoc, *LPXDoc;
+
+// Helper Funtion
+inline long XStr2Int( const char* str, long default_value = 0 )
+{
+    return ( str && *str ) ? atol(str) : default_value;
+}
+
+inline string TrimRight(const string& source, const string& t)
+{
+    string str = source;
+    return str.erase ( str.find_last_not_of ( t ) + 1 ) ;
+}
+
+inline string TrimLeft(const string& source , const string& t)
+{
+    string str = source;
+    return str.erase ( 0 , source.find_first_not_of ( t ) ) ;
+}
+
+inline string Trim(const string& source)
+{
+    string str = source;
+    return TrimLeft ( TrimRight ( str , "\r\n\t " ) , "\r\n\t " ) ;
+}
+
+inline bool XIsEmptyString( const char* str )
+{
+    string s(str);
+    s = Trim(s);
+
+    return (s.length() == 0);
+}
+
+#endif

Added: sandbox/jng/vanilla_swig/Bindings/src/IMake/stdafx.cpp
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/IMake/stdafx.cpp	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/IMake/stdafx.cpp	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// IMake.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file

Added: sandbox/jng/vanilla_swig/Bindings/src/IMake/stdafx.h
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/IMake/stdafx.h	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/IMake/stdafx.h	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,24 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#ifndef _IMAKE_STD_H_
+#define _IMAKE_STD_H_
+
+#include <string>
+#include <iostream>
+#include <vector>
+#include <algorithm>
+#include <deque>
+#include <map>
+#include <set>
+#include <iostream>
+#include <sstream>
+#include <time.h>
+#include <ctype.h>
+#include <cstdlib>
+#include <cstring>
+#include <cstdio>
+
+#endif

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/MapGuideDotNetApi.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/MapGuideDotNetApi.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/MapGuideDotNetApi.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <PackageId>MapGuideDotNetApi</PackageId>
+    <TargetFramework>netstandard2.0</TargetFramework>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <Version>3.1.1.9389</Version>
+    <PackageLicenseUrl>https://www.gnu.org/licenses/lgpl-2.1.txt</PackageLicenseUrl>
+    <PackageProjectUrl>https://github.com/jumpinjackie/mapguide-api-bindings/</PackageProjectUrl>
+    <RepositoryUrl>https://github.com/jumpinjackie/mapguide-api-bindings/</RepositoryUrl>
+    <PackageIconUrl>https://mapguide.osgeo.org/</PackageIconUrl>
+    <PackageTags>MapGuide GIS Geospatial Maps</PackageTags>
+    <Authors>Jackie Ng</Authors>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Content Include="build/**" PackagePath="%(Identity)" />
+    <Content Include="runtimes/**" PackagePath="%(Identity)" />
+  </ItemGroup>
+
+</Project>

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/Properties/AssemblyInfo.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/Properties/AssemblyInfo.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/Properties/AssemblyInfo.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,77 @@
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+using System;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+//
+// 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("MapGuide .net API Dynamic Link Library")]
+[assembly: AssemblyDescription(".net wrapper for the MapGuide API")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MapGuide Open Source")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: CLSCompliant(true)]
+
+//
+// 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 Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("3.1.1.9389")]
+[assembly: AssemblyFileVersion("3.1.1.9389")]
+[assembly: AssemblyInformationalVersion("3.1.1.9389")]
+
+//
+// In order to sign your assembly you must specify a key to use. Refer to the
+// Microsoft .NET Framework documentation for more information on assembly signing.
+//
+// Use the attributes below to control which key is used for signing.
+//
+// Notes:
+//   (*) If no key is specified, the assembly is not signed.
+//   (*) KeyName refers to a key that has been installed in the Crypto Service
+//       Provider (CSP) on your machine. KeyFile refers to a file which contains
+//       a key.
+//   (*) If the KeyFile and the KeyName values are both specified, the
+//       following processing occurs:
+//       (1) If the KeyName can be found in the CSP, that key is used.
+//       (2) If the KeyName does not exist and the KeyFile does exist, the key
+//           in the KeyFile is installed into the CSP and used.
+//   (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
+//       When specifying the KeyFile, the location of the KeyFile should be
+//       relative to the project output directory which is
+//       %Project Directory%\obj\<configuration>. For example, if your KeyFile is
+//       located in the project directory, you would specify the AssemblyKeyFile
+//       attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
+//   (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
+//       documentation for more information on this.
+//
+[assembly: AssemblyDelaySign(false)]
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/build/net461/MapGuideDotNetApi.targets
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/build/net461/MapGuideDotNetApi.targets	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/build/net461/MapGuideDotNetApi.targets	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+    <PropertyGroup>
+        <!-- if ShouldIncludeNativeMapGuideApi == False then don't include the native libraries -->
+        <ShouldIncludeNativeMapGuideApi Condition=" '$(ShouldIncludeNativeMapGuideApi)' == '' ">True</ShouldIncludeNativeMapGuideApi>
+    </PropertyGroup>
+
+    <ItemGroup Condition=" '$(ShouldIncludeNativeMapGuideApi)' != 'False' ">
+        <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-x64\native\**\*" Condition="'$(OS)' == 'Windows_NT' And '$(Platform)' == 'x64'">
+            <Link>%(Filename)%(Extension)</Link>
+            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        </None>
+        <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\win-x86\native\**\*" Condition="'$(OS)' == 'Windows_NT' And '$(Platform)' == 'x86'">
+            <Link>%(Filename)%(Extension)</Link>
+            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        </None>
+    </ItemGroup>
+
+</Project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/EntryPoint.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/EntryPoint.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/EntryPoint.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,43 @@
+//
+//  Copyright (C) 2004-2015 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+using System;
+using System.IO;
+using System.Reflection;
+
+namespace OSGeo.MapGuide
+{
+    /// <summary>
+    /// This is the entry point of the MapGuide API
+    /// </summary>
+    public class MapGuideApi
+    {
+        static MapGuideApi()
+        {
+            
+        }
+        
+        /// <summary>
+        /// Initializes the MapGuide Web Tier APIs. You must call this method before using any other class or method
+        /// in the MapGuide API
+        /// </summary>
+        /// <param name="configFile">The path to the web tier configuration file</param>
+        /// <remarks>Subsequent calls do nothing and return immediately</remarks>
+        public static void MgInitializeWebTier(string configFile) {
+            MapGuideDotNetUnmanagedApiPINVOKE.MgInitializeWebTier(configFile);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/ManagedException.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/ManagedException.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/ManagedException.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,85 @@
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+using System;
+
+namespace OSGeo.MapGuide
+{
+    /// <summary>
+    /// ManagedException is the exception class from which the root of unmanaged exception derive
+    /// This class, deriving from .NET Exception, allows to use the most common properties of
+    /// the Exception class on exceptions wrapping unmanaged MgException classes. The implementation
+    /// of these properties relies on the equivalent MgException methods.
+    ///
+    /// Although this class does not wrap any unmanaged class, it still holds a C++ pointer to
+    /// the MgException that derives from it.
+    /// </summary>
+    public class ManagedException : Exception
+    {
+        private bool mIsWrapper;
+        private string mMessage;
+        private string mStackTrace;
+
+        public ManagedException()
+        {
+            mIsWrapper = true;
+            mMessage = string.Empty;
+            mStackTrace = string.Empty;
+        }
+
+        public override string Message
+        {
+            get
+            {
+                return mIsWrapper ? ((MgException)this).GetExceptionMessage() : mMessage;
+            }
+        }
+
+        public override string StackTrace
+        {
+            get
+            {
+                if (mIsWrapper)
+                {
+                    //Some cosmetic cleaning of the C++ stack trace to better line up with the .net one
+                    string[] mgStackTrace = ((MgException)this).GetStackTrace().Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
+                    //This currently looks like the following if we re-join:
+                    //
+                    // at- <stack frame>
+                    // at- <stack frame>
+                    // at- <stack frame>
+                    //
+                    //The "-" being a leftover, so replace "at-" with "at" as well after re-joining.
+                    //The reason we don't blindly replace "-" in the C++ stack is because we don't want to scramble any physical path
+                    //that would also contain a "-"
+                    string sanitizedStack = ("   at" + string.Join(Environment.NewLine + "   at", mgStackTrace)).Replace("at-", "at");
+                    return string.Format("{0}{1}   ==== [C++ <-> .net] ===={1}{2}", sanitizedStack, Environment.NewLine, base.StackTrace);
+                }
+                else
+                {
+                    return mStackTrace;
+                }
+            }
+        }
+
+        public override string ToString()
+        {
+            string className = this.GetType().ToString();
+            return string.Format("{0}: {1}{2}{3}", className, this.Message, Environment.NewLine, this.StackTrace);
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/MgClassMap.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/MgClassMap.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/MgClassMap.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,361 @@
+// This file is autogenerated by ClassMapGen. DO NOT EDIT
+
+using System;
+using System.Collections.Generic;
+
+namespace OSGeo.MapGuide
+{
+    internal static class MgClassMap
+    {
+        static Dictionary<int, string> classNameMap;
+        
+        static MgClassMap()
+        {
+            classNameMap = new Dictionary<int, string>();
+            
+            classNameMap[20004] = "OSGeo.MapGuide.MgAgfReaderWriter";
+            classNameMap[20005] = "OSGeo.MapGuide.MgAggregateGeometry";
+            classNameMap[1500] = "OSGeo.MapGuide.MgApplicationException";
+            classNameMap[20006] = "OSGeo.MapGuide.MgArcSegment";
+            classNameMap[1501] = "OSGeo.MapGuide.MgArgumentOutOfRangeException";
+            classNameMap[10500] = "OSGeo.MapGuide.MgArrayTypeMismatchException";
+            classNameMap[30000] = "OSGeo.MapGuide.MgAuthenticationFailedException";
+            classNameMap[1005] = "OSGeo.MapGuide.MgBatchPropertyCollection";
+            classNameMap[10252] = "OSGeo.MapGuide.MgBlobProperty";
+            classNameMap[10253] = "OSGeo.MapGuide.MgBooleanProperty";
+            classNameMap[10254] = "OSGeo.MapGuide.MgByteProperty";
+            classNameMap[1250] = "OSGeo.MapGuide.MgByteReader";
+            classNameMap[1257] = "OSGeo.MapGuide.MgByteSink";
+            classNameMap[1251] = "OSGeo.MapGuide.MgByteSource";
+            classNameMap[11750] = "OSGeo.MapGuide.MgClassDefinition";
+            classNameMap[11780] = "OSGeo.MapGuide.MgClassDefinitionCollection";
+            classNameMap[1502] = "OSGeo.MapGuide.MgClassNotFoundException";
+            classNameMap[10255] = "OSGeo.MapGuide.MgClobProperty";
+            classNameMap[10250] = "OSGeo.MapGuide.MgColor";
+            classNameMap[1503] = "OSGeo.MapGuide.MgConfigurationException";
+            classNameMap[1504] = "OSGeo.MapGuide.MgConfigurationLoadFailedException";
+            classNameMap[1505] = "OSGeo.MapGuide.MgConfigurationSaveFailedException";
+            classNameMap[30001] = "OSGeo.MapGuide.MgConnectionFailedException";
+            classNameMap[30002] = "OSGeo.MapGuide.MgConnectionNotOpenException";
+            classNameMap[20003] = "OSGeo.MapGuide.MgCoordinate";
+            classNameMap[20009] = "OSGeo.MapGuide.MgCoordinateIterator";
+            classNameMap[20500] = "OSGeo.MapGuide.MgCoordinateSystem";
+            classNameMap[20504] = "OSGeo.MapGuide.MgCoordinateSystemCatalog";
+            classNameMap[20506] = "OSGeo.MapGuide.MgCoordinateSystemCategory";
+            classNameMap[20510] = "OSGeo.MapGuide.MgCoordinateSystemCategoryDictionary";
+            classNameMap[21000] = "OSGeo.MapGuide.MgCoordinateSystemComputationFailedException";
+            classNameMap[21001] = "OSGeo.MapGuide.MgCoordinateSystemConversionFailedException";
+            classNameMap[20507] = "OSGeo.MapGuide.MgCoordinateSystemDatum";
+            classNameMap[20512] = "OSGeo.MapGuide.MgCoordinateSystemDatumDictionary";
+            classNameMap[20511] = "OSGeo.MapGuide.MgCoordinateSystemDictionary";
+            classNameMap[20521] = "OSGeo.MapGuide.MgCoordinateSystemDictionaryUtility";
+            classNameMap[20509] = "OSGeo.MapGuide.MgCoordinateSystemEllipsoid";
+            classNameMap[20513] = "OSGeo.MapGuide.MgCoordinateSystemEllipsoidDictionary";
+            classNameMap[20514] = "OSGeo.MapGuide.MgCoordinateSystemEnum";
+            classNameMap[20515] = "OSGeo.MapGuide.MgCoordinateSystemEnumInteger32";
+            classNameMap[20501] = "OSGeo.MapGuide.MgCoordinateSystemFactory";
+            classNameMap[20516] = "OSGeo.MapGuide.MgCoordinateSystemFilter";
+            classNameMap[20517] = "OSGeo.MapGuide.MgCoordinateSystemFilterInteger32";
+            classNameMap[20505] = "OSGeo.MapGuide.MgCoordinateSystemFormatConverter";
+            classNameMap[20542] = "OSGeo.MapGuide.MgCoordinateSystemGeodeticAnalyticalTransformDefParams";
+            classNameMap[20543] = "OSGeo.MapGuide.MgCoordinateSystemGeodeticInterpolationTransformDefParams";
+            classNameMap[20544] = "OSGeo.MapGuide.MgCoordinateSystemGeodeticMultipleRegressionTransformDefParams";
+            classNameMap[20533] = "OSGeo.MapGuide.MgCoordinateSystemGeodeticPath";
+            classNameMap[20535] = "OSGeo.MapGuide.MgCoordinateSystemGeodeticPathDictionary";
+            classNameMap[20534] = "OSGeo.MapGuide.MgCoordinateSystemGeodeticPathElement";
+            classNameMap[20508] = "OSGeo.MapGuide.MgCoordinateSystemGeodeticTransformation";
+            classNameMap[20536] = "OSGeo.MapGuide.MgCoordinateSystemGeodeticTransformDef";
+            classNameMap[20540] = "OSGeo.MapGuide.MgCoordinateSystemGeodeticTransformDefDictionary";
+            classNameMap[20545] = "OSGeo.MapGuide.MgCoordinateSystemGeodeticTransformGridFile";
+            classNameMap[20532] = "OSGeo.MapGuide.MgCoordinateSystemGridBase";
+            classNameMap[20524] = "OSGeo.MapGuide.MgCoordinateSystemGridBoundary";
+            classNameMap[20529] = "OSGeo.MapGuide.MgCoordinateSystemGridLine";
+            classNameMap[20526] = "OSGeo.MapGuide.MgCoordinateSystemGridLineCollection";
+            classNameMap[20530] = "OSGeo.MapGuide.MgCoordinateSystemGridRegion";
+            classNameMap[20527] = "OSGeo.MapGuide.MgCoordinateSystemGridRegionCollection";
+            classNameMap[20525] = "OSGeo.MapGuide.MgCoordinateSystemGridSpecification";
+            classNameMap[20531] = "OSGeo.MapGuide.MgCoordinateSystemGridTick";
+            classNameMap[20528] = "OSGeo.MapGuide.MgCoordinateSystemGridTickCollection";
+            classNameMap[21002] = "OSGeo.MapGuide.MgCoordinateSystemInitializationFailedException";
+            classNameMap[21003] = "OSGeo.MapGuide.MgCoordinateSystemLoadFailedException";
+            classNameMap[20518] = "OSGeo.MapGuide.MgCoordinateSystemMathComparator";
+            classNameMap[20502] = "OSGeo.MapGuide.MgCoordinateSystemMeasure";
+            classNameMap[21004] = "OSGeo.MapGuide.MgCoordinateSystemMeasureFailedException";
+            classNameMap[20522] = "OSGeo.MapGuide.MgCoordinateSystemMgrs";
+            classNameMap[20520] = "OSGeo.MapGuide.MgCoordinateSystemProjectionInformation";
+            classNameMap[20503] = "OSGeo.MapGuide.MgCoordinateSystemTransform";
+            classNameMap[21005] = "OSGeo.MapGuide.MgCoordinateSystemTransformFailedException";
+            classNameMap[20519] = "OSGeo.MapGuide.MgCoordinateSystemUnitInformation";
+            classNameMap[20048] = "OSGeo.MapGuide.MgCoordinateXY";
+            classNameMap[20051] = "OSGeo.MapGuide.MgCoordinateXYM";
+            classNameMap[20049] = "OSGeo.MapGuide.MgCoordinateXYZ";
+            classNameMap[20050] = "OSGeo.MapGuide.MgCoordinateXYZM";
+            classNameMap[20010] = "OSGeo.MapGuide.MgCurve";
+            classNameMap[20011] = "OSGeo.MapGuide.MgCurvePolygon";
+            classNameMap[20047] = "OSGeo.MapGuide.MgCurvePolygonCollection";
+            classNameMap[20012] = "OSGeo.MapGuide.MgCurveRing";
+            classNameMap[20052] = "OSGeo.MapGuide.MgCurveRingCollection";
+            classNameMap[20013] = "OSGeo.MapGuide.MgCurveSegment";
+            classNameMap[20041] = "OSGeo.MapGuide.MgCurveSegmentCollection";
+            classNameMap[20014] = "OSGeo.MapGuide.MgCurveString";
+            classNameMap[20043] = "OSGeo.MapGuide.MgCurveStringCollection";
+            classNameMap[11751] = "OSGeo.MapGuide.MgDataPropertyDefinition";
+            classNameMap[11773] = "OSGeo.MapGuide.MgDataReader";
+            classNameMap[1256] = "OSGeo.MapGuide.MgDateTime";
+            classNameMap[1506] = "OSGeo.MapGuide.MgDateTimeException";
+            classNameMap[10256] = "OSGeo.MapGuide.MgDateTimeProperty";
+            classNameMap[30003] = "OSGeo.MapGuide.MgDbException";
+            classNameMap[30004] = "OSGeo.MapGuide.MgDbXmlException";
+            classNameMap[1507] = "OSGeo.MapGuide.MgDecryptionException";
+            classNameMap[11775] = "OSGeo.MapGuide.MgDeleteFeatures";
+            classNameMap[1508] = "OSGeo.MapGuide.MgDirectoryNotFoundException";
+            classNameMap[1000] = "OSGeo.MapGuide.MgDisposableCollection";
+            classNameMap[1509] = "OSGeo.MapGuide.MgDivideByZeroException";
+            classNameMap[1510] = "OSGeo.MapGuide.MgDomainException";
+            classNameMap[10257] = "OSGeo.MapGuide.MgDoubleProperty";
+            classNameMap[30700] = "OSGeo.MapGuide.MgDrawingService";
+            classNameMap[1511] = "OSGeo.MapGuide.MgDuplicateDirectoryException";
+            classNameMap[1512] = "OSGeo.MapGuide.MgDuplicateFileException";
+            classNameMap[30005] = "OSGeo.MapGuide.MgDuplicateGroupException";
+            classNameMap[30006] = "OSGeo.MapGuide.MgDuplicateNameException";
+            classNameMap[1513] = "OSGeo.MapGuide.MgDuplicateObjectException";
+            classNameMap[30007] = "OSGeo.MapGuide.MgDuplicateParameterException";
+            classNameMap[30008] = "OSGeo.MapGuide.MgDuplicateRepositoryException";
+            classNameMap[10501] = "OSGeo.MapGuide.MgDuplicateResourceDataException";
+            classNameMap[10502] = "OSGeo.MapGuide.MgDuplicateResourceException";
+            classNameMap[30009] = "OSGeo.MapGuide.MgDuplicateRoleException";
+            classNameMap[30010] = "OSGeo.MapGuide.MgDuplicateServerException";
+            classNameMap[30011] = "OSGeo.MapGuide.MgDuplicateSessionException";
+            classNameMap[30012] = "OSGeo.MapGuide.MgDuplicateUserException";
+            classNameMap[30013] = "OSGeo.MapGuide.MgDwfException";
+            classNameMap[30014] = "OSGeo.MapGuide.MgDwfSectionNotFoundException";
+            classNameMap[30015] = "OSGeo.MapGuide.MgDwfSectionResourceNotFoundException";
+            classNameMap[30901] = "OSGeo.MapGuide.MgDwfVersion";
+            classNameMap[10503] = "OSGeo.MapGuide.MgEmptyFeatureSetException";
+            classNameMap[1514] = "OSGeo.MapGuide.MgEncryptionException";
+            classNameMap[30016] = "OSGeo.MapGuide.MgEndOfStreamException";
+            classNameMap[20001] = "OSGeo.MapGuide.MgEnvelope";
+            classNameMap[1515] = "OSGeo.MapGuide.MgException";
+            classNameMap[10504] = "OSGeo.MapGuide.MgFdoException";
+            classNameMap[11772] = "OSGeo.MapGuide.MgFeatureAggregateOptions";
+            classNameMap[11774] = "OSGeo.MapGuide.MgFeatureCommandCollection";
+            classNameMap[31001] = "OSGeo.MapGuide.MgFeatureInformation";
+            classNameMap[11764] = "OSGeo.MapGuide.MgFeatureProperty";
+            classNameMap[11771] = "OSGeo.MapGuide.MgFeatureQueryOptions";
+            classNameMap[11753] = "OSGeo.MapGuide.MgFeatureReader";
+            classNameMap[11778] = "OSGeo.MapGuide.MgFeatureSchema";
+            classNameMap[11779] = "OSGeo.MapGuide.MgFeatureSchemaCollection";
+            classNameMap[11754] = "OSGeo.MapGuide.MgFeatureService";
+            classNameMap[10505] = "OSGeo.MapGuide.MgFeatureServiceException";
+            classNameMap[11786] = "OSGeo.MapGuide.MgFileFeatureSourceParams";
+            classNameMap[1516] = "OSGeo.MapGuide.MgFileIoException";
+            classNameMap[1517] = "OSGeo.MapGuide.MgFileNotFoundException";
+            classNameMap[20016] = "OSGeo.MapGuide.MgGeometricEntity";
+            classNameMap[11756] = "OSGeo.MapGuide.MgGeometricPropertyDefinition";
+            classNameMap[20019] = "OSGeo.MapGuide.MgGeometry";
+            classNameMap[20020] = "OSGeo.MapGuide.MgGeometryCollection";
+            classNameMap[20021] = "OSGeo.MapGuide.MgGeometryComponent";
+            classNameMap[21006] = "OSGeo.MapGuide.MgGeometryException";
+            classNameMap[20002] = "OSGeo.MapGuide.MgGeometryFactory";
+            classNameMap[11758] = "OSGeo.MapGuide.MgGeometryProperty";
+            classNameMap[11785] = "OSGeo.MapGuide.MgGeometryTypeInfo";
+            classNameMap[30018] = "OSGeo.MapGuide.MgGroupNotFoundException";
+            classNameMap[11782] = "OSGeo.MapGuide.MgGwsFeatureReader";
+            classNameMap[40000] = "OSGeo.MapGuide.MgHttpHeader";
+            classNameMap[40006] = "OSGeo.MapGuide.MgHttpPrimitiveValue";
+            classNameMap[40004] = "OSGeo.MapGuide.MgHttpRequest";
+            classNameMap[40002] = "OSGeo.MapGuide.MgHttpRequestMetadata";
+            classNameMap[40001] = "OSGeo.MapGuide.MgHttpRequestParam";
+            classNameMap[40005] = "OSGeo.MapGuide.MgHttpResponse";
+            classNameMap[40003] = "OSGeo.MapGuide.MgHttpResult";
+            classNameMap[1518] = "OSGeo.MapGuide.MgIndexOutOfRangeException";
+            classNameMap[11776] = "OSGeo.MapGuide.MgInsertFeatures";
+            classNameMap[10258] = "OSGeo.MapGuide.MgInt16Property";
+            classNameMap[10259] = "OSGeo.MapGuide.MgInt32Property";
+            classNameMap[10260] = "OSGeo.MapGuide.MgInt64Property";
+            classNameMap[10000] = "OSGeo.MapGuide.MgIntCollection";
+            classNameMap[1519] = "OSGeo.MapGuide.MgInvalidArgumentException";
+            classNameMap[1520] = "OSGeo.MapGuide.MgInvalidCastException";
+            classNameMap[21007] = "OSGeo.MapGuide.MgInvalidCoordinateSystemException";
+            classNameMap[21008] = "OSGeo.MapGuide.MgInvalidCoordinateSystemTypeException";
+            classNameMap[21009] = "OSGeo.MapGuide.MgInvalidCoordinateSystemUnitsException";
+            classNameMap[30019] = "OSGeo.MapGuide.MgInvalidDwfPackageException";
+            classNameMap[30020] = "OSGeo.MapGuide.MgInvalidDwfSectionException";
+            classNameMap[30021] = "OSGeo.MapGuide.MgInvalidFeatureSourceException";
+            classNameMap[30022] = "OSGeo.MapGuide.MgInvalidIpAddressException";
+            classNameMap[30023] = "OSGeo.MapGuide.MgInvalidLicenseException";
+            classNameMap[30024] = "OSGeo.MapGuide.MgInvalidLogEntryException";
+            classNameMap[10507] = "OSGeo.MapGuide.MgInvalidMapDefinitionException";
+            classNameMap[1522] = "OSGeo.MapGuide.MgInvalidOperationException";
+            classNameMap[30026] = "OSGeo.MapGuide.MgInvalidPasswordException";
+            classNameMap[30027] = "OSGeo.MapGuide.MgInvalidPrintLayoutFontSizeUnitsException";
+            classNameMap[30028] = "OSGeo.MapGuide.MgInvalidPrintLayoutPositionUnitsException";
+            classNameMap[30029] = "OSGeo.MapGuide.MgInvalidPrintLayoutSizeUnitsException";
+            classNameMap[1523] = "OSGeo.MapGuide.MgInvalidPropertyTypeException";
+            classNameMap[10508] = "OSGeo.MapGuide.MgInvalidRepositoryNameException";
+            classNameMap[10509] = "OSGeo.MapGuide.MgInvalidRepositoryTypeException";
+            classNameMap[10510] = "OSGeo.MapGuide.MgInvalidResourceDataNameException";
+            classNameMap[10511] = "OSGeo.MapGuide.MgInvalidResourceDataTypeException";
+            classNameMap[10512] = "OSGeo.MapGuide.MgInvalidResourceNameException";
+            classNameMap[10513] = "OSGeo.MapGuide.MgInvalidResourcePathException";
+            classNameMap[10514] = "OSGeo.MapGuide.MgInvalidResourcePreProcessingTypeException";
+            classNameMap[10515] = "OSGeo.MapGuide.MgInvalidResourceTypeException";
+            classNameMap[30031] = "OSGeo.MapGuide.MgInvalidServerNameException";
+            classNameMap[1524] = "OSGeo.MapGuide.MgInvalidStreamHeaderException";
+            classNameMap[1525] = "OSGeo.MapGuide.MgIoException";
+            classNameMap[31300] = "OSGeo.MapGuide.MgKmlService";
+            classNameMap[30501] = "OSGeo.MapGuide.MgLayer";
+            classNameMap[12003] = "OSGeo.MapGuide.MgLayerBase";
+            classNameMap[12002] = "OSGeo.MapGuide.MgLayerCollection";
+            classNameMap[12001] = "OSGeo.MapGuide.MgLayerGroup";
+            classNameMap[12004] = "OSGeo.MapGuide.MgLayerGroupCollection";
+            classNameMap[10517] = "OSGeo.MapGuide.MgLayerNotFoundException";
+            classNameMap[30904] = "OSGeo.MapGuide.MgLayout";
+            classNameMap[1526] = "OSGeo.MapGuide.MgLengthException";
+            classNameMap[30032] = "OSGeo.MapGuide.MgLicenseException";
+            classNameMap[30033] = "OSGeo.MapGuide.MgLicenseExpiredException";
+            classNameMap[20023] = "OSGeo.MapGuide.MgLinearRing";
+            classNameMap[20053] = "OSGeo.MapGuide.MgLinearRingCollection";
+            classNameMap[20024] = "OSGeo.MapGuide.MgLinearSegment";
+            classNameMap[20042] = "OSGeo.MapGuide.MgLineString";
+            classNameMap[20044] = "OSGeo.MapGuide.MgLineStringCollection";
+            classNameMap[1527] = "OSGeo.MapGuide.MgLogicException";
+            classNameMap[11766] = "OSGeo.MapGuide.MgLongTransactionReader";
+            classNameMap[30500] = "OSGeo.MapGuide.MgMap";
+            classNameMap[12000] = "OSGeo.MapGuide.MgMapBase";
+            classNameMap[12005] = "OSGeo.MapGuide.MgMapCollection";
+            classNameMap[30900] = "OSGeo.MapGuide.MgMappingService";
+            classNameMap[30905] = "OSGeo.MapGuide.MgMapPlot";
+            classNameMap[30906] = "OSGeo.MapGuide.MgMapPlotCollection";
+            classNameMap[20029] = "OSGeo.MapGuide.MgMultiCurvePolygon";
+            classNameMap[20030] = "OSGeo.MapGuide.MgMultiCurveString";
+            classNameMap[20031] = "OSGeo.MapGuide.MgMultiGeometry";
+            classNameMap[20032] = "OSGeo.MapGuide.MgMultiLineString";
+            classNameMap[20033] = "OSGeo.MapGuide.MgMultiPoint";
+            classNameMap[20034] = "OSGeo.MapGuide.MgMultiPolygon";
+            classNameMap[1528] = "OSGeo.MapGuide.MgNotFiniteNumberException";
+            classNameMap[1529] = "OSGeo.MapGuide.MgNotImplementedException";
+            classNameMap[1530] = "OSGeo.MapGuide.MgNullArgumentException";
+            classNameMap[1531] = "OSGeo.MapGuide.MgNullPropertyValueException";
+            classNameMap[1532] = "OSGeo.MapGuide.MgNullReferenceException";
+            classNameMap[1533] = "OSGeo.MapGuide.MgObjectNotFoundException";
+            classNameMap[11759] = "OSGeo.MapGuide.MgObjectPropertyDefinition";
+            classNameMap[30035] = "OSGeo.MapGuide.MgOperationProcessingException";
+            classNameMap[1534] = "OSGeo.MapGuide.MgOutOfMemoryException";
+            classNameMap[1535] = "OSGeo.MapGuide.MgOutOfRangeException";
+            classNameMap[1536] = "OSGeo.MapGuide.MgOverflowException";
+            classNameMap[30604] = "OSGeo.MapGuide.MgPackageStatusInformation";
+            classNameMap[11788] = "OSGeo.MapGuide.MgParameter";
+            classNameMap[10004] = "OSGeo.MapGuide.MgParameterCollection";
+            classNameMap[30036] = "OSGeo.MapGuide.MgParameterNotFoundException";
+            classNameMap[30037] = "OSGeo.MapGuide.MgPathTooLongException";
+            classNameMap[1537] = "OSGeo.MapGuide.MgPlatformNotSupportedException";
+            classNameMap[30902] = "OSGeo.MapGuide.MgPlotSpecification";
+            classNameMap[20000] = "OSGeo.MapGuide.MgPoint";
+            classNameMap[20045] = "OSGeo.MapGuide.MgPointCollection";
+            classNameMap[20035] = "OSGeo.MapGuide.MgPolygon";
+            classNameMap[20046] = "OSGeo.MapGuide.MgPolygonCollection";
+            classNameMap[30039] = "OSGeo.MapGuide.MgPortNotAvailableException";
+            classNameMap[30040] = "OSGeo.MapGuide.MgPrintToScaleModeNotSelectedException";
+            classNameMap[31400] = "OSGeo.MapGuide.MgProfilingService";
+            classNameMap[2000] = "OSGeo.MapGuide.MgProperty";
+            classNameMap[1002] = "OSGeo.MapGuide.MgPropertyCollection";
+            classNameMap[2002] = "OSGeo.MapGuide.MgPropertyDefinition";
+            classNameMap[10001] = "OSGeo.MapGuide.MgPropertyDefinitionCollection";
+            classNameMap[11769] = "OSGeo.MapGuide.MgRaster";
+            classNameMap[11770] = "OSGeo.MapGuide.MgRasterProperty";
+            classNameMap[11768] = "OSGeo.MapGuide.MgRasterPropertyDefinition";
+            classNameMap[12006] = "OSGeo.MapGuide.MgReadOnlyLayerCollection";
+            classNameMap[20037] = "OSGeo.MapGuide.MgRegion";
+            classNameMap[31002] = "OSGeo.MapGuide.MgRenderingOptions";
+            classNameMap[31000] = "OSGeo.MapGuide.MgRenderingService";
+            classNameMap[30041] = "OSGeo.MapGuide.MgRepositoryCreationFailedException";
+            classNameMap[30042] = "OSGeo.MapGuide.MgRepositoryNotFoundException";
+            classNameMap[30043] = "OSGeo.MapGuide.MgRepositoryNotOpenException";
+            classNameMap[30044] = "OSGeo.MapGuide.MgRepositoryOpenFailedException";
+            classNameMap[11526] = "OSGeo.MapGuide.MgResource";
+            classNameMap[10518] = "OSGeo.MapGuide.MgResourceBusyException";
+            classNameMap[10519] = "OSGeo.MapGuide.MgResourceDataNotFoundException";
+            classNameMap[11500] = "OSGeo.MapGuide.MgResourceIdentifier";
+            classNameMap[10520] = "OSGeo.MapGuide.MgResourceNotFoundException";
+            classNameMap[11501] = "OSGeo.MapGuide.MgResourceService";
+            classNameMap[1538] = "OSGeo.MapGuide.MgResourcesException";
+            classNameMap[1539] = "OSGeo.MapGuide.MgResourcesLoadFailedException";
+            classNameMap[1540] = "OSGeo.MapGuide.MgResourceTagNotFoundException";
+            classNameMap[20038] = "OSGeo.MapGuide.MgRing";
+            classNameMap[30045] = "OSGeo.MapGuide.MgRoleNotFoundException";
+            classNameMap[1541] = "OSGeo.MapGuide.MgRuntimeException";
+            classNameMap[30502] = "OSGeo.MapGuide.MgSelection";
+            classNameMap[12007] = "OSGeo.MapGuide.MgSelectionBase";
+            classNameMap[30607] = "OSGeo.MapGuide.MgServerAdmin";
+            classNameMap[30046] = "OSGeo.MapGuide.MgServerNotFoundException";
+            classNameMap[30047] = "OSGeo.MapGuide.MgServerNotOnlineException";
+            classNameMap[11251] = "OSGeo.MapGuide.MgService";
+            classNameMap[10521] = "OSGeo.MapGuide.MgServiceNotAvailableException";
+            classNameMap[10522] = "OSGeo.MapGuide.MgServiceNotSupportedException";
+            classNameMap[30048] = "OSGeo.MapGuide.MgSessionExpiredException";
+            classNameMap[30052] = "OSGeo.MapGuide.MgSessionNotFoundException";
+            classNameMap[10261] = "OSGeo.MapGuide.MgSingleProperty";
+            classNameMap[30605] = "OSGeo.MapGuide.MgSite";
+            classNameMap[30601] = "OSGeo.MapGuide.MgSiteConnection";
+            classNameMap[30608] = "OSGeo.MapGuide.MgSiteInfo";
+            classNameMap[11761] = "OSGeo.MapGuide.MgSpatialContextReader";
+            classNameMap[11762] = "OSGeo.MapGuide.MgSqlDataReader";
+            classNameMap[1542] = "OSGeo.MapGuide.MgStreamIoException";
+            classNameMap[1003] = "OSGeo.MapGuide.MgStringCollection";
+            classNameMap[2001] = "OSGeo.MapGuide.MgStringProperty";
+            classNameMap[10003] = "OSGeo.MapGuide.MgStringPropertyCollection";
+            classNameMap[1543] = "OSGeo.MapGuide.MgSystemException";
+            classNameMap[1544] = "OSGeo.MapGuide.MgTemporaryFileNotAvailableException";
+            classNameMap[1545] = "OSGeo.MapGuide.MgThirdPartyException";
+            classNameMap[31200] = "OSGeo.MapGuide.MgTileService";
+            classNameMap[11787] = "OSGeo.MapGuide.MgTransaction";
+            classNameMap[30049] = "OSGeo.MapGuide.MgUnauthorizedAccessException";
+            classNameMap[1547] = "OSGeo.MapGuide.MgUnclassifiedException";
+            classNameMap[1548] = "OSGeo.MapGuide.MgUnderflowException";
+            classNameMap[30056] = "OSGeo.MapGuide.MgUnknownTileProviderException";
+            classNameMap[30057] = "OSGeo.MapGuide.MgUnsupportedTileProviderException";
+            classNameMap[11777] = "OSGeo.MapGuide.MgUpdateFeatures";
+            classNameMap[30050] = "OSGeo.MapGuide.MgUriFormatException";
+            classNameMap[30606] = "OSGeo.MapGuide.MgUserInformation";
+            classNameMap[10523] = "OSGeo.MapGuide.MgUserNotFoundException";
+            classNameMap[11257] = "OSGeo.MapGuide.MgWarnings";
+            classNameMap[50005] = "OSGeo.MapGuide.MgWebBufferCommand";
+            classNameMap[50000] = "OSGeo.MapGuide.MgWebCommand";
+            classNameMap[50012] = "OSGeo.MapGuide.MgWebCommandCollection";
+            classNameMap[50015] = "OSGeo.MapGuide.MgWebCommandWidget";
+            classNameMap[50025] = "OSGeo.MapGuide.MgWebContextMenu";
+            classNameMap[50016] = "OSGeo.MapGuide.MgWebFlyoutWidget";
+            classNameMap[50009] = "OSGeo.MapGuide.MgWebGetPrintablePageCommand";
+            classNameMap[50011] = "OSGeo.MapGuide.MgWebHelpCommand";
+            classNameMap[50022] = "OSGeo.MapGuide.MgWebInformationPane";
+            classNameMap[50003] = "OSGeo.MapGuide.MgWebInvokeScriptCommand";
+            classNameMap[50004] = "OSGeo.MapGuide.MgWebInvokeUrlCommand";
+            classNameMap[50026] = "OSGeo.MapGuide.MgWebLayout";
+            classNameMap[50008] = "OSGeo.MapGuide.MgWebMeasureCommand";
+            classNameMap[50007] = "OSGeo.MapGuide.MgWebPrintCommand";
+            classNameMap[50002] = "OSGeo.MapGuide.MgWebSearchCommand";
+            classNameMap[50006] = "OSGeo.MapGuide.MgWebSelectWithinCommand";
+            classNameMap[50014] = "OSGeo.MapGuide.MgWebSeparatorWidget";
+            classNameMap[50023] = "OSGeo.MapGuide.MgWebTaskBar";
+            classNameMap[50017] = "OSGeo.MapGuide.MgWebTaskBarWidget";
+            classNameMap[50024] = "OSGeo.MapGuide.MgWebTaskPane";
+            classNameMap[50021] = "OSGeo.MapGuide.MgWebToolBar";
+            classNameMap[50019] = "OSGeo.MapGuide.MgWebUiPane";
+            classNameMap[50020] = "OSGeo.MapGuide.MgWebUiSizablePane";
+            classNameMap[50001] = "OSGeo.MapGuide.MgWebUiTargetCommand";
+            classNameMap[50010] = "OSGeo.MapGuide.MgWebViewOptionsCommand";
+            classNameMap[50013] = "OSGeo.MapGuide.MgWebWidget";
+            classNameMap[50018] = "OSGeo.MapGuide.MgWebWidgetCollection";
+            classNameMap[20040] = "OSGeo.MapGuide.MgWktReaderWriter";
+            classNameMap[1549] = "OSGeo.MapGuide.MgXmlException";
+            classNameMap[1550] = "OSGeo.MapGuide.MgXmlParserException";
+
+        }
+        
+        internal static string GetTypeName(int id)
+        {
+            return classNameMap.ContainsKey(id) ? classNameMap[id] : null;
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/MgObjectFactory.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/MgObjectFactory.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/MgObjectFactory.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,82 @@
+//
+//  Copyright (C) 2004-2015 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+using System;
+using System.Reflection;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+namespace OSGeo.MapGuide
+{
+    internal static class MgObjectFactory 
+    {
+        static string GetClassName(IntPtr objPtr)
+        {
+            IntPtr cPtr = MapGuideDotNetUnmanagedApiPINVOKE.GetClassName(objPtr);
+            string str = Marshal.PtrToStringUni(cPtr);
+            Marshal.FreeCoTaskMem(cPtr);
+            return str;
+        }
+    
+        internal static T CreateObject<T>(IntPtr objPtr) where T : class
+        {
+            T obj = null;
+            int clsId = MapGuideDotNetUnmanagedApiPINVOKE.GetClassId(objPtr);
+            string typeName = MgClassMap.GetTypeName(clsId);
+            if (typeName == null) //Shouldn't happen. But if it did, this would mean we missed a spot when compiling class ids
+            {
+                throw new Exception("Could not resolve .net type for this unmanaged pointer. The unmanaged pointer reported a class ID of: " + clsId);
+            }
+            
+            var type = Type.GetType(typeName);
+            if (type == null) //Shouldn't happen. But if it did, this would mean we didn't expose this class to SWIG
+            {
+                throw new Exception("The type " + typeName + " does not exist. The unmanaged pointer reported a class ID of: " + clsId);
+            }
+            else
+            {
+                object[] args = new object[] 
+                {
+                    objPtr,
+                    true /* ownMemory */
+                };
+                
+                //The constructor we require has been assigned internal visibility by SWIG. We could change it to public, but the internal
+                //visibility is the ideal one for purposes of encapulsation (this is internal use only). So instead of Activator.CreateInstance()
+                //which does not work with internal constructors, we'll find the ctor ourselves and invoke it.
+                var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
+                var ctors = type.GetTypeInfo().GetConstructors(flags);
+                var ctor = ctors.FirstOrDefault(ci =>
+                                {
+                                    var parms = ci.GetParameters();
+                                    if (parms.Length == 2)
+                                    {
+                                        return parms[0].ParameterType == typeof(IntPtr) &&
+                                               parms[1].ParameterType == typeof(bool);
+                                    }
+                                    return false;
+                                });
+                if (ctor == null)
+                    throw new Exception("Could not find required constructor among " + ctors.Length + " constructors with signature (IntPtr, bool) on type: " + type.Name);
+                    
+                obj = ctor.Invoke(args) as T;
+                if (obj == null)
+                    throw new Exception("Could not create an instance of type " + typeof(T).Name + " (concrete type: " + type.Name + "). The unmanaged pointer reported a class ID of: " + clsId);
+            }
+            return obj;
+        }
+    } 
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/MgStringCollection.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/MgStringCollection.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/MgStringCollection.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,35 @@
+//
+//  Copyright (C) 2004-2015 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+using System;
+using System.IO;
+using System.Reflection;
+using System.Collections.Generic;
+
+namespace OSGeo.MapGuide
+{
+    partial class MgStringCollection
+    {
+        public MgStringCollection(IEnumerable<string> strings)
+            : this()
+        {
+            foreach (var str in strings)
+            {
+                Add(str);
+            }
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/Streams.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/Streams.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/custom/Streams.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,264 @@
+//
+//  Copyright (C) 2018 Jackie Ng
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+using System;
+using System.IO;
+
+namespace OSGeo.MapGuide
+{
+    /// <summary>
+    /// A read-only, rewindable stream
+    /// </summary>
+    public abstract class ReadOnlyRewindableStream : Stream
+    {
+        /// <summary>
+        /// Resets the internal position of the stream
+        /// </summary>
+        public abstract void Rewind();
+
+        /// <summary>
+        /// Gets whether this stream supports reading
+        /// </summary>
+        public override bool CanRead
+        {
+            get { return true; }
+        }
+
+        /// <summary>
+        /// Gets whether this stream supports seeking
+        /// </summary>
+        public override bool CanSeek
+        {
+            get { return false; }
+        }
+
+        /// <summary>
+        /// Gets whether this stream supports writing
+        /// </summary>
+        public override bool CanWrite
+        {
+            get { return false; }
+        }
+
+        /// <summary>
+        /// Gets whether this stream is rewindable. If not, calls to <see cref="Rewind"/> throw a 
+        /// <see cref="T:System.InvalidOperationException"/>
+        /// </summary>
+        public abstract bool CanRewind { get; }
+
+        /// <summary>
+        /// Gets or sets the position (not implemented)
+        /// </summary>
+        public override long Position
+        {
+            get
+            {
+                throw new NotImplementedException();
+            }
+            set
+            {
+                throw new NotImplementedException();
+            }
+        }
+
+        /// <summary>
+        /// Seeks to the specified offset in the stream (not implemented)
+        /// </summary>
+        /// <param name="offset"></param>
+        /// <param name="origin"></param>
+        /// <returns></returns>
+        public override long Seek(long offset, SeekOrigin origin)
+        {
+            throw new NotImplementedException();
+        }
+
+        /// <summary>
+        /// Sets the length of the stream (not implemented)
+        /// </summary>
+        /// <param name="value"></param>
+        public override void SetLength(long value)
+        {
+            throw new NotImplementedException();
+        }
+
+        /// <summary>
+        /// Writes the specified buffer into the stream (not implemented)
+        /// </summary>
+        /// <param name="buffer"></param>
+        /// <param name="offset"></param>
+        /// <param name="count"></param>
+        public override void Write(byte[] buffer, int offset, int count)
+        {
+            throw new NotImplementedException();
+        }
+    }
+
+    /// <summary>
+    /// Represents a method that returns a <see cref="OSGeo.MapGuide.MgByteReader"/> instance
+    /// </summary>
+    /// <returns></returns>
+    public delegate MgByteReader GetByteReaderMethod();
+
+    /// <summary>
+    /// A read-only <see cref="System.IO.Stream"/> adapter for the <see cref="OSGeo.MapGuide.MgByteReader"/>
+    /// class.
+    /// </summary>
+    public class MgReadOnlyStream : ReadOnlyRewindableStream
+    {
+        private MgByteReader _reader;
+
+        /// <summary>
+        /// Creates a new instance
+        /// </summary>
+        /// <param name="method"></param>
+        public MgReadOnlyStream(GetByteReaderMethod method)
+        {
+            _reader = method();
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MgReadOnlyStream"/> class.
+        /// </summary>
+        /// <param name="reader">The reader.</param>
+        public MgReadOnlyStream(MgByteReader reader)
+        {
+            _reader = reader;
+        }
+
+        /// <summary>
+        /// Releases unmanaged resources and performs other cleanup operations before the
+        /// <see cref="MgReadOnlyStream"/> is reclaimed by garbage collection.
+        /// </summary>
+        ~MgReadOnlyStream()
+        {
+            Dispose(false);
+        }
+
+        /// <summary>
+        /// Releases the unmanaged resources used by the <see cref="T:System.IO.Stream"/> and optionally releases the managed resources.
+        /// </summary>
+        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                _reader.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        /// <summary>
+        /// When overridden in a derived class, clears all buffers for this stream and causes any buffered data to be written to the underlying device.
+        /// </summary>
+        /// <exception cref="T:System.IO.IOException">
+        /// An I/O error occurs.
+        ///   </exception>
+        public override void Flush()
+        {
+
+        }
+
+        /// <summary>
+        /// When overridden in a derived class, gets the length in bytes of the stream.
+        /// </summary>
+        /// <returns>
+        /// A long value representing the length of the stream in bytes.
+        ///   </returns>
+        ///   
+        /// <exception cref="T:System.NotSupportedException">
+        /// A class derived from Stream does not support seeking.
+        ///   </exception>
+        ///   
+        /// <exception cref="T:System.ObjectDisposedException">
+        /// Methods were called after the stream was closed.
+        ///   </exception>
+        public override long Length
+        {
+            //NOTE: MgByteReader only returns remaining length! Should we
+            //be keeping track of position and adding on this value?
+            get { return _reader.GetLength(); }
+        }
+
+        /// <summary>
+        /// Gets whether this stream is rewindable. If not, calls to <see cref="Rewind"/> throw a
+        /// <see cref="T:System.InvalidOperationException"/>
+        /// </summary>
+        public override bool CanRewind
+        {
+            get { return _reader.IsRewindable(); }
+        }
+
+        /// <summary>
+        /// Resets the internal position of the stream
+        /// </summary>
+        public override void Rewind()
+        {
+            if (!CanRewind)
+                throw new InvalidOperationException("Stream is not rewindable");
+
+            _reader.Rewind();
+        }
+
+        /// <summary>
+        /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
+        /// </summary>
+        /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between <paramref name="offset"/> and (<paramref name="offset"/> + <paramref name="count"/> - 1) replaced by the bytes read from the current source.</param>
+        /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin storing the data read from the current stream.</param>
+        /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
+        /// <returns>
+        /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
+        /// </returns>
+        /// <exception cref="T:System.ArgumentException">
+        /// The sum of <paramref name="offset"/> and <paramref name="count"/> is larger than the buffer length.
+        ///   </exception>
+        ///   
+        /// <exception cref="T:System.ArgumentNullException">
+        ///   <paramref name="buffer"/> is null.
+        ///   </exception>
+        ///   
+        /// <exception cref="T:System.ArgumentOutOfRangeException">
+        ///   <paramref name="offset"/> or <paramref name="count"/> is negative.
+        ///   </exception>
+        ///   
+        /// <exception cref="T:System.IO.IOException">
+        /// An I/O error occurs.
+        ///   </exception>
+        ///   
+        /// <exception cref="T:System.NotSupportedException">
+        /// The stream does not support reading.
+        ///   </exception>
+        ///   
+        /// <exception cref="T:System.ObjectDisposedException">
+        /// Methods were called after the stream was closed.
+        ///   </exception>
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            int read = 0;
+            //For good times, please always have the offset as 0
+            if (offset == 0)
+            {
+                read = _reader.Read(buffer, count);
+            }
+            else //So you want to play the hard way eh? Bad performance for you!
+            {
+                byte[] b = new byte[count];
+                read = _reader.Read(b, count);
+                Array.Copy(b, 0, buffer, offset, read);
+            }
+            return read;
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/ubuntu.14.04-x64/native/readme.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/ubuntu.14.04-x64/native/readme.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/ubuntu.14.04-x64/native/readme.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1 @@
+This library was built on Ubuntu 14.04 64-bit via CMake
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x64/native/readme.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x64/native/readme.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x64/native/readme.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1 @@
+This folder conatins the 64-bit unmanaged windows binaries for the MapGuide API
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x86/native/readme.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x86/native/readme.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/MapGuideDotNetApi/runtimes/win-x86/native/readme.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1 @@
+This folder conatins the 32-bit unmanaged windows binaries for the MapGuide API
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/AppThrowable.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/AppThrowable.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/AppThrowable.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,57 @@
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+package org.osgeo.mapguide;
+
+class AppThrowable extends RuntimeException
+{
+    public AppThrowable()
+    {
+    }
+
+    public AppThrowable(long cPtr, boolean cMemoryOwn)
+    {
+    }
+
+    public String getMessage()
+    {
+        try
+        {
+            return ((MgException)this).getExceptionMessage();
+        }
+        catch (MgException e)
+        {
+            return "";
+        }
+    }
+
+    public void printStackTrace()
+    {
+        try
+        {
+            System.out.println(((MgException)this).getExceptionStackTrace());
+        }
+        catch (MgException e)
+        {
+        }
+    }
+
+    public void delete()
+    {
+        //implemented by derived classes
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/MgBase64.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/MgBase64.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/MgBase64.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,1469 @@
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+/**
+ * Encodes and decodes to and from Base64 notation.
+ *
+ * <p>
+ * Change Log:
+ * </p>
+ * <ul>
+ *  <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
+ *   some convenience methods for reading and writing to and from files.</li>
+ *  <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
+ *   with other encodings (like EBCDIC).</li>
+ *  <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
+ *   encoded data was a single byte.</li>
+ *  <li>v2.0 - I got rid of methods that used booleans to set options.
+ *   Now everything is more consolidated and cleaner. The code now detects
+ *   when data that's being decoded is gzip-compressed and will decompress it
+ *   automatically. Generally things are cleaner. You'll probably have to
+ *   change some method calls that you were making to support the new
+ *   options format (<tt>int</tt>s that you "OR" together).</li>
+ *  <li>v1.5.1 - Fixed bug when decompressing and decoding to a
+ *   byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.
+ *   Added the ability to "suspend" encoding in the Output Stream so
+ *   you can turn on and off the encoding if you need to embed base64
+ *   data in an otherwise "normal" stream (like an XML file).</li>
+ *  <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
+ *      This helps when using GZIP streams.
+ *      Added the ability to GZip-compress objects before encoding them.</li>
+ *  <li>v1.4 - Added helper methods to read/write files.</li>
+ *  <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
+ *  <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
+ *      where last buffer being read, if not completely full, was not returned.</li>
+ *  <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
+ *  <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
+ * </ul>
+ *
+ * <p>
+ * I am placing this code in the Public Domain. Do with it as you will.
+ * This software comes with no guarantees or warranties but with
+ * plenty of well-wishing instead!
+ * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
+ * periodically to check for updates or to contribute improvements.
+ * </p>
+ *
+ * @author Robert Harder
+ * @author rob at iharder.net
+ * @version 2.1
+ */
+
+package org.osgeo.mapguide;
+
+public class MgBase64
+{
+
+/* ********  P U B L I C   F I E L D S  ******** */
+
+
+    /** No options specified. Value is zero. */
+    public final static int NO_OPTIONS = 0;
+
+    /** Specify encoding. */
+    public final static int ENCODE = 1;
+
+
+    /** Specify decoding. */
+    public final static int DECODE = 0;
+
+
+    /** Specify that data should be gzip-compressed. */
+    public final static int GZIP = 2;
+
+
+    /** Don't break lines when encoding (violates strict Base64 specification) */
+    public final static int DONT_BREAK_LINES = 8;
+
+
+/* ********  P R I V A T E   F I E L D S  ******** */
+
+
+    /** Maximum line length (76) of Base64 output. */
+    private final static int MAX_LINE_LENGTH = 76;
+
+
+    /** The equals sign (=) as a byte. */
+    private final static byte EQUALS_SIGN = (byte)'=';
+
+
+    /** The new line character (\n) as a byte. */
+    private final static byte NEW_LINE = (byte)'\n';
+
+
+    /** Preferred encoding. */
+    private final static String PREFERRED_ENCODING = "UTF-8";
+
+
+    /** The 64 valid Base64 values. */
+    private final static byte[] ALPHABET;
+    private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */
+    {
+        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+        (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+        (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+        (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+        (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+        (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
+        (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
+    };
+
+    /** Determine which ALPHABET to use. */
+    static
+    {
+        byte[] __bytes;
+        try
+        {
+            __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING );
+        }   // end try
+        catch (java.io.UnsupportedEncodingException use)
+        {
+            __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
+        }   // end catch
+        ALPHABET = __bytes;
+    }   // end static
+
+
+    /**
+     * Translates a Base64 value to either its 6-bit reconstruction value
+     * or a negative number indicating some other meaning.
+     **/
+    private final static byte[] DECODABET =
+    {
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+        -5,-5,                                      // Whitespace: Tab and Linefeed
+        -9,-9,                                      // Decimal 11 - 12
+        -5,                                         // Whitespace: Carriage Return
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+        -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+        -5,                                         // Whitespace: Space
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+        62,                                         // Plus sign at decimal 43
+        -9,-9,-9,                                   // Decimal 44 - 46
+        63,                                         // Slash at decimal 47
+        52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
+        -9,-9,-9,                                   // Decimal 58 - 60
+        -1,                                         // Equals sign at decimal 61
+        -9,-9,-9,                                      // Decimal 62 - 64
+        0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
+        14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
+        -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96
+        26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
+        39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
+        -9,-9,-9,-9                                 // Decimal 123 - 126
+        /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+
+    // I think I end up not using the BAD_ENCODING indicator.
+    //private final static byte BAD_ENCODING    = -9; // Indicates error in encoding
+    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
+    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
+
+
+    /** Defeats instantiation. */
+    private MgBase64(){}
+
+
+
+/* ********  E N C O D I N G   M E T H O D S  ******** */
+
+
+    /**
+     * Encodes up to the first three bytes of array <var>threeBytes</var>
+     * and returns a four-byte array in Base64 notation.
+     * The actual number of significant bytes in your array is
+     * given by <var>numSigBytes</var>.
+     * The array <var>threeBytes</var> needs only be as big as
+     * <var>numSigBytes</var>.
+     * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
+     *
+     * @param b4 A reusable byte array to reduce array instantiation
+     * @param threeBytes the array to convert
+     * @param numSigBytes the number of significant bytes in your array
+     * @return four byte array in Base64 notation.
+     * @since 1.5.1
+     */
+    private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes )
+    {
+        encode3to4( threeBytes, 0, numSigBytes, b4, 0 );
+        return b4;
+    }   // end encode3to4
+
+
+    /**
+     * Encodes up to three bytes of the array <var>source</var>
+     * and writes the resulting four Base64 bytes to <var>destination</var>.
+     * The source and destination arrays can be manipulated
+     * anywhere along their length by specifying
+     * <var>srcOffset</var> and <var>destOffset</var>.
+     * This method does not check to make sure your arrays
+     * are large enough to accomodate <var>srcOffset</var> + 3 for
+     * the <var>source</var> array or <var>destOffset</var> + 4 for
+     * the <var>destination</var> array.
+     * The actual number of significant bytes in your array is
+     * given by <var>numSigBytes</var>.
+     *
+     * @param source the array to convert
+     * @param srcOffset the index where conversion begins
+     * @param numSigBytes the number of significant bytes in your array
+     * @param destination the array to hold the conversion
+     * @param destOffset the index where output will be put
+     * @return the <var>destination</var> array
+     * @since 1.3
+     */
+    private static byte[] encode3to4(
+     byte[] source, int srcOffset, int numSigBytes,
+     byte[] destination, int destOffset )
+    {
+        //           1         2         3
+        // 01234567890123456789012345678901 Bit position
+        // --------000000001111111122222222 Array position from threeBytes
+        // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
+        //          >>18  >>12  >> 6  >> 0  Right shift necessary
+        //                0x3f  0x3f  0x3f  Additional AND
+
+        // Create buffer with zero-padding if there are only one or two
+        // significant bytes passed in the array.
+        // We have to shift left 24 in order to flush out the 1's that appear
+        // when Java treats a value as negative that is cast from a byte to an int.
+        int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )
+                     | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
+                     | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
+
+        switch( numSigBytes )
+        {
+            case 3:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
+                destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];
+                return destination;
+
+            case 2:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
+                destination[ destOffset + 3 ] = EQUALS_SIGN;
+                return destination;
+
+            case 1:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = EQUALS_SIGN;
+                destination[ destOffset + 3 ] = EQUALS_SIGN;
+                return destination;
+
+            default:
+                return destination;
+        }   // end switch
+    }   // end encode3to4
+
+
+
+    /**
+     * Serializes an object and returns the Base64-encoded
+     * version of that serialized object. If the object
+     * cannot be serialized or there is another error,
+     * the method will return <tt>null</tt>.
+     * The object is not GZip-compressed before being encoded.
+     *
+     * @param serializableObject The object to encode
+     * @return The Base64-encoded object
+     * @since 1.4
+     */
+    public static String encodeObject( java.io.Serializable serializableObject )
+    {
+        return encodeObject( serializableObject, NO_OPTIONS );
+    }   // end encodeObject
+
+
+
+    /**
+     * Serializes an object and returns the Base64-encoded
+     * version of that serialized object. If the object
+     * cannot be serialized or there is another error,
+     * the method will return <tt>null</tt>.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     * @param serializableObject The object to encode
+     * @param options Specified options
+     * @return The Base64-encoded object
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeObject( java.io.Serializable serializableObject, int options )
+    {
+        // Streams
+        java.io.ByteArrayOutputStream  baos  = null;
+        java.io.OutputStream           b64os = null;
+        java.io.ObjectOutputStream     oos   = null;
+        java.util.zip.GZIPOutputStream gzos  = null;
+
+        // Isolate options
+        int gzip           = (options & GZIP);
+        int dontBreakLines = (options & DONT_BREAK_LINES);
+
+        try
+        {
+            // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
+            baos  = new java.io.ByteArrayOutputStream();
+            b64os = new MgBase64.OutputStream( baos, ENCODE | dontBreakLines );
+
+            // GZip?
+            if( gzip == GZIP )
+            {
+                gzos = new java.util.zip.GZIPOutputStream( b64os );
+                oos  = new java.io.ObjectOutputStream( gzos );
+            }   // end if: gzip
+            else
+                oos   = new java.io.ObjectOutputStream( b64os );
+
+            oos.writeObject( serializableObject );
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            e.printStackTrace();
+            return null;
+        }   // end catch
+        finally
+        {
+            try{ oos.close();   } catch( Exception e ){}
+            try{ gzos.close();  } catch( Exception e ){}
+            try{ b64os.close(); } catch( Exception e ){}
+            try{ baos.close();  } catch( Exception e ){}
+        }   // end finally
+
+        // Return value according to relevant encoding.
+        try
+        {
+            return new String( baos.toByteArray(), PREFERRED_ENCODING );
+        }   // end try
+        catch (java.io.UnsupportedEncodingException uue)
+        {
+            return new String( baos.toByteArray() );
+        }   // end catch
+
+    }   // end encode
+
+
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * Does not GZip-compress data.
+     *
+     * @param source The data to convert
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source )
+    {
+        return encodeBytes( source, 0, source.length, NO_OPTIONS );
+    }   // end encodeBytes
+
+
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     *
+     * @param source The data to convert
+     * @param options Specified options
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source, int options )
+    {
+        return encodeBytes( source, 0, source.length, options );
+    }   // end encodeBytes
+
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * Does not GZip-compress data.
+     *
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source, int off, int len )
+    {
+        return encodeBytes( source, off, len, NO_OPTIONS );
+    }   // end encodeBytes
+
+
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     *
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @param options Specified options
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source, int off, int len, int options )
+    {
+        // Isolate options
+        int dontBreakLines = ( options & DONT_BREAK_LINES );
+        int gzip           = ( options & GZIP   );
+
+        // Compress?
+        if( gzip == GZIP )
+        {
+            java.io.ByteArrayOutputStream  baos  = null;
+            java.util.zip.GZIPOutputStream gzos  = null;
+            MgBase64.OutputStream          b64os = null;
+
+
+            try
+            {
+                // GZip -> Base64 -> ByteArray
+                baos = new java.io.ByteArrayOutputStream();
+                b64os = new MgBase64.OutputStream( baos, ENCODE | dontBreakLines );
+                gzos  = new java.util.zip.GZIPOutputStream( b64os );
+
+                gzos.write( source, off, len );
+                gzos.close();
+            }   // end try
+            catch( java.io.IOException e )
+            {
+                e.printStackTrace();
+                return null;
+            }   // end catch
+            finally
+            {
+                try{ gzos.close();  } catch( Exception e ){}
+                try{ b64os.close(); } catch( Exception e ){}
+                try{ baos.close();  } catch( Exception e ){}
+            }   // end finally
+
+            // Return value according to relevant encoding.
+            try
+            {
+                return new String( baos.toByteArray(), PREFERRED_ENCODING );
+            }   // end try
+            catch (java.io.UnsupportedEncodingException uue)
+            {
+                return new String( baos.toByteArray() );
+            }   // end catch
+        }   // end if: compress
+
+        // Else, don't compress. Better not to use streams at all then.
+        else
+        {
+            // Convert option to boolean in way that code likes it.
+            boolean breakLines = dontBreakLines == 0;
+
+            int    len43   = len * 4 / 3;
+            byte[] outBuff = new byte[   ( len43 )                      // Main 4:3
+                                       + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding
+                                       + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
+            int d = 0;
+            int e = 0;
+            int len2 = len - 2;
+            int lineLength = 0;
+            for( ; d < len2; d+=3, e+=4 )
+            {
+                encode3to4( source, d+off, 3, outBuff, e );
+
+                lineLength += 4;
+                if( breakLines && lineLength == MAX_LINE_LENGTH )
+                {
+                    outBuff[e+4] = NEW_LINE;
+                    e++;
+                    lineLength = 0;
+                }   // end if: end of line
+            }   // en dfor: each piece of array
+
+            if( d < len )
+            {
+                encode3to4( source, d+off, len - d, outBuff, e );
+                e += 4;
+            }   // end if: some padding needed
+
+
+            // Return value according to relevant encoding.
+            try
+            {
+                return new String( outBuff, 0, e, PREFERRED_ENCODING );
+            }   // end try
+            catch (java.io.UnsupportedEncodingException uue)
+            {
+                return new String( outBuff, 0, e );
+            }   // end catch
+
+        }   // end else: don't compress
+
+    }   // end encodeBytes
+
+
+
+
+
+/* ********  D E C O D I N G   M E T H O D S  ******** */
+
+
+    /**
+     * Decodes four bytes from array <var>source</var>
+     * and writes the resulting bytes (up to three of them)
+     * to <var>destination</var>.
+     * The source and destination arrays can be manipulated
+     * anywhere along their length by specifying
+     * <var>srcOffset</var> and <var>destOffset</var>.
+     * This method does not check to make sure your arrays
+     * are large enough to accomodate <var>srcOffset</var> + 4 for
+     * the <var>source</var> array or <var>destOffset</var> + 3 for
+     * the <var>destination</var> array.
+     * This method returns the actual number of bytes that
+     * were converted from the Base64 encoding.
+     *
+     *
+     * @param source the array to convert
+     * @param srcOffset the index where conversion begins
+     * @param destination the array to hold the conversion
+     * @param destOffset the index where output will be put
+     * @return the number of decoded bytes converted
+     * @since 1.3
+     */
+    private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )
+    {
+        // Example: Dk==
+        if( source[ srcOffset + 2] == EQUALS_SIGN )
+        {
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
+
+            destination[ destOffset ] = (byte)( outBuff >>> 16 );
+            return 1;
+        }
+
+        // Example: DkL=
+        else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
+        {
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );
+
+            destination[ destOffset     ] = (byte)( outBuff >>> 16 );
+            destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );
+            return 2;
+        }
+
+        // Example: DkLE
+        else
+        {
+            try{
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
+            //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)
+                          | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );
+
+
+            destination[ destOffset     ] = (byte)( outBuff >> 16 );
+            destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );
+            destination[ destOffset + 2 ] = (byte)( outBuff       );
+
+            return 3;
+            }catch( Exception e){
+                System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) );
+                System.out.println(""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ) );
+                System.out.println(""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ) );
+                System.out.println(""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ) );
+                return -1;
+            }   //e nd catch
+        }
+    }   // end decodeToBytes
+
+
+
+
+    /**
+     * Very low-level access to decoding ASCII characters in
+     * the form of a byte array. Does not support automatically
+     * gunzipping or any other "fancy" features.
+     *
+     * @param source The Base64 encoded data
+     * @param off    The offset of where to begin decoding
+     * @param len    The length of characters to decode
+     * @return decoded data
+     * @since 1.3
+     */
+    public static byte[] decode( byte[] source, int off, int len )
+    {
+        int    len34   = len * 3 / 4;
+        byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
+        int    outBuffPosn = 0;
+
+        byte[] b4        = new byte[4];
+        int    b4Posn    = 0;
+        int    i         = 0;
+        byte   sbiCrop   = 0;
+        byte   sbiDecode = 0;
+        for( i = off; i < off+len; i++ )
+        {
+            sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
+            sbiDecode = DECODABET[ sbiCrop ];
+
+            if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
+            {
+                if( sbiDecode >= EQUALS_SIGN_ENC )
+                {
+                    b4[ b4Posn++ ] = sbiCrop;
+                    if( b4Posn > 3 )
+                    {
+                        outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
+                        b4Posn = 0;
+
+                        // If that was the equals sign, break out of 'for' loop
+                        if( sbiCrop == EQUALS_SIGN )
+                            break;
+                    }   // end if: quartet built
+
+                }   // end if: equals sign or better
+
+            }   // end if: white space, equals sign or better
+            else
+            {
+                System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
+                return null;
+            }   // end else:
+        }   // each input character
+
+        byte[] out = new byte[ outBuffPosn ];
+        System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
+        return out;
+    }   // end decode
+
+
+
+
+    /**
+     * Decodes data from Base64 notation, automatically
+     * detecting gzip-compressed data and decompressing it.
+     *
+     * @param s the string to decode
+     * @return the decoded data
+     * @since 1.4
+     */
+    public static byte[] decode( String s )
+    {
+        byte[] bytes;
+        try
+        {
+            bytes = s.getBytes( PREFERRED_ENCODING );
+        }   // end try
+        catch( java.io.UnsupportedEncodingException uee )
+        {
+            bytes = s.getBytes();
+        }   // end catch
+        //</change>
+
+        // Decode
+        bytes = decode( bytes, 0, bytes.length );
+
+
+        // Check to see if it's gzip-compressed
+        // GZIP Magic Two-Byte Number: 0x8b1f (35615)
+        if( bytes != null && bytes.length >= 4 )
+        {
+
+            int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
+            if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head )
+            {
+                java.io.ByteArrayInputStream  bais = null;
+                java.util.zip.GZIPInputStream gzis = null;
+                java.io.ByteArrayOutputStream baos = null;
+                byte[] buffer = new byte[2048];
+                int    length = 0;
+
+                try
+                {
+                    baos = new java.io.ByteArrayOutputStream();
+                    bais = new java.io.ByteArrayInputStream( bytes );
+                    gzis = new java.util.zip.GZIPInputStream( bais );
+
+                    while( ( length = gzis.read( buffer ) ) >= 0 )
+                    {
+                        baos.write(buffer,0,length);
+                    }   // end while: reading input
+
+                    // No error? Get new bytes.
+                    bytes = baos.toByteArray();
+
+                }   // end try
+                catch( java.io.IOException e )
+                {
+                    // Just return originally-decoded bytes
+                }   // end catch
+                finally
+                {
+                    try{ baos.close(); } catch( Exception e ){}
+                    try{ gzis.close(); } catch( Exception e ){}
+                    try{ bais.close(); } catch( Exception e ){}
+                }   // end finally
+
+            }   // end if: gzipped
+        }   // end if: bytes.length >= 2
+
+        return bytes;
+    }   // end decode
+
+
+
+
+    /**
+     * Attempts to decode Base64 data and deserialize a Java
+     * Object within. Returns <tt>null</tt> if there was an error.
+     *
+     * @param encodedObject The Base64 data to decode
+     * @return The decoded and deserialized object
+     * @since 1.5
+     */
+    public static Object decodeToObject( String encodedObject )
+    {
+        // Decode and gunzip if necessary
+        byte[] objBytes = decode( encodedObject );
+
+        java.io.ByteArrayInputStream  bais = null;
+        java.io.ObjectInputStream     ois  = null;
+        Object obj = null;
+
+        try
+        {
+            bais = new java.io.ByteArrayInputStream( objBytes );
+            ois  = new java.io.ObjectInputStream( bais );
+
+            obj = ois.readObject();
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            e.printStackTrace();
+            obj = null;
+        }   // end catch
+        catch( java.lang.ClassNotFoundException e )
+        {
+            e.printStackTrace();
+            obj = null;
+        }   // end catch
+        finally
+        {
+            try{ bais.close(); } catch( Exception e ){}
+            try{ ois.close();  } catch( Exception e ){}
+        }   // end finally
+
+        return obj;
+    }   // end decodeObject
+
+
+
+    /**
+     * Convenience method for encoding data to a file.
+     *
+     * @param dataToEncode byte array of data to encode in base64 form
+     * @param filename Filename for saving encoded data
+     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+     *
+     * @since 2.1
+     */
+    public static boolean encodeToFile( byte[] dataToEncode, String filename )
+    {
+        boolean success = false;
+        MgBase64.OutputStream bos = null;
+        try
+        {
+            bos = new MgBase64.OutputStream(
+                      new java.io.FileOutputStream( filename ), MgBase64.ENCODE );
+            bos.write( dataToEncode );
+            success = true;
+        }   // end try
+        catch( java.io.IOException e )
+        {
+
+            success = false;
+        }   // end catch: IOException
+        finally
+        {
+            try{ bos.close(); } catch( Exception e ){}
+        }   // end finally
+
+        return success;
+    }   // end encodeToFile
+
+
+    /**
+     * Convenience method for decoding data to a file.
+     *
+     * @param dataToDecode Base64-encoded data as a string
+     * @param filename Filename for saving decoded data
+     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+     *
+     * @since 2.1
+     */
+    public static boolean decodeToFile( String dataToDecode, String filename )
+    {
+        boolean success = false;
+        MgBase64.OutputStream bos = null;
+        try
+        {
+                bos = new MgBase64.OutputStream(
+                          new java.io.FileOutputStream( filename ), MgBase64.DECODE );
+                bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
+                success = true;
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            success = false;
+        }   // end catch: IOException
+        finally
+        {
+                try{ bos.close(); } catch( Exception e ){}
+        }   // end finally
+
+        return success;
+    }   // end decodeToFile
+
+
+
+
+    /**
+     * Convenience method for reading a base64-encoded
+     * file and decoding it.
+     *
+     * @param filename Filename for reading encoded data
+     * @return decoded byte array or null if unsuccessful
+     *
+     * @since 2.1
+     */
+    public static byte[] decodeFromFile( String filename )
+    {
+        byte[] decodedData = null;
+        MgBase64.InputStream bis = null;
+        try
+        {
+            // Set up some useful variables
+            java.io.File file = new java.io.File( filename );
+            byte[] buffer = null;
+            int length   = 0;
+            int numBytes = 0;
+
+            // Check for size of file
+            if( file.length() > Integer.MAX_VALUE )
+            {
+                System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." );
+                return null;
+            }   // end if: file too big for int index
+            buffer = new byte[ (int)file.length() ];
+
+            // Open a stream
+            bis = new MgBase64.InputStream(
+                      new java.io.BufferedInputStream(
+                      new java.io.FileInputStream( file ) ), MgBase64.DECODE );
+
+            // Read until done
+            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
+                length += numBytes;
+
+            // Save in a variable to return
+            decodedData = new byte[ length ];
+            System.arraycopy( buffer, 0, decodedData, 0, length );
+
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            System.err.println( "Error decoding from file " + filename );
+        }   // end catch: IOException
+        finally
+        {
+            try{ bis.close(); } catch( Exception e) {}
+        }   // end finally
+
+        return decodedData;
+    }   // end decodeFromFile
+
+
+
+    /**
+     * Convenience method for reading a binary file
+     * and base64-encoding it.
+     *
+     * @param filename Filename for reading binary data
+     * @return base64-encoded string or null if unsuccessful
+     *
+     * @since 2.1
+     */
+    public static String encodeFromFile( String filename )
+    {
+        String encodedData = null;
+        MgBase64.InputStream bis = null;
+        try
+        {
+            // Set up some useful variables
+            java.io.File file = new java.io.File( filename );
+            byte[] buffer = new byte[ (int)(file.length() * 1.4) ];
+            int length   = 0;
+            int numBytes = 0;
+
+            // Open a stream
+            bis = new MgBase64.InputStream(
+                      new java.io.BufferedInputStream(
+                      new java.io.FileInputStream( file ) ), MgBase64.ENCODE );
+
+            // Read until done
+            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
+                length += numBytes;
+
+            // Save in a variable to return
+            encodedData = new String( buffer, 0, length, MgBase64.PREFERRED_ENCODING );
+
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            System.err.println( "Error encoding from file " + filename );
+        }   // end catch: IOException
+        finally
+        {
+            try{ bis.close(); } catch( Exception e) {}
+        }   // end finally
+
+        return encodedData;
+        }   // end encodeFromFile
+
+
+
+
+    /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
+
+
+
+    /**
+     * A {@link MgBase64.InputStream} will read data from another
+     * <tt>java.io.InputStream</tt>, given in the constructor,
+     * and encode/decode to/from MgBase64 notation on the fly.
+     *
+     * @see MgBase64
+     * @since 1.3
+     */
+    public static class InputStream extends java.io.FilterInputStream
+    {
+        private boolean encode;         // Encoding or decoding
+        private int     position;       // Current position in the buffer
+        private byte[]  buffer;         // Small buffer holding converted data
+        private int     bufferLength;   // Length of buffer (3 or 4)
+        private int     numSigBytes;    // Number of meaningful bytes in the buffer
+        private int     lineLength;
+        private boolean breakLines;     // Break lines at less than 80 characters
+
+
+        /**
+         * Constructs a {@link MgBase64.InputStream} in DECODE mode.
+         *
+         * @param in the <tt>java.io.InputStream</tt> from which to read data.
+         * @since 1.3
+         */
+        public InputStream( java.io.InputStream in )
+        {
+            this( in, DECODE );
+        }   // end constructor
+
+
+        /**
+         * Constructs a {@link MgBase64.InputStream} in
+         * either ENCODE or DECODE mode.
+         * <p>
+         * Valid options:<pre>
+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+         * </pre>
+         * <p>
+         * Example: <code>new MgBase64.InputStream( in, MgBase64.DECODE )</code>
+         *
+         *
+         * @param in the <tt>java.io.InputStream</tt> from which to read data.
+         * @param options Specified options
+         * @see MgBase64#ENCODE
+         * @see MgBase64#DECODE
+         * @see MgBase64#DONT_BREAK_LINES
+         * @since 2.0
+         */
+        public InputStream( java.io.InputStream in, int options )
+        {
+            super( in );
+            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+            this.encode       = (options & ENCODE) == ENCODE;
+            this.bufferLength = encode ? 4 : 3;
+            this.buffer   = new byte[ bufferLength ];
+            this.position = -1;
+            this.lineLength = 0;
+        }   // end constructor
+
+        /**
+         * Reads enough of the input stream to convert
+         * to/from Base64 and returns the next byte.
+         *
+         * @return next byte
+         * @since 1.3
+         */
+        public int read() throws java.io.IOException
+        {
+            // Do we need to get data?
+            if( position < 0 )
+            {
+                if( encode )
+                {
+                    byte[] b3 = new byte[3];
+                    int numBinaryBytes = 0;
+                    for( int i = 0; i < 3; i++ )
+                    {
+                        try
+                        {
+                            int b = in.read();
+
+                            // If end of stream, b is -1.
+                            if( b >= 0 )
+                            {
+                                b3[i] = (byte)b;
+                                numBinaryBytes++;
+                            }   // end if: not end of stream
+
+                        }   // end try: read
+                        catch( java.io.IOException e )
+                        {
+                            // Only a problem if we got no data at all.
+                            if( i == 0 )
+                                throw e;
+
+                        }   // end catch
+                    }   // end for: each needed input byte
+
+                    if( numBinaryBytes > 0 )
+                    {
+                        encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
+                        position = 0;
+                        numSigBytes = 4;
+                    }   // end if: got data
+                    else
+                    {
+                        return -1;
+                    }   // end else
+                }   // end if: encoding
+
+                // Else decoding
+                else
+                {
+                    byte[] b4 = new byte[4];
+                    int i = 0;
+                    for( i = 0; i < 4; i++ )
+                    {
+                        // Read four "meaningful" bytes:
+                        int b = 0;
+                        do{ b = in.read(); }
+                        while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
+
+                        if( b < 0 )
+                            break; // Reads a -1 if end of stream
+
+                        b4[i] = (byte)b;
+                    }   // end for: each needed input byte
+
+                    if( i == 4 )
+                    {
+                        numSigBytes = decode4to3( b4, 0, buffer, 0 );
+                        position = 0;
+                    }   // end if: got four characters
+                    else if( i == 0 ){
+                        return -1;
+                    }   // end else if: also padded correctly
+                    else
+                    {
+                        // Must have broken out from above.
+                        throw new java.io.IOException( "Improperly padded Base64 input." );
+                    }   // end
+
+                }   // end else: decode
+            }   // end else: get data
+
+            // Got data?
+            if( position >= 0 )
+            {
+                // End of relevant data?
+                if( /*!encode &&*/ position >= numSigBytes )
+                    return -1;
+
+                if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
+                {
+                    lineLength = 0;
+                    return '\n';
+                }   // end if
+                else
+                {
+                    lineLength++;   // This isn't important when decoding
+                                    // but throwing an extra "if" seems
+                                    // just as wasteful.
+
+                    int b = buffer[ position++ ];
+
+                    if( position >= bufferLength )
+                        position = -1;
+
+                    return b & 0xFF; // This is how you "cast" a byte that's
+                                     // intended to be unsigned.
+                }   // end else
+            }   // end if: position >= 0
+
+            // Else error
+            else
+            {
+                // When JDK1.4 is more accepted, use an assertion here.
+                throw new java.io.IOException( "Error in Base64 code reading stream." );
+            }   // end else
+        }   // end read
+
+
+        /**
+         * Calls {@link #read()} repeatedly until the end of stream
+         * is reached or <var>len</var> bytes are read.
+         * Returns number of bytes read into array or -1 if
+         * end of stream is encountered.
+         *
+         * @param dest array to hold values
+         * @param off offset for array
+         * @param len max number of bytes to read into array
+         * @return bytes read into array or -1 if end of stream is encountered.
+         * @since 1.3
+         */
+        public int read( byte[] dest, int off, int len ) throws java.io.IOException
+        {
+            int i;
+            int b;
+            for( i = 0; i < len; i++ )
+            {
+                b = read();
+
+                //if( b < 0 && i == 0 )
+                //    return -1;
+
+                if( b >= 0 )
+                    dest[off + i] = (byte)b;
+                else if( i == 0 )
+                    return -1;
+                else
+                    break; // Out of 'for' loop
+            }   // end for: each byte read
+            return i;
+        }   // end read
+
+    }   // end inner class InputStream
+
+
+
+
+
+
+    /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
+
+
+
+    /**
+     * A {@link MgBase64.OutputStream} will write data to another
+     * <tt>java.io.OutputStream</tt>, given in the constructor,
+     * and encode/decode to/from Base64 notation on the fly.
+     *
+     * @see Base64
+     * @since 1.3
+     */
+    public static class OutputStream extends java.io.FilterOutputStream
+    {
+        private boolean encode;
+        private int     position;
+        private byte[]  buffer;
+        private int     bufferLength;
+        private int     lineLength;
+        private boolean breakLines;
+        private byte[]  b4; // Scratch used in a few places
+        private boolean suspendEncoding;
+
+        /**
+         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
+         *
+         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
+         * @since 1.3
+         */
+        public OutputStream( java.io.OutputStream out )
+        {
+            this( out, ENCODE );
+        }   // end constructor
+
+
+        /**
+         * Constructs a {@link Base64.OutputStream} in
+         * either ENCODE or DECODE mode.
+         * <p>
+         * Valid options:<pre>
+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+         * </pre>
+         * <p>
+         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
+         *
+         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
+         * @param options Specified options.
+         * @see Base64#ENCODE
+         * @see Base64#DECODE
+         * @see Base64#DONT_BREAK_LINES
+         * @since 1.3
+         */
+        public OutputStream( java.io.OutputStream out, int options )
+        {
+            super( out );
+            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+            this.encode       = (options & ENCODE) == ENCODE;
+            this.bufferLength = encode ? 3 : 4;
+            this.buffer       = new byte[ bufferLength ];
+            this.position     = 0;
+            this.lineLength   = 0;
+            this.suspendEncoding = false;
+            this.b4           = new byte[4];
+        }   // end constructor
+
+
+        /**
+         * Writes the byte to the output stream after
+         * converting to/from Base64 notation.
+         * When encoding, bytes are buffered three
+         * at a time before the output stream actually
+         * gets a write() call.
+         * When decoding, bytes are buffered four
+         * at a time.
+         *
+         * @param theByte the byte to write
+         * @since 1.3
+         */
+        public void write(int theByte) throws java.io.IOException
+        {
+            // Encoding suspended?
+            if( suspendEncoding )
+            {
+                super.out.write( theByte );
+                return;
+            }   // end if: supsended
+
+            // Encode?
+            if( encode )
+            {
+                buffer[ position++ ] = (byte)theByte;
+                if( position >= bufferLength )  // Enough to encode.
+                {
+                    out.write( encode3to4( b4, buffer, bufferLength ) );
+
+                    lineLength += 4;
+                    if( breakLines && lineLength >= MAX_LINE_LENGTH )
+                    {
+                        out.write( NEW_LINE );
+                        lineLength = 0;
+                    }   // end if: end of line
+
+                    position = 0;
+                }   // end if: enough to output
+            }   // end if: encoding
+
+            // Else, Decoding
+            else
+            {
+                // Meaningful Base64 character?
+                if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC )
+                {
+                    buffer[ position++ ] = (byte)theByte;
+                    if( position >= bufferLength )  // Enough to output.
+                    {
+                        int len = MgBase64.decode4to3( buffer, 0, b4, 0 );
+                        out.write( b4, 0, len );
+                        //out.write( MgBase64.decode4to3( buffer ) );
+                        position = 0;
+                    }   // end if: enough to output
+                }   // end if: meaningful base64 character
+                else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC )
+                {
+                    throw new java.io.IOException( "Invalid character in Base64 data." );
+                }   // end else: not white space either
+            }   // end else: decoding
+        }   // end write
+
+
+
+        /**
+         * Calls {@link #write(int)} repeatedly until <var>len</var>
+         * bytes are written.
+         *
+         * @param theBytes array from which to read bytes
+         * @param off offset for array
+         * @param len max number of bytes to read into array
+         * @since 1.3
+         */
+        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
+        {
+            // Encoding suspended?
+            if( suspendEncoding )
+            {
+                super.out.write( theBytes, off, len );
+                return;
+            }   // end if: supsended
+
+            for( int i = 0; i < len; i++ )
+            {
+                write( theBytes[ off + i ] );
+            }   // end for: each byte written
+
+        }   // end write
+
+
+
+        /**
+         * Method added by PHIL. [Thanks, PHIL. -Rob]
+         * This pads the buffer without closing the stream.
+         */
+        public void flushBase64() throws java.io.IOException
+        {
+            if( position > 0 )
+            {
+                if( encode )
+                {
+                    out.write( encode3to4( b4, buffer, position ) );
+                    position = 0;
+                }   // end if: encoding
+                else
+                {
+                    throw new java.io.IOException( "Base64 input not properly padded." );
+                }   // end else: decoding
+            }   // end if: buffer partially full
+
+        }   // end flush
+
+
+        /**
+         * Flushes and closes (I think, in the superclass) the stream.
+         *
+         * @since 1.3
+         */
+        public void close() throws java.io.IOException
+        {
+            // 1. Ensure that pending characters are written
+            flushBase64();
+
+            // 2. Actually close the stream
+            // Base class both flushes and closes.
+            super.close();
+
+            buffer = null;
+            out    = null;
+        }   // end close
+
+
+
+        /**
+         * Suspends encoding of the stream.
+         * May be helpful if you need to embed a piece of
+         * base640-encoded data in a stream.
+         *
+         * @since 1.5.1
+         */
+        public void suspendEncoding() throws java.io.IOException
+        {
+            flushBase64();
+            this.suspendEncoding = true;
+        }   // end suspendEncoding
+
+
+        /**
+         * Resumes encoding of the stream.
+         * May be helpful if you need to embed a piece of
+         * base640-encoded data in a stream.
+         *
+         * @since 1.5.1
+         */
+        public void resumeEncoding()
+        {
+            this.suspendEncoding = false;
+        }   // end resumeEncoding
+
+
+
+    }   // end inner class OutputStream
+
+
+}   // end class MgBase64

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/MgLocalizer.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/MgLocalizer.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/MgLocalizer.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,154 @@
+/*
+  Copyright (C) 2004-2011 by Autodesk, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of version 2.1 of the GNU Lesser
+  General Public License as published by the Free Software Foundation.
+
+  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+package org.osgeo.mapguide;
+
+import java.util.*;
+import java.io.*;
+
+public class MgLocalizer
+{
+    protected static String english = "en";
+    protected static String localizationPath = "";
+    protected static Hashtable languages = new Hashtable();
+
+    public static void SetLocalizedFilesPath(String path)
+    {
+        localizationPath = path;
+    }
+
+    public static String Localize(String text, String locale, int os)
+    {
+        String fontSuffix = (os==0? "Windows": (os==1? "Macintosh": "Linux"));
+        Hashtable sb = null;
+        try
+        {
+            sb = GetStringBundle(locale);
+        }
+        catch (Exception e)
+        {
+            return "";
+        }
+        int len = text.length();
+
+        for (int i = 0; i < len; )
+        {
+            int pos1 = text.indexOf("__#", i);
+            if (pos1 != -1)
+            {
+                int pos2 = text.indexOf("#__", pos1 + 3);
+                if (pos2 != -1)
+                {
+                    String id = text.substring(pos1 + 3, pos2);
+                    String locStr;
+                    locStr = (String)sb.get(id.equals("@font") || id.equals("@fontsize") ? id + fontSuffix : id);
+                    if (locStr == null)
+                        locStr = "";
+                    int locLen = locStr.length();
+
+                    String begin, end;
+                    if (pos1 > 0)
+                        begin = text.substring(0, pos1);
+                    else
+                        begin = "";
+                    end = text.substring(pos2 + 3);
+                    text = begin + locStr + end;
+
+                    len = len - 6 - id.length() + locLen;
+                    i = pos1 + locLen;
+                }
+                else
+                    i = len;
+            }
+            else
+                i = len;
+        }
+        return text;
+    }
+
+    public static String GetString(String id, String locale)
+    {
+        Hashtable sb = null;
+        try
+        {
+            sb = GetStringBundle(locale);
+        }
+        catch (Exception e)
+        {
+            return "";
+        }
+        String s = (String)sb.get(id);
+        if (s == null)
+            return "";
+        return s;
+    }
+
+    protected static Hashtable GetStringBundle(String locale)
+    {
+        if (locale.equals(""))
+            locale = english;
+        else
+            locale = locale.toLowerCase();
+
+        if (!languages.containsKey(locale))
+        {
+            BufferedReader in = null;
+            try
+            {
+                File f = new File(localizationPath + locale);
+                if (!f.exists())
+                {
+                    // requested locale is not supported, default to English
+                    if (languages.containsKey(english))
+                        return (Hashtable)languages.get(english);
+                    f = new File(localizationPath + english);
+                }
+                in = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8"));
+                String line;
+                Hashtable sb = new Hashtable();
+                while ((line = in.readLine()) != null)
+                {
+                    line = line.trim();
+                    if (line.equals("") || line.charAt(0) == '#')
+                        continue;
+                    int sep = line.indexOf('=');
+                    if (sep == -1)
+                        continue;
+                    String key = line.substring(0, sep).trim();
+                    if (key.equals(""))
+                        continue;
+                    sb.put(key, line.substring(sep + 1).trim());
+                }
+                languages.put(locale, sb);
+            }
+            catch (Exception e)
+            {
+                return null;
+            }
+            finally
+            {
+                if (in != null)
+                {
+                    try { in.close(); }
+                    catch (Exception e) { }
+                }
+            }
+        }
+        return (Hashtable)languages.get(locale);
+    }
+
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/ObjectFactory.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/ObjectFactory.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/Java/org/osgeo/mapguide/ObjectFactory.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,396 @@
+package org.osgeo.mapguide;
+
+import java.util.*;
+import java.lang.reflect.*;
+
+public class ObjectFactory
+{
+    public static Object createObject(int id, long cptr, boolean ownCptr)
+    {
+        Constructor ctor = (Constructor)classMap.get(new Integer(id));
+        if(ctor == null)
+            return null;
+        try
+        {
+            return ctor.newInstance(new Object[] { new Long(cptr), new Boolean(ownCptr) });
+        }
+        catch(Exception e)
+        {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+
+    protected static Hashtable classMap;
+
+    static
+    {
+        classMap = new Hashtable();
+        try
+        {
+            classMap.put(new Integer(20004), getSWIGCtor("MgAgfReaderWriter"));
+            classMap.put(new Integer(20005), getSWIGCtor("MgAggregateGeometry"));
+            classMap.put(new Integer(1500), getSWIGCtor("MgApplicationException"));
+            classMap.put(new Integer(20006), getSWIGCtor("MgArcSegment"));
+            classMap.put(new Integer(1501), getSWIGCtor("MgArgumentOutOfRangeException"));
+            classMap.put(new Integer(10500), getSWIGCtor("MgArrayTypeMismatchException"));
+            classMap.put(new Integer(30000), getSWIGCtor("MgAuthenticationFailedException"));
+            classMap.put(new Integer(1005), getSWIGCtor("MgBatchPropertyCollection"));
+            classMap.put(new Integer(10252), getSWIGCtor("MgBlobProperty"));
+            classMap.put(new Integer(10253), getSWIGCtor("MgBooleanProperty"));
+            classMap.put(new Integer(10254), getSWIGCtor("MgByteProperty"));
+            classMap.put(new Integer(1250), getSWIGCtor("MgByteReader"));
+            classMap.put(new Integer(1257), getSWIGCtor("MgByteSink"));
+            classMap.put(new Integer(1251), getSWIGCtor("MgByteSource"));
+            classMap.put(new Integer(11750), getSWIGCtor("MgClassDefinition"));
+            classMap.put(new Integer(11780), getSWIGCtor("MgClassDefinitionCollection"));
+            classMap.put(new Integer(1502), getSWIGCtor("MgClassNotFoundException"));
+            classMap.put(new Integer(10255), getSWIGCtor("MgClobProperty"));
+            classMap.put(new Integer(10250), getSWIGCtor("MgColor"));
+            classMap.put(new Integer(1503), getSWIGCtor("MgConfigurationException"));
+            classMap.put(new Integer(1504), getSWIGCtor("MgConfigurationLoadFailedException"));
+            classMap.put(new Integer(1505), getSWIGCtor("MgConfigurationSaveFailedException"));
+            classMap.put(new Integer(30001), getSWIGCtor("MgConnectionFailedException"));
+            classMap.put(new Integer(30002), getSWIGCtor("MgConnectionNotOpenException"));
+            classMap.put(new Integer(20003), getSWIGCtor("MgCoordinate"));
+            classMap.put(new Integer(20009), getSWIGCtor("MgCoordinateIterator"));
+            classMap.put(new Integer(20500), getSWIGCtor("MgCoordinateSystem"));
+            classMap.put(new Integer(20504), getSWIGCtor("MgCoordinateSystemCatalog"));
+            classMap.put(new Integer(20506), getSWIGCtor("MgCoordinateSystemCategory"));
+            classMap.put(new Integer(20510), getSWIGCtor("MgCoordinateSystemCategoryDictionary"));
+            classMap.put(new Integer(21000), getSWIGCtor("MgCoordinateSystemComputationFailedException"));
+            classMap.put(new Integer(21001), getSWIGCtor("MgCoordinateSystemConversionFailedException"));
+            classMap.put(new Integer(20507), getSWIGCtor("MgCoordinateSystemDatum"));
+            classMap.put(new Integer(20512), getSWIGCtor("MgCoordinateSystemDatumDictionary"));
+            classMap.put(new Integer(20511), getSWIGCtor("MgCoordinateSystemDictionary"));
+            classMap.put(new Integer(20521), getSWIGCtor("MgCoordinateSystemDictionaryUtility"));
+            classMap.put(new Integer(20509), getSWIGCtor("MgCoordinateSystemEllipsoid"));
+            classMap.put(new Integer(20513), getSWIGCtor("MgCoordinateSystemEllipsoidDictionary"));
+            classMap.put(new Integer(20514), getSWIGCtor("MgCoordinateSystemEnum"));
+            classMap.put(new Integer(20515), getSWIGCtor("MgCoordinateSystemEnumInteger32"));
+            classMap.put(new Integer(20501), getSWIGCtor("MgCoordinateSystemFactory"));
+            classMap.put(new Integer(20516), getSWIGCtor("MgCoordinateSystemFilter"));
+            classMap.put(new Integer(20517), getSWIGCtor("MgCoordinateSystemFilterInteger32"));
+            classMap.put(new Integer(20505), getSWIGCtor("MgCoordinateSystemFormatConverter"));
+            classMap.put(new Integer(20542), getSWIGCtor("MgCoordinateSystemGeodeticAnalyticalTransformDefParams"));
+            classMap.put(new Integer(20543), getSWIGCtor("MgCoordinateSystemGeodeticInterpolationTransformDefParams"));
+            classMap.put(new Integer(20544), getSWIGCtor("MgCoordinateSystemGeodeticMultipleRegressionTransformDefParams"));
+            classMap.put(new Integer(20533), getSWIGCtor("MgCoordinateSystemGeodeticPath"));
+            classMap.put(new Integer(20535), getSWIGCtor("MgCoordinateSystemGeodeticPathDictionary"));
+            classMap.put(new Integer(20534), getSWIGCtor("MgCoordinateSystemGeodeticPathElement"));
+            classMap.put(new Integer(20508), getSWIGCtor("MgCoordinateSystemGeodeticTransformation"));
+            classMap.put(new Integer(20536), getSWIGCtor("MgCoordinateSystemGeodeticTransformDef"));
+            classMap.put(new Integer(20540), getSWIGCtor("MgCoordinateSystemGeodeticTransformDefDictionary"));
+            classMap.put(new Integer(20545), getSWIGCtor("MgCoordinateSystemGeodeticTransformGridFile"));
+            classMap.put(new Integer(20532), getSWIGCtor("MgCoordinateSystemGridBase"));
+            classMap.put(new Integer(20524), getSWIGCtor("MgCoordinateSystemGridBoundary"));
+            classMap.put(new Integer(20529), getSWIGCtor("MgCoordinateSystemGridLine"));
+            classMap.put(new Integer(20526), getSWIGCtor("MgCoordinateSystemGridLineCollection"));
+            classMap.put(new Integer(20530), getSWIGCtor("MgCoordinateSystemGridRegion"));
+            classMap.put(new Integer(20527), getSWIGCtor("MgCoordinateSystemGridRegionCollection"));
+            classMap.put(new Integer(20525), getSWIGCtor("MgCoordinateSystemGridSpecification"));
+            classMap.put(new Integer(20531), getSWIGCtor("MgCoordinateSystemGridTick"));
+            classMap.put(new Integer(20528), getSWIGCtor("MgCoordinateSystemGridTickCollection"));
+            classMap.put(new Integer(21002), getSWIGCtor("MgCoordinateSystemInitializationFailedException"));
+            classMap.put(new Integer(21003), getSWIGCtor("MgCoordinateSystemLoadFailedException"));
+            classMap.put(new Integer(20518), getSWIGCtor("MgCoordinateSystemMathComparator"));
+            classMap.put(new Integer(20502), getSWIGCtor("MgCoordinateSystemMeasure"));
+            classMap.put(new Integer(21004), getSWIGCtor("MgCoordinateSystemMeasureFailedException"));
+            classMap.put(new Integer(20522), getSWIGCtor("MgCoordinateSystemMgrs"));
+            classMap.put(new Integer(20520), getSWIGCtor("MgCoordinateSystemProjectionInformation"));
+            classMap.put(new Integer(20503), getSWIGCtor("MgCoordinateSystemTransform"));
+            classMap.put(new Integer(21005), getSWIGCtor("MgCoordinateSystemTransformFailedException"));
+            classMap.put(new Integer(20519), getSWIGCtor("MgCoordinateSystemUnitInformation"));
+            classMap.put(new Integer(20048), getSWIGCtor("MgCoordinateXY"));
+            classMap.put(new Integer(20051), getSWIGCtor("MgCoordinateXYM"));
+            classMap.put(new Integer(20049), getSWIGCtor("MgCoordinateXYZ"));
+            classMap.put(new Integer(20050), getSWIGCtor("MgCoordinateXYZM"));
+            classMap.put(new Integer(20010), getSWIGCtor("MgCurve"));
+            classMap.put(new Integer(20011), getSWIGCtor("MgCurvePolygon"));
+            classMap.put(new Integer(20047), getSWIGCtor("MgCurvePolygonCollection"));
+            classMap.put(new Integer(20012), getSWIGCtor("MgCurveRing"));
+            classMap.put(new Integer(20052), getSWIGCtor("MgCurveRingCollection"));
+            classMap.put(new Integer(20013), getSWIGCtor("MgCurveSegment"));
+            classMap.put(new Integer(20041), getSWIGCtor("MgCurveSegmentCollection"));
+            classMap.put(new Integer(20014), getSWIGCtor("MgCurveString"));
+            classMap.put(new Integer(20043), getSWIGCtor("MgCurveStringCollection"));
+            classMap.put(new Integer(11751), getSWIGCtor("MgDataPropertyDefinition"));
+            classMap.put(new Integer(11773), getSWIGCtor("MgDataReader"));
+            classMap.put(new Integer(1256), getSWIGCtor("MgDateTime"));
+            classMap.put(new Integer(1506), getSWIGCtor("MgDateTimeException"));
+            classMap.put(new Integer(10256), getSWIGCtor("MgDateTimeProperty"));
+            classMap.put(new Integer(30003), getSWIGCtor("MgDbException"));
+            classMap.put(new Integer(30004), getSWIGCtor("MgDbXmlException"));
+            classMap.put(new Integer(1507), getSWIGCtor("MgDecryptionException"));
+            classMap.put(new Integer(11775), getSWIGCtor("MgDeleteFeatures"));
+            classMap.put(new Integer(1508), getSWIGCtor("MgDirectoryNotFoundException"));
+            classMap.put(new Integer(1000), getSWIGCtor("MgDisposableCollection"));
+            classMap.put(new Integer(1509), getSWIGCtor("MgDivideByZeroException"));
+            classMap.put(new Integer(1510), getSWIGCtor("MgDomainException"));
+            classMap.put(new Integer(10257), getSWIGCtor("MgDoubleProperty"));
+            classMap.put(new Integer(30700), getSWIGCtor("MgDrawingService"));
+            classMap.put(new Integer(1511), getSWIGCtor("MgDuplicateDirectoryException"));
+            classMap.put(new Integer(1512), getSWIGCtor("MgDuplicateFileException"));
+            classMap.put(new Integer(30005), getSWIGCtor("MgDuplicateGroupException"));
+            classMap.put(new Integer(30006), getSWIGCtor("MgDuplicateNameException"));
+            classMap.put(new Integer(1513), getSWIGCtor("MgDuplicateObjectException"));
+            classMap.put(new Integer(30007), getSWIGCtor("MgDuplicateParameterException"));
+            classMap.put(new Integer(30008), getSWIGCtor("MgDuplicateRepositoryException"));
+            classMap.put(new Integer(10501), getSWIGCtor("MgDuplicateResourceDataException"));
+            classMap.put(new Integer(10502), getSWIGCtor("MgDuplicateResourceException"));
+            classMap.put(new Integer(30009), getSWIGCtor("MgDuplicateRoleException"));
+            classMap.put(new Integer(30010), getSWIGCtor("MgDuplicateServerException"));
+            classMap.put(new Integer(30011), getSWIGCtor("MgDuplicateSessionException"));
+            classMap.put(new Integer(30012), getSWIGCtor("MgDuplicateUserException"));
+            classMap.put(new Integer(30013), getSWIGCtor("MgDwfException"));
+            classMap.put(new Integer(30014), getSWIGCtor("MgDwfSectionNotFoundException"));
+            classMap.put(new Integer(30015), getSWIGCtor("MgDwfSectionResourceNotFoundException"));
+            classMap.put(new Integer(30901), getSWIGCtor("MgDwfVersion"));
+            classMap.put(new Integer(10503), getSWIGCtor("MgEmptyFeatureSetException"));
+            classMap.put(new Integer(1514), getSWIGCtor("MgEncryptionException"));
+            classMap.put(new Integer(30016), getSWIGCtor("MgEndOfStreamException"));
+            classMap.put(new Integer(20001), getSWIGCtor("MgEnvelope"));
+            classMap.put(new Integer(1515), getSWIGCtor("MgException"));
+            classMap.put(new Integer(10504), getSWIGCtor("MgFdoException"));
+            classMap.put(new Integer(11772), getSWIGCtor("MgFeatureAggregateOptions"));
+            classMap.put(new Integer(11774), getSWIGCtor("MgFeatureCommandCollection"));
+            classMap.put(new Integer(31001), getSWIGCtor("MgFeatureInformation"));
+            classMap.put(new Integer(11764), getSWIGCtor("MgFeatureProperty"));
+            classMap.put(new Integer(11771), getSWIGCtor("MgFeatureQueryOptions"));
+            classMap.put(new Integer(11753), getSWIGCtor("MgFeatureReader"));
+            classMap.put(new Integer(11778), getSWIGCtor("MgFeatureSchema"));
+            classMap.put(new Integer(11779), getSWIGCtor("MgFeatureSchemaCollection"));
+            classMap.put(new Integer(11754), getSWIGCtor("MgFeatureService"));
+            classMap.put(new Integer(10505), getSWIGCtor("MgFeatureServiceException"));
+            classMap.put(new Integer(11786), getSWIGCtor("MgFileFeatureSourceParams"));
+            classMap.put(new Integer(1516), getSWIGCtor("MgFileIoException"));
+            classMap.put(new Integer(1517), getSWIGCtor("MgFileNotFoundException"));
+            classMap.put(new Integer(20016), getSWIGCtor("MgGeometricEntity"));
+            classMap.put(new Integer(11756), getSWIGCtor("MgGeometricPropertyDefinition"));
+            classMap.put(new Integer(20019), getSWIGCtor("MgGeometry"));
+            classMap.put(new Integer(20020), getSWIGCtor("MgGeometryCollection"));
+            classMap.put(new Integer(20021), getSWIGCtor("MgGeometryComponent"));
+            classMap.put(new Integer(21006), getSWIGCtor("MgGeometryException"));
+            classMap.put(new Integer(20002), getSWIGCtor("MgGeometryFactory"));
+            classMap.put(new Integer(11758), getSWIGCtor("MgGeometryProperty"));
+            classMap.put(new Integer(11785), getSWIGCtor("MgGeometryTypeInfo"));
+            classMap.put(new Integer(30018), getSWIGCtor("MgGroupNotFoundException"));
+            classMap.put(new Integer(11782), getSWIGCtor("MgGwsFeatureReader"));
+            classMap.put(new Integer(40000), getSWIGCtor("MgHttpHeader"));
+            classMap.put(new Integer(40006), getSWIGCtor("MgHttpPrimitiveValue"));
+            classMap.put(new Integer(40004), getSWIGCtor("MgHttpRequest"));
+            classMap.put(new Integer(40002), getSWIGCtor("MgHttpRequestMetadata"));
+            classMap.put(new Integer(40001), getSWIGCtor("MgHttpRequestParam"));
+            classMap.put(new Integer(40005), getSWIGCtor("MgHttpResponse"));
+            classMap.put(new Integer(40003), getSWIGCtor("MgHttpResult"));
+            classMap.put(new Integer(1518), getSWIGCtor("MgIndexOutOfRangeException"));
+            classMap.put(new Integer(11776), getSWIGCtor("MgInsertFeatures"));
+            classMap.put(new Integer(10258), getSWIGCtor("MgInt16Property"));
+            classMap.put(new Integer(10259), getSWIGCtor("MgInt32Property"));
+            classMap.put(new Integer(10260), getSWIGCtor("MgInt64Property"));
+            classMap.put(new Integer(10000), getSWIGCtor("MgIntCollection"));
+            classMap.put(new Integer(1519), getSWIGCtor("MgInvalidArgumentException"));
+            classMap.put(new Integer(1520), getSWIGCtor("MgInvalidCastException"));
+            classMap.put(new Integer(21007), getSWIGCtor("MgInvalidCoordinateSystemException"));
+            classMap.put(new Integer(21008), getSWIGCtor("MgInvalidCoordinateSystemTypeException"));
+            classMap.put(new Integer(21009), getSWIGCtor("MgInvalidCoordinateSystemUnitsException"));
+            classMap.put(new Integer(30019), getSWIGCtor("MgInvalidDwfPackageException"));
+            classMap.put(new Integer(30020), getSWIGCtor("MgInvalidDwfSectionException"));
+            classMap.put(new Integer(30021), getSWIGCtor("MgInvalidFeatureSourceException"));
+            classMap.put(new Integer(30022), getSWIGCtor("MgInvalidIpAddressException"));
+            classMap.put(new Integer(30023), getSWIGCtor("MgInvalidLicenseException"));
+            classMap.put(new Integer(30024), getSWIGCtor("MgInvalidLogEntryException"));
+            classMap.put(new Integer(10507), getSWIGCtor("MgInvalidMapDefinitionException"));
+            classMap.put(new Integer(1522), getSWIGCtor("MgInvalidOperationException"));
+            classMap.put(new Integer(30026), getSWIGCtor("MgInvalidPasswordException"));
+            classMap.put(new Integer(30027), getSWIGCtor("MgInvalidPrintLayoutFontSizeUnitsException"));
+            classMap.put(new Integer(30028), getSWIGCtor("MgInvalidPrintLayoutPositionUnitsException"));
+            classMap.put(new Integer(30029), getSWIGCtor("MgInvalidPrintLayoutSizeUnitsException"));
+            classMap.put(new Integer(1523), getSWIGCtor("MgInvalidPropertyTypeException"));
+            classMap.put(new Integer(10508), getSWIGCtor("MgInvalidRepositoryNameException"));
+            classMap.put(new Integer(10509), getSWIGCtor("MgInvalidRepositoryTypeException"));
+            classMap.put(new Integer(10510), getSWIGCtor("MgInvalidResourceDataNameException"));
+            classMap.put(new Integer(10511), getSWIGCtor("MgInvalidResourceDataTypeException"));
+            classMap.put(new Integer(10512), getSWIGCtor("MgInvalidResourceNameException"));
+            classMap.put(new Integer(10513), getSWIGCtor("MgInvalidResourcePathException"));
+            classMap.put(new Integer(10514), getSWIGCtor("MgInvalidResourcePreProcessingTypeException"));
+            classMap.put(new Integer(10515), getSWIGCtor("MgInvalidResourceTypeException"));
+            classMap.put(new Integer(30031), getSWIGCtor("MgInvalidServerNameException"));
+            classMap.put(new Integer(1524), getSWIGCtor("MgInvalidStreamHeaderException"));
+            classMap.put(new Integer(1525), getSWIGCtor("MgIoException"));
+            classMap.put(new Integer(31300), getSWIGCtor("MgKmlService"));
+            classMap.put(new Integer(30501), getSWIGCtor("MgLayer"));
+            classMap.put(new Integer(12003), getSWIGCtor("MgLayerBase"));
+            classMap.put(new Integer(12002), getSWIGCtor("MgLayerCollection"));
+            classMap.put(new Integer(12001), getSWIGCtor("MgLayerGroup"));
+            classMap.put(new Integer(12004), getSWIGCtor("MgLayerGroupCollection"));
+            classMap.put(new Integer(10517), getSWIGCtor("MgLayerNotFoundException"));
+            classMap.put(new Integer(30904), getSWIGCtor("MgLayout"));
+            classMap.put(new Integer(1526), getSWIGCtor("MgLengthException"));
+            classMap.put(new Integer(30032), getSWIGCtor("MgLicenseException"));
+            classMap.put(new Integer(30033), getSWIGCtor("MgLicenseExpiredException"));
+            classMap.put(new Integer(20023), getSWIGCtor("MgLinearRing"));
+            classMap.put(new Integer(20053), getSWIGCtor("MgLinearRingCollection"));
+            classMap.put(new Integer(20024), getSWIGCtor("MgLinearSegment"));
+            classMap.put(new Integer(20042), getSWIGCtor("MgLineString"));
+            classMap.put(new Integer(20044), getSWIGCtor("MgLineStringCollection"));
+            classMap.put(new Integer(1527), getSWIGCtor("MgLogicException"));
+            classMap.put(new Integer(11766), getSWIGCtor("MgLongTransactionReader"));
+            classMap.put(new Integer(30500), getSWIGCtor("MgMap"));
+            classMap.put(new Integer(12000), getSWIGCtor("MgMapBase"));
+            classMap.put(new Integer(12005), getSWIGCtor("MgMapCollection"));
+            classMap.put(new Integer(30900), getSWIGCtor("MgMappingService"));
+            classMap.put(new Integer(30905), getSWIGCtor("MgMapPlot"));
+            classMap.put(new Integer(30906), getSWIGCtor("MgMapPlotCollection"));
+            classMap.put(new Integer(20029), getSWIGCtor("MgMultiCurvePolygon"));
+            classMap.put(new Integer(20030), getSWIGCtor("MgMultiCurveString"));
+            classMap.put(new Integer(20031), getSWIGCtor("MgMultiGeometry"));
+            classMap.put(new Integer(20032), getSWIGCtor("MgMultiLineString"));
+            classMap.put(new Integer(20033), getSWIGCtor("MgMultiPoint"));
+            classMap.put(new Integer(20034), getSWIGCtor("MgMultiPolygon"));
+            classMap.put(new Integer(1528), getSWIGCtor("MgNotFiniteNumberException"));
+            classMap.put(new Integer(1529), getSWIGCtor("MgNotImplementedException"));
+            classMap.put(new Integer(1530), getSWIGCtor("MgNullArgumentException"));
+            classMap.put(new Integer(1531), getSWIGCtor("MgNullPropertyValueException"));
+            classMap.put(new Integer(1532), getSWIGCtor("MgNullReferenceException"));
+            classMap.put(new Integer(1533), getSWIGCtor("MgObjectNotFoundException"));
+            classMap.put(new Integer(11759), getSWIGCtor("MgObjectPropertyDefinition"));
+            classMap.put(new Integer(30035), getSWIGCtor("MgOperationProcessingException"));
+            classMap.put(new Integer(1534), getSWIGCtor("MgOutOfMemoryException"));
+            classMap.put(new Integer(1535), getSWIGCtor("MgOutOfRangeException"));
+            classMap.put(new Integer(1536), getSWIGCtor("MgOverflowException"));
+            classMap.put(new Integer(30604), getSWIGCtor("MgPackageStatusInformation"));
+            classMap.put(new Integer(11788), getSWIGCtor("MgParameter"));
+            classMap.put(new Integer(10004), getSWIGCtor("MgParameterCollection"));
+            classMap.put(new Integer(30036), getSWIGCtor("MgParameterNotFoundException"));
+            classMap.put(new Integer(30037), getSWIGCtor("MgPathTooLongException"));
+            classMap.put(new Integer(1537), getSWIGCtor("MgPlatformNotSupportedException"));
+            classMap.put(new Integer(30902), getSWIGCtor("MgPlotSpecification"));
+            classMap.put(new Integer(20000), getSWIGCtor("MgPoint"));
+            classMap.put(new Integer(20045), getSWIGCtor("MgPointCollection"));
+            classMap.put(new Integer(20035), getSWIGCtor("MgPolygon"));
+            classMap.put(new Integer(20046), getSWIGCtor("MgPolygonCollection"));
+            classMap.put(new Integer(30039), getSWIGCtor("MgPortNotAvailableException"));
+            classMap.put(new Integer(30040), getSWIGCtor("MgPrintToScaleModeNotSelectedException"));
+            classMap.put(new Integer(31400), getSWIGCtor("MgProfilingService"));
+            classMap.put(new Integer(2000), getSWIGCtor("MgProperty"));
+            classMap.put(new Integer(1002), getSWIGCtor("MgPropertyCollection"));
+            classMap.put(new Integer(2002), getSWIGCtor("MgPropertyDefinition"));
+            classMap.put(new Integer(10001), getSWIGCtor("MgPropertyDefinitionCollection"));
+            classMap.put(new Integer(11769), getSWIGCtor("MgRaster"));
+            classMap.put(new Integer(11770), getSWIGCtor("MgRasterProperty"));
+            classMap.put(new Integer(11768), getSWIGCtor("MgRasterPropertyDefinition"));
+            classMap.put(new Integer(12006), getSWIGCtor("MgReadOnlyLayerCollection"));
+            classMap.put(new Integer(20037), getSWIGCtor("MgRegion"));
+            classMap.put(new Integer(31002), getSWIGCtor("MgRenderingOptions"));
+            classMap.put(new Integer(31000), getSWIGCtor("MgRenderingService"));
+            classMap.put(new Integer(30041), getSWIGCtor("MgRepositoryCreationFailedException"));
+            classMap.put(new Integer(30042), getSWIGCtor("MgRepositoryNotFoundException"));
+            classMap.put(new Integer(30043), getSWIGCtor("MgRepositoryNotOpenException"));
+            classMap.put(new Integer(30044), getSWIGCtor("MgRepositoryOpenFailedException"));
+            classMap.put(new Integer(11526), getSWIGCtor("MgResource"));
+            classMap.put(new Integer(10518), getSWIGCtor("MgResourceBusyException"));
+            classMap.put(new Integer(10519), getSWIGCtor("MgResourceDataNotFoundException"));
+            classMap.put(new Integer(11500), getSWIGCtor("MgResourceIdentifier"));
+            classMap.put(new Integer(10520), getSWIGCtor("MgResourceNotFoundException"));
+            classMap.put(new Integer(11501), getSWIGCtor("MgResourceService"));
+            classMap.put(new Integer(1538), getSWIGCtor("MgResourcesException"));
+            classMap.put(new Integer(1539), getSWIGCtor("MgResourcesLoadFailedException"));
+            classMap.put(new Integer(1540), getSWIGCtor("MgResourceTagNotFoundException"));
+            classMap.put(new Integer(20038), getSWIGCtor("MgRing"));
+            classMap.put(new Integer(30045), getSWIGCtor("MgRoleNotFoundException"));
+            classMap.put(new Integer(1541), getSWIGCtor("MgRuntimeException"));
+            classMap.put(new Integer(30502), getSWIGCtor("MgSelection"));
+            classMap.put(new Integer(12007), getSWIGCtor("MgSelectionBase"));
+            classMap.put(new Integer(30607), getSWIGCtor("MgServerAdmin"));
+            classMap.put(new Integer(30046), getSWIGCtor("MgServerNotFoundException"));
+            classMap.put(new Integer(30047), getSWIGCtor("MgServerNotOnlineException"));
+            classMap.put(new Integer(11251), getSWIGCtor("MgService"));
+            classMap.put(new Integer(10521), getSWIGCtor("MgServiceNotAvailableException"));
+            classMap.put(new Integer(10522), getSWIGCtor("MgServiceNotSupportedException"));
+            classMap.put(new Integer(30048), getSWIGCtor("MgSessionExpiredException"));
+            classMap.put(new Integer(30052), getSWIGCtor("MgSessionNotFoundException"));
+            classMap.put(new Integer(10261), getSWIGCtor("MgSingleProperty"));
+            classMap.put(new Integer(30605), getSWIGCtor("MgSite"));
+            classMap.put(new Integer(30601), getSWIGCtor("MgSiteConnection"));
+            classMap.put(new Integer(30608), getSWIGCtor("MgSiteInfo"));
+            classMap.put(new Integer(11761), getSWIGCtor("MgSpatialContextReader"));
+            classMap.put(new Integer(11762), getSWIGCtor("MgSqlDataReader"));
+            classMap.put(new Integer(1542), getSWIGCtor("MgStreamIoException"));
+            classMap.put(new Integer(1003), getSWIGCtor("MgStringCollection"));
+            classMap.put(new Integer(2001), getSWIGCtor("MgStringProperty"));
+            classMap.put(new Integer(10003), getSWIGCtor("MgStringPropertyCollection"));
+            classMap.put(new Integer(1543), getSWIGCtor("MgSystemException"));
+            classMap.put(new Integer(1544), getSWIGCtor("MgTemporaryFileNotAvailableException"));
+            classMap.put(new Integer(1545), getSWIGCtor("MgThirdPartyException"));
+            classMap.put(new Integer(31200), getSWIGCtor("MgTileService"));
+            classMap.put(new Integer(11787), getSWIGCtor("MgTransaction"));
+            classMap.put(new Integer(30049), getSWIGCtor("MgUnauthorizedAccessException"));
+            classMap.put(new Integer(1547), getSWIGCtor("MgUnclassifiedException"));
+            classMap.put(new Integer(1548), getSWIGCtor("MgUnderflowException"));
+            classMap.put(new Integer(30056), getSWIGCtor("MgUnknownTileProviderException"));
+            classMap.put(new Integer(30057), getSWIGCtor("MgUnsupportedTileProviderException"));
+            classMap.put(new Integer(11777), getSWIGCtor("MgUpdateFeatures"));
+            classMap.put(new Integer(30050), getSWIGCtor("MgUriFormatException"));
+            classMap.put(new Integer(30606), getSWIGCtor("MgUserInformation"));
+            classMap.put(new Integer(10523), getSWIGCtor("MgUserNotFoundException"));
+            classMap.put(new Integer(11257), getSWIGCtor("MgWarnings"));
+            classMap.put(new Integer(50005), getSWIGCtor("MgWebBufferCommand"));
+            classMap.put(new Integer(50000), getSWIGCtor("MgWebCommand"));
+            classMap.put(new Integer(50012), getSWIGCtor("MgWebCommandCollection"));
+            classMap.put(new Integer(50015), getSWIGCtor("MgWebCommandWidget"));
+            classMap.put(new Integer(50025), getSWIGCtor("MgWebContextMenu"));
+            classMap.put(new Integer(50016), getSWIGCtor("MgWebFlyoutWidget"));
+            classMap.put(new Integer(50009), getSWIGCtor("MgWebGetPrintablePageCommand"));
+            classMap.put(new Integer(50011), getSWIGCtor("MgWebHelpCommand"));
+            classMap.put(new Integer(50022), getSWIGCtor("MgWebInformationPane"));
+            classMap.put(new Integer(50003), getSWIGCtor("MgWebInvokeScriptCommand"));
+            classMap.put(new Integer(50004), getSWIGCtor("MgWebInvokeUrlCommand"));
+            classMap.put(new Integer(50026), getSWIGCtor("MgWebLayout"));
+            classMap.put(new Integer(50008), getSWIGCtor("MgWebMeasureCommand"));
+            classMap.put(new Integer(50007), getSWIGCtor("MgWebPrintCommand"));
+            classMap.put(new Integer(50002), getSWIGCtor("MgWebSearchCommand"));
+            classMap.put(new Integer(50006), getSWIGCtor("MgWebSelectWithinCommand"));
+            classMap.put(new Integer(50014), getSWIGCtor("MgWebSeparatorWidget"));
+            classMap.put(new Integer(50023), getSWIGCtor("MgWebTaskBar"));
+            classMap.put(new Integer(50017), getSWIGCtor("MgWebTaskBarWidget"));
+            classMap.put(new Integer(50024), getSWIGCtor("MgWebTaskPane"));
+            classMap.put(new Integer(50021), getSWIGCtor("MgWebToolBar"));
+            classMap.put(new Integer(50019), getSWIGCtor("MgWebUiPane"));
+            classMap.put(new Integer(50020), getSWIGCtor("MgWebUiSizablePane"));
+            classMap.put(new Integer(50001), getSWIGCtor("MgWebUiTargetCommand"));
+            classMap.put(new Integer(50010), getSWIGCtor("MgWebViewOptionsCommand"));
+            classMap.put(new Integer(50013), getSWIGCtor("MgWebWidget"));
+            classMap.put(new Integer(50018), getSWIGCtor("MgWebWidgetCollection"));
+            classMap.put(new Integer(20040), getSWIGCtor("MgWktReaderWriter"));
+            classMap.put(new Integer(1549), getSWIGCtor("MgXmlException"));
+            classMap.put(new Integer(1550), getSWIGCtor("MgXmlParserException"));
+
+        }
+        catch(Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    private static Constructor getSWIGCtor(String className) throws ClassNotFoundException, Exception
+    {
+        Constructor swigCtor = null;
+        Constructor[] cons = Class.forName("org.osgeo.mapguide." + className).getDeclaredConstructors();
+        for (int i = 0; i < cons.length; i++)
+        {
+            Class[] parameterTypes = cons[i].getParameterTypes();
+            if (parameterTypes.length == 2 && parameterTypes[0].equals(Long.TYPE) && parameterTypes[1].equals(Boolean.TYPE))
+            {
+                swigCtor = cons[i];
+                swigCtor.setAccessible(true); //This ctor will be protected, so we need to make it accessible
+            }
+        }
+        if (swigCtor == null)
+        {
+            throw new Exception("Could not find the expected internal SWIG constructor for class: " + className);
+        }
+        return swigCtor;
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/DotNet.sln
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/DotNet.sln	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/DotNet.sln	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,50 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26430.13
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2531ADAC-3A0B-4951-A1A6-DC9EA9F4ECEB}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{724ED7AA-2728-4598-8D46-3FB8933E1822}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestCommon", "src\TestCommon\TestCommon.csproj", "{75850376-F5CC-48E2-A527-79C9613A04DA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestMapGuideApi", "src\TestMapGuideApi\TestMapGuideApi.csproj", "{B1F5B09F-FEBF-436F-8951-D315B603BBF4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestRunner", "src\TestRunner\TestRunner.csproj", "{4DCFCFDB-3698-45B6-908E-19DA1CFF56FD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestMisc", "src\TestMisc\TestMisc.csproj", "{2B00F5A5-EC6B-4413-B5D0-22A746938E69}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{75850376-F5CC-48E2-A527-79C9613A04DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{75850376-F5CC-48E2-A527-79C9613A04DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{75850376-F5CC-48E2-A527-79C9613A04DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{75850376-F5CC-48E2-A527-79C9613A04DA}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B1F5B09F-FEBF-436F-8951-D315B603BBF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B1F5B09F-FEBF-436F-8951-D315B603BBF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B1F5B09F-FEBF-436F-8951-D315B603BBF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B1F5B09F-FEBF-436F-8951-D315B603BBF4}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4DCFCFDB-3698-45B6-908E-19DA1CFF56FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4DCFCFDB-3698-45B6-908E-19DA1CFF56FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4DCFCFDB-3698-45B6-908E-19DA1CFF56FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4DCFCFDB-3698-45B6-908E-19DA1CFF56FD}.Release|Any CPU.Build.0 = Release|Any CPU
+		{2B00F5A5-EC6B-4413-B5D0-22A746938E69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{2B00F5A5-EC6B-4413-B5D0-22A746938E69}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{2B00F5A5-EC6B-4413-B5D0-22A746938E69}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{2B00F5A5-EC6B-4413-B5D0-22A746938E69}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{75850376-F5CC-48E2-A527-79C9613A04DA} = {2531ADAC-3A0B-4951-A1A6-DC9EA9F4ECEB}
+		{B1F5B09F-FEBF-436F-8951-D315B603BBF4} = {2531ADAC-3A0B-4951-A1A6-DC9EA9F4ECEB}
+		{4DCFCFDB-3698-45B6-908E-19DA1CFF56FD} = {2531ADAC-3A0B-4951-A1A6-DC9EA9F4ECEB}
+		{2B00F5A5-EC6B-4413-B5D0-22A746938E69} = {2531ADAC-3A0B-4951-A1A6-DC9EA9F4ECEB}
+	EndGlobalSection
+EndGlobal

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/FullFramework.sln
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/FullFramework.sln	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/FullFramework.sln	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,61 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26124.0
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestMiscFull", "src\TestMiscFull\TestMiscFull.csproj", "{27285BB5-37B4-42DD-AB2C-CCD91824D044}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestCommonFull", "src\TestCommonFull\TestCommonFull.csproj", "{EB8713C5-0284-47C5-950A-90E4EB326669}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestMapGuideApiFull", "src\TestMapGuideApiFull\TestMapGuideApiFull.csproj", "{6584C02F-080F-425D-BE53-9F72F7BE2897}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestRunnerFull", "src\TestRunnerFull\TestRunnerFull.csproj", "{757BA8EB-065F-47A7-834D-025C1559E2A5}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{27285BB5-37B4-42DD-AB2C-CCD91824D044}.Debug|x64.ActiveCfg = Debug|x64
+		{27285BB5-37B4-42DD-AB2C-CCD91824D044}.Debug|x64.Build.0 = Debug|x64
+		{27285BB5-37B4-42DD-AB2C-CCD91824D044}.Debug|x86.ActiveCfg = Debug|x86
+		{27285BB5-37B4-42DD-AB2C-CCD91824D044}.Debug|x86.Build.0 = Debug|x86
+		{27285BB5-37B4-42DD-AB2C-CCD91824D044}.Release|x64.ActiveCfg = Release|x64
+		{27285BB5-37B4-42DD-AB2C-CCD91824D044}.Release|x64.Build.0 = Release|x64
+		{27285BB5-37B4-42DD-AB2C-CCD91824D044}.Release|x86.ActiveCfg = Release|x86
+		{27285BB5-37B4-42DD-AB2C-CCD91824D044}.Release|x86.Build.0 = Release|x86
+		{EB8713C5-0284-47C5-950A-90E4EB326669}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{EB8713C5-0284-47C5-950A-90E4EB326669}.Debug|x64.Build.0 = Debug|Any CPU
+		{EB8713C5-0284-47C5-950A-90E4EB326669}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{EB8713C5-0284-47C5-950A-90E4EB326669}.Debug|x86.Build.0 = Debug|Any CPU
+		{EB8713C5-0284-47C5-950A-90E4EB326669}.Release|x64.ActiveCfg = Release|Any CPU
+		{EB8713C5-0284-47C5-950A-90E4EB326669}.Release|x64.Build.0 = Release|Any CPU
+		{EB8713C5-0284-47C5-950A-90E4EB326669}.Release|x86.ActiveCfg = Release|Any CPU
+		{EB8713C5-0284-47C5-950A-90E4EB326669}.Release|x86.Build.0 = Release|Any CPU
+		{6584C02F-080F-425D-BE53-9F72F7BE2897}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{6584C02F-080F-425D-BE53-9F72F7BE2897}.Debug|x64.Build.0 = Debug|Any CPU
+		{6584C02F-080F-425D-BE53-9F72F7BE2897}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{6584C02F-080F-425D-BE53-9F72F7BE2897}.Debug|x86.Build.0 = Debug|Any CPU
+		{6584C02F-080F-425D-BE53-9F72F7BE2897}.Release|x64.ActiveCfg = Release|Any CPU
+		{6584C02F-080F-425D-BE53-9F72F7BE2897}.Release|x64.Build.0 = Release|Any CPU
+		{6584C02F-080F-425D-BE53-9F72F7BE2897}.Release|x86.ActiveCfg = Release|Any CPU
+		{6584C02F-080F-425D-BE53-9F72F7BE2897}.Release|x86.Build.0 = Release|Any CPU
+		{757BA8EB-065F-47A7-834D-025C1559E2A5}.Debug|x64.ActiveCfg = Debug|x64
+		{757BA8EB-065F-47A7-834D-025C1559E2A5}.Debug|x64.Build.0 = Debug|x64
+		{757BA8EB-065F-47A7-834D-025C1559E2A5}.Debug|x86.ActiveCfg = Debug|x86
+		{757BA8EB-065F-47A7-834D-025C1559E2A5}.Debug|x86.Build.0 = Debug|x86
+		{757BA8EB-065F-47A7-834D-025C1559E2A5}.Release|x64.ActiveCfg = Release|x64
+		{757BA8EB-065F-47A7-834D-025C1559E2A5}.Release|x64.Build.0 = Release|x64
+		{757BA8EB-065F-47A7-834D-025C1559E2A5}.Release|x86.ActiveCfg = Release|x86
+		{757BA8EB-065F-47A7-834D-025C1559E2A5}.Release|x86.Build.0 = Release|x86
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {DBAEC14C-FD27-4A23-ACFB-403ED5B835AB}
+	EndGlobalSection
+EndGlobal

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ApiTypes.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ApiTypes.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ApiTypes.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    public class ApiTypes
+    {
+        public const string Platform = "Api";
+        public const string Http = "Http";
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Assert.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Assert.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Assert.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    public class AssertException : Exception
+    {
+        public AssertException() { }
+        public AssertException(string message) : base(message) { }
+        public AssertException(string message, Exception inner) : base(message, inner) { }
+    }
+
+    public class Assert
+    {
+        public static void AreBytesEqual(byte[] expected, byte[] actual)
+        {
+            byte[] a = expected;
+            byte[] b = actual;
+            if (a.Length != b.Length)
+                throw new AssertException("Expected byte array size of " + a.Length + ", got size of: " + b.Length);
+
+            for (int i = 0; i < a.Length; i++)
+            {
+                if (a[i] != b[i])
+                    throw new AssertException("Byte number " + (i + 1) + " in expected byte array does not match byte in actual byte array");
+            }
+        }
+
+        public static void AreEqual<T>(T expected, T actual)
+        {
+            if (typeof(T) == typeof(byte[]))
+            {
+                AreBytesEqual(expected as byte[], actual as byte[]);
+            }
+            else if (!EqualityComparer<T>.Default.Equals(expected, actual))
+                throw new AssertException("Expected: " + expected + ", got: " + actual);
+        }
+
+        public static void Greater<T>(T value, T against) where T : IComparable
+        {
+            if (value.CompareTo(against) <= 0)
+                throw new AssertException(value + " is not greater than " + against);
+        }
+
+        public static void IsNull(object obj)
+        {
+            if (obj != null)
+                throw new AssertException("Reference is not null");
+        }
+
+        public static void IsNotNull(object obj)
+        {
+            if (obj == null)
+                throw new AssertException("Reference is null");
+        }
+
+        public static void IsTrue(bool condition)
+        {
+            if (!condition)
+                throw new AssertException("Condition evaluated to false. Expected: true");
+        }
+
+        public static void IsFalse(bool condition)
+        {
+            if (condition)
+                throw new AssertException("Condition evaluated to true. Expected: false");
+        }
+
+        public static void IsNaN(double value)
+        {
+            if (!double.IsNaN(value))
+                throw new AssertException("Double is a number. Expected: NaN");
+        }
+
+        public static void Fail(string msg)
+        {
+            throw new AssertException(msg);
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/CommonTests.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/CommonTests.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/CommonTests.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,62 @@
+using System;
+using System.Reflection;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    //Defines tests outside of the SQLite-based test suite
+
+    public interface IExternalTest
+    {
+        void Execute(IPlatformFactory factory, ITestLogger logger);
+    }
+
+    public interface IPlatformFactory
+    {
+        MgService CreateService(int serviceType);
+        MgMapBase CreateMap(MgResourceIdentifier mapDefinition);
+        MgMapBase CreateMap(string coordSys, MgEnvelope env, string name);
+        MgLayerBase CreateLayer(MgResourceIdentifier resId);
+    }
+
+    public class CommonTests
+    {
+        public static int Execute(IPlatformFactory factory, ITestLogger logger, ref int testsRun)
+        {
+            int failures = 0;
+            var types = typeof(IPlatformFactory).GetTypeInfo().Assembly.GetTypes();
+            foreach (var type in types)
+            {
+                if (typeof(IExternalTest).IsAssignableFrom(type) && type.GetTypeInfo().IsClass)
+                {
+                    var test = (IExternalTest)Activator.CreateInstance(type);
+                    try
+                    {
+                        logger.WriteLine("****** Executing platform test: " + type.Name + " *********");
+                        Console.WriteLine("Executing external platform test: " + type.Name);
+                        test.Execute(factory, logger);
+                    }
+                    catch (AssertException ex)
+                    {
+                        logger.WriteLine("Assertion failure: " + ex.Message);
+                        Console.WriteLine("Assertion failure: " + ex.Message);
+                        failures++;
+                    }
+                    catch (Exception ex)
+                    {
+                        logger.WriteLine("General failure: " + ex.ToString());
+                        Console.WriteLine("General failure: " + ex.ToString());
+                        failures++;
+                    }
+                    finally
+                    {
+                        testsRun++;
+                    }
+                }
+            }
+            return failures;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/CommonUtility.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/CommonUtility.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/CommonUtility.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,814 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Globalization;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Xml;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    /// <summary>
+    /// Extension methods for SqliteVm
+    /// </summary>
+    public static class SqliteDbExtensions
+    {
+        /*
+        public static int Prepare(this SqliteVm vm, string sql, params object[] args)
+        {
+            string formattedSql = string.Format(sql, args);
+            return vm.Prepare(formattedSql);
+        }
+        */
+        public static int Execute(this SqliteVm vm, string sql, params object[] args)
+        {
+            string formattedSql = string.Format(sql, args);
+            return vm.Execute(formattedSql);
+        }
+
+        public static bool ReadParameterValue(this SqliteVm vm, int paramSetId, string paramName, NameValueCollection result, bool bIsPath = false)
+        {
+            int stat = vm.Execute("Select ParamValue from Params WHERE ParamSet={0} AND ParamName=\"{1}\"", paramSetId, paramName);
+            if (stat == Sqlite.Row)
+            {
+                string str = vm.GetString("ParamValue");
+                if (bIsPath)
+                {
+                    str = CommonUtility.GetPath(str);
+                }
+                result.Add(paramName, str);
+                return true;
+            }
+            return false;
+        }
+
+        public static bool ReadParameterValue(this SqliteVm vm, string paramName, NameValueCollection result)
+        {
+            int stat = vm.Execute("Select ParamValue from Params WHERE ParamName=\"{0}\"", paramName);
+            if (stat == Sqlite.Row)
+            {
+                result.Add(paramName, vm.GetString("ParamValue"));
+                return true;
+            }
+            return false;
+        }
+
+        public static bool ReadCommonParameterValue(this SqliteVm vm, string paramName, NameValueCollection result)
+        {
+            int stat = vm.Execute("Select ParamValue from CommonParams WHERE ParamName=\"{0}\"", paramName);
+            if (stat == Sqlite.Row)
+            {
+                result.Add(paramName, vm.GetString("ParamValue"));
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /// <summary>
+    /// Common utility methods
+    /// </summary>
+    public static class CommonUtility
+    {
+        public static MgResourceIdentifier TryGetResourceId(this NameValueCollection param, string key)
+        {
+            if (param[key] != null)
+            {
+                return new MgResourceIdentifier(param[key]);
+            }
+            return null;
+        }
+
+        public static NameValueCollection SetCommonParams(int paramSet, SqliteDb db)
+        {
+            NameValueCollection result = null;
+            var vm = new SqliteVm(db, false);
+            try
+            {
+                result = new NameValueCollection();
+
+                vm.ReadParameterValue(paramSet, "OPERATION", result);
+                vm.ReadCommonParameterValue("VERSION", result);
+                vm.ReadCommonParameterValue("CREDENTIALS", result);
+                vm.ReadCommonParameterValue("LOCALE", result);
+
+                vm = null;
+            }
+            catch (MgException ex)
+            {
+                throw new UnitTestException(string.Format("Exception from MapGuide:\n{0}", ex.GetDetails()));
+            }
+            catch (Exception ex)
+            {
+                try
+                {
+                    vm = null;
+                    vm = new SqliteVm(db, true);
+                    vm.ReadParameterValue("VERSION", result);
+                    vm.ReadParameterValue("CREDENTIALS", result);
+                    vm.ReadParameterValue("LOCALE", result);
+                }
+                catch (Exception ex2)
+                {
+
+                }
+            }
+            return result;
+        }
+
+        public static MgByteReader TryGetByteReader(this NameValueCollection param, string key, bool bCheck = true)
+        {
+            string path = param[key] ?? string.Empty;
+            if (string.IsNullOrEmpty(path))
+                return null;
+            else
+                return GetByteReaderFromPath(path, bCheck);
+        }
+
+        private static MgByteReader GetByteReaderFromPath(string path, bool bCheck = true)
+        {
+            string fixedPath = FixRelativePath(path);
+
+            if (bCheck)
+            {
+                if (File.Exists(fixedPath))
+                {
+                    MgByteSource source = new MgByteSource(fixedPath);
+                    MgByteReader reader = source.GetReader();
+                    return reader;
+                }
+                return null;
+            }
+            else
+            {
+                MgByteSource source = new MgByteSource(fixedPath);
+                MgByteReader reader = source.GetReader();
+                return reader;
+            }
+        }
+
+        public static string GetDbPath(string dumpFileName)
+        {
+            var db = new SqliteDb();
+            var dbPath = dumpFileName.Replace(".dump", ".db");
+            var dbName = CommonUtility.GetPath(dbPath);
+
+            if (!File.Exists(dumpFileName) && !File.Exists(dbName))
+            {
+                throw new UnitTestException(string.Format("Error: Dump file {0} not found. Unable to create database file", dumpFileName));
+            }
+            else if (!File.Exists(dbName))
+            {
+                db.GenerateDatabase(dumpFileName, dbName);
+            }
+            else if (File.Exists(dumpFileName) && File.GetLastWriteTimeUtc(dumpFileName) > File.GetLastWriteTimeUtc(dbName))
+            {
+                try
+                {
+                    File.Delete(dbName);
+                    db.GenerateDatabase(dumpFileName, dbName);
+                }
+                catch
+                {
+                    throw new UnitTestException(string.Format("Unable to delete database file {0}. The file is either in use or is read-only. The database has not been updated", dbName));
+                }
+            }
+
+            return dbPath;
+        }
+
+        public static string GetPath(string path)
+        {
+            string fixedPath = FixRelativePath(path);
+
+            if (Path.IsPathRooted(fixedPath))
+                return Path.GetFullPath(fixedPath.Replace("\\", "/"));
+            else
+                return Path.GetFullPath(Path.Combine(GetAssemblyPath(), fixedPath).Replace("\\", "/"));
+        }
+
+        public static string FixRelativePath(string path)
+        {
+            //Our DNX root is 2 levels deeper, so fix any input paths that expect the original depth
+            string fixedPath = path?.Replace("\\", "/");
+            if (path?.Contains(TestDataRoot.Path) == false)
+            {
+                fixedPath = path?.Replace("\\", "/");
+                fixedPath = fixedPath?.Replace("../../TestData", TestDataRoot.Path) ?? string.Empty;
+                fixedPath = fixedPath?.Replace("\\", "/");
+            }
+            return fixedPath;
+        }
+
+        private static string GetAssemblyPath()
+        {
+            return Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);
+        }
+
+        public static MgStringCollection StringToMgStringCollection(string str)
+        {
+            try
+            {
+                MgStringCollection coll = new MgStringCollection();
+                string[] tokens = str.Split(',');
+                foreach (var token in tokens)
+                {
+                    coll.Add(token);
+                }
+                return coll;
+            }
+            catch (MgException ex)
+            {
+                throw new UnitTestException(string.Format("Exception from MapGuide:\n{0}", ex.GetDetails()));
+            }
+        }
+
+        public static string MgStringCollectionToString(MgStringCollection coll)
+        {
+            try
+            {
+                //Sigh, we're too smart for our own good. Yes, the trailing comma
+                //should be there!
+                StringBuilder sb = new StringBuilder();
+                for (int i = 0; i < coll.GetCount(); i++)
+                {
+                    sb.Append(coll.GetItem(i));
+                    sb.Append(",");
+                }
+                return sb.ToString();
+
+                /*
+                List<string> items = new List<string>();
+                for (int i = 0; i < coll.GetCount(); i++)
+                {
+                    items.Add(coll.GetItem(i));
+                }
+                return string.Join(",", items.ToArray());
+                 */
+            }
+            catch (MgException ex)
+            {
+                throw new UnitTestException(string.Format("Exception from MapGuide:\n{0}", ex.GetDetails()));
+            }
+        }
+
+        public static string BooleanToString(bool b)
+        {
+            return b ? "True" : "False";
+        }
+
+        public static string MgEnvelopeToString(MgEnvelope env)
+        {
+            try
+            {
+                var ll = env.GetLowerLeftCoordinate();
+                var ur = env.GetUpperRightCoordinate();
+                return string.Format(CultureInfo.InvariantCulture,
+                    "({0}:{1})-({2}:{3})",
+                    ll.GetX(),
+                    ll.GetY(),
+                    ur.GetX(),
+                    ur.GetY());
+            }
+            catch (MgException ex)
+            {
+                throw new UnitTestException(string.Format("Exception from MapGuide:\n{0}", ex.GetDetails()));
+            }
+        }
+
+        public static string MgPointToString(MgPoint pt)
+        {
+            try
+            {
+                var coord = pt.GetCoordinate();
+                return string.Format(CultureInfo.InvariantCulture,
+                    "({0}:{1})",
+                    coord.GetX(),
+                    coord.GetY());
+            }
+            catch (MgException ex)
+            {
+                throw new UnitTestException(string.Format("Exception from MapGuide:\n{0}", ex.GetDetails()));
+            }
+        }
+
+        public static string GetExtension(string name)
+        {
+            if (name.LastIndexOf(".") >= 0)
+            {
+                return name.Substring(name.LastIndexOf(".") + 1);
+            }
+            else
+            {
+                if (name == "MG_USER_CREDENTIALS")
+                    return "txt";
+                else
+                    return "bin";
+            }
+        }
+
+        public static string GetMimeType(string extension)
+        {
+            switch (extension)
+            {
+                case "agf":
+                    return "application/agf";
+                case "bin":
+                    return "application/octet-stream";
+                case "dwf":
+                    return "model/vnd.dwf";
+                case "jpg":
+                case "jpeg":
+                    return "image/jpeg";
+                case "png":
+                    return "image/png";
+                case "tif":
+                case "tiff":
+                    return "image/tiff";
+                case "html":
+                    return "text/html";
+                case "txt":
+                    return "text/plain";
+                case "xml":
+                    return "text/xml";
+                default:
+                    return "application/octet-stream";
+            }
+        }
+
+        public static object SpecialDataHandling(string operation, object resultData, string mimeType)
+        {
+            object res = resultData;
+            switch (operation)
+            {
+                case "ENUMERATERESOURCES":
+                    res = RemoveTimeStamp(resultData.ToString());
+                    break;
+                case "GETDRAWINGLAYER":
+                    res = RemoveDwfSectionName(resultData, Encoding.UTF8);
+                    break;
+                case "GETDRAWINGSECTION":
+                    res = RemoveDwfSectionName(resultData, Encoding.UTF8);
+                    break;
+                case "GETLOG":
+                    res = RemoveLogEntryTimeStamp(resultData.ToString());
+                    break;
+                case "GETMAP":
+                    res = GetMapHeader(resultData.ToString());
+                    break;
+                case "GETLONGTRANSACTIONS":
+                    res = RemoveCreationDate(resultData.ToString());
+                    break;
+                case "GETSPATIALCONTEXTS":
+                    res = RemoveFdoProviderVersion(resultData.ToString());
+                    break;
+            }
+
+            string strRes = res as string;
+            if (strRes != null && mimeType == "text/xml")
+            {
+                var doc = new XmlDocument();
+                doc.LoadXml(strRes);
+                res = SortElement(doc, "");
+            }
+            return res;
+        }
+        
+        internal static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+
+        class InvertedComparer<T> : IComparer<T>
+        {
+            private IComparer<T> _comp;
+
+            public InvertedComparer(IComparer<T> comp) { _comp = comp; }
+
+            public int Compare(T x, T y)
+            {
+                int res = _comp.Compare(x, y);
+                //Invert the non-zero results
+                if (res > 0)
+                    return -1;
+                else if (res < 0)
+                    return 1;
+                else
+                    return 0;
+            }
+        }
+
+        private static string SortElement(XmlNode elem, string preText)
+        {
+            var elemArray = new List<string>();
+            string elemString = "";
+            if (elem.ChildNodes.Count > 0)
+            {
+                int elCount = 0;
+                int txtCount = 0;
+                //foreach (XmlNode child in elem.ChildNodes)
+                for (int i = 0; i < elem.ChildNodes.Count; i++)
+                {
+                    var child = elem.ChildNodes[i];
+                    if (child.NodeType == XmlNodeType.Element)
+                    {
+                        var elemValue = SortElement(child, preText + "  ");
+                        if (!string.IsNullOrEmpty(elemValue))
+                        {
+                            elemArray.Add(elemValue);
+                            elCount++;
+                        }
+                    }
+                    else if (child.NodeType == XmlNodeType.Text)
+                    {
+                        string content = child.InnerText.Trim();
+                        if (!string.IsNullOrEmpty(content))
+                        {
+                            elemArray.Add(content);
+                            txtCount++;
+                        }
+                    }
+                }
+
+                //We have to ordinal compare to match the sort behaviour of
+                //sort() in PHP
+                elemArray.Sort((s1, s2) =>
+                {
+                    return string.CompareOrdinal(s1, s2);
+                });
+                foreach (string str in elemArray)
+                {
+                    elemString += str;
+                }
+            }
+
+            string endTag = "";
+            if (elemArray.Count > 1 && elemString.Length > 0)
+            {
+                endTag = "\n" + preText;
+            }
+            string tagName = "";
+            if (!(elem is XmlDocument))
+            {
+                tagName = elem.Name;
+            }
+            endTag += "</" + tagName + ">";
+
+            if ("" != tagName)
+            {
+                elemString = "\n" + preText + "<" + tagName + ">" + elemString + endTag;
+            }
+
+            return elemString;
+        }
+
+        private static string RemoveTimeStamp(string resultData)
+        {
+            string result = resultData;
+            string newResult = result;
+            while (result.IndexOf("<CreatedDate>") >= 0)
+            {
+                newResult = result.Substring(0, result.IndexOf("<CreatedDate>"));
+                newResult += result.Substring(result.IndexOf("</ModifiedDate>") + "</ModifiedDate>".Length);
+                result = newResult;
+            }
+            return newResult;
+        }
+
+        private static object RemoveDwfSectionName(object resultData, Encoding enc)
+        {
+            bool bFromByteArray = false;
+            byte[] bResultData = resultData as byte[];
+            string strResultData = resultData as string;
+            /*
+            if (strResultData == null)
+            {
+                if (bResultData != null)
+                {
+                    strResultData = enc.GetString(bResultData);
+                    bFromByteArray = true;
+                }
+            }*/
+
+            if (strResultData != null)
+            {
+                //Console.WriteLine("RemoveDwfSectionName: length = {0}", strResultData.Length);
+                int idx = strResultData.IndexOf(".w2d");
+                //Console.WriteLine("RemoveDwfSectionName: widx = {0}", idx);
+                if (idx >= 0)
+                {
+                    string newResult = strResultData.Substring(idx);
+                    int eidx = newResult.IndexOf("EndOfDWF");
+                    //Console.WriteLine("RemoveDwfSectionName: eidx = {0}", eidx);
+                    if (0 != eidx)
+                    {
+                        newResult = newResult.Substring(0, eidx);
+                        //Console.WriteLine("RemoveDwfSectionName: newlength = {0}", newResult.Length);
+                    }
+                    if (bFromByteArray)
+                        return enc.GetBytes(newResult);
+                    else
+                        return newResult;
+                }
+            }
+            else if (bResultData != null)
+            {
+                byte[] bW2d = enc.GetBytes(".w2d");
+                byte[] bEOF = enc.GetBytes("EndOfDWF");
+
+                int widx = -1;
+                int eidx = -1;
+
+                int wMatches = 0;
+                int eMatches = 0;
+
+                int i = 0;
+                while (i < bResultData.Length)
+                {
+                    //Haven't found .w2d sequence
+                    if (widx < 0 && wMatches == 0)
+                    {
+                        //We've found a "."
+                        if (bResultData[i] == bW2d[0])
+                        {
+                            wMatches++;
+                            i++;
+
+                            //Now try to follow through this sequence to see if it is ".w2d"
+                            while (wMatches < bW2d.Length)
+                            {
+                                //End of array. Abort
+                                if (i >= bResultData.Length)
+                                    break;
+
+                                //Next byte in sequence matches. Advance
+                                if (bResultData[i] == bW2d[wMatches])
+                                {
+                                    //Increment matches
+                                    wMatches++;
+
+                                    //Check if full sequence matches
+                                    if (wMatches == bW2d.Length)
+                                    {
+                                        //Match. Record index which is current position minus the # of consecutive matches
+                                        widx = i - wMatches;
+                                        break;
+                                    }
+                                }
+                                else //Incomplete sequence. Break this loop
+                                {
+                                    wMatches = 0; //Reset
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                    //Haven't found EndOfDWF sequence
+                    else if (eidx < 0 && eMatches == 0)
+                    {
+                        //We've found a "E"
+                        if (bResultData[i] == bEOF[0])
+                        {
+                            eMatches++;
+                            i++;
+
+                            //Now try to follow through this sequence to see if it is "EndOfDWF"
+                            while (eMatches < bEOF.Length)
+                            {
+                                //End of array. Abort
+                                if (i >= bResultData.Length)
+                                    break;
+
+                                //Next byte in sequence matches. Advance
+                                if (bResultData[i] == bEOF[eMatches])
+                                {
+                                    //Increment matches
+                                    eMatches++;
+
+                                    //Check if full sequence matches
+                                    if (eMatches == bEOF.Length)
+                                    {
+                                        //Match. Record index which is current position minus the # of consecutive matches
+                                        eidx = i - eMatches;
+                                        break;
+                                    }
+                                }
+                                else //Incomplete sequence. Break this loop
+                                {
+                                    eMatches = 0; //Reset
+                                    break;
+                                }
+                            }
+                        }
+                    }
+
+                    //Found both offsets. We're done
+                    if (widx > 0 && eidx > widx)
+                        break;
+
+                    i++;
+                }
+
+                if (widx > 0 && eidx > widx)
+                {
+                    byte[] newResult = new byte[eidx - widx];
+                    int off = 0;
+                    for (int j = widx; j <= eidx; j++)
+                    {
+                        newResult[off] = bResultData[j];
+                    }
+                }
+            }
+            return resultData;
+        }
+
+        private static string RemoveLogEntryTimeStamp(string resultData)
+        {
+            string result = resultData;
+            string newResult = result;
+            while (result.IndexOf("<") >= 0)
+            {
+                newResult = result.Substring(0, result.IndexOf("<"));
+                newResult += result.Substring(result.IndexOf(">") + 1);
+                result = newResult;
+            }
+            return newResult;
+        }
+
+        private static string GetMapHeader(string resultData)
+        {
+            if (resultData.IndexOf("(DWF V06.01)") >= 0)
+                resultData = "(DWF V06.01)";
+            return resultData;
+        }
+
+        private static string RemoveFdoProviderVersion(string resultData)
+        {
+            string newResult = resultData;
+
+            int istart = resultData.IndexOf("<ProviderName>") + "<ProviderName>".Length;
+            int iend = resultData.IndexOf("</ProviderName>");
+            if (istart < 0 || iend < 0)
+                return resultData;
+
+            newResult = resultData.Substring(0, istart);
+
+            string provider = resultData.Substring(istart, iend - istart);
+
+            newResult += StripVersionFromFdoProvider(provider);
+
+            newResult += resultData.Substring(iend);
+            resultData = newResult;
+
+            return newResult;
+        }
+
+        private static string StripVersionFromFdoProvider(string provider)
+        {
+            string[] tokens = provider.Split('.');
+            if (tokens.Length == 4)
+                return $"{tokens[0]}.{tokens[1]}";
+            else
+                return provider;
+        }
+
+        private static string RemoveCreationDate(string resultData)
+        {
+            string newResult = resultData;
+            while (resultData.IndexOf("<CreationDate>") >= 0)
+            {
+                newResult = resultData.Substring(0, resultData.IndexOf("<CreationDate>"));
+                newResult += resultData.Substring(resultData.IndexOf("</CreationDate>") + "</CreationDate>".Length);
+                resultData = newResult;
+            }
+            return newResult;
+        }
+
+        public static object ProcessExceptionMessage(object resultData)
+        {
+            string strResultData = resultData as string;
+            if (strResultData != null)
+            {
+                string text = "exception occurred";
+                if (strResultData.Contains(text))
+                {
+                    strResultData = strResultData.Substring(0, strResultData.IndexOf(text) + text.Length);
+                }
+                return strResultData;
+            }
+            return resultData;
+        }
+
+        public static bool SpecialValidation(string operation, object resultData, object expectedResult)
+        {
+            if (operation == "GETFEATUREPROVIDERS")
+            {
+                //We expect both to be strings here
+                return GetFeatureProvidersValidation(resultData.ToString(), expectedResult.ToString());
+            }
+            return false;
+        }
+
+        private static bool GetFeatureProvidersValidation(string resultData, string expectedResult)
+        {
+            throw new NotImplementedException();
+        }
+
+        public static object RemoveStackTraceFromResult(object result)
+        {
+            var strResult = result as string;
+            //TODO: Clean out stack trace
+            return result;
+        }
+
+        public static string GetExtensionFromMimeType(string mimeType)
+        {
+            string extension = "xml";
+            if (mimeType.Contains("ePlot"))
+                return "dwf";
+            if (mimeType.Contains("text/plain"))
+                return "txt";
+            if (mimeType.Contains("text/html"))
+                return "html";
+
+            switch (mimeType)
+            {
+                case "application/agf":
+                    return "agf";
+                case "application/octet-stream":
+                    return "bin";
+                case "model/vnd.dwf":
+                    return "dwf";
+                case "image/jpeg":
+                    return "jpg";
+                case "image/png":
+                    return "png";
+                case "image/tiff":
+                    return "tiff";
+                case "application/x-w2d":
+                    return "dwf";
+            }
+
+            return extension;
+        }
+
+        public static bool ByteArraysEqual(byte[] bExpected, byte[] bActual, string operation, string testName)
+        {
+            if (bExpected == null && bActual != null)
+                return false;
+
+            if (bExpected != null && bActual == null)
+                return false;
+
+            bool bRet = true;
+#if DEBUG_BINARY_COMPARISON
+            bool bLogged = false;
+            Guid guid = Guid.NewGuid();
+            using (StreamWriter sw1 = new StreamWriter(guid.ToString() + "_" + operation + "_" + testName + "_expected.txt", false))
+            using (StreamWriter sw2 = new StreamWriter(guid.ToString() + "_" + operation + "_" + testName + "_actual.txt", false))
+            {
+#endif
+            for (int i = 0; i < bExpected.Length; i++)
+            {
+                if (i >= bExpected.Length ||
+                    i >= bActual.Length)
+                {
+                    break;
+                }
+
+                byte b1 = bExpected[i];
+                byte b2 = bActual[i];
+
+#if DEBUG_BINARY_COMPARISON
+                sw1.WriteLine("{0} {1}", b1, Convert.ToChar(b1));
+                sw2.WriteLine("{0} {1}", b2, Convert.ToChar(b2));
+#endif
+
+                if (b1 != b2)
+                {
+#if DEBUG_BINARY_COMPARISON
+                    bRet = false;
+                    if (!bLogged)
+                    {
+                        System.Diagnostics.Debug.WriteLine(string.Format("[MgTestRunner]: Comparison {0} returned false. See logged text files", guid.ToString()));
+                        bLogged = true;
+                    }
+#else
+                    return false;
+#endif
+                }
+            }
+#if DEBUG_BINARY_COMPARISON
+            }
+
+            if (bRet)
+            {
+                File.Delete(guid.ToString() + "_" + operation + "_" + testName + "_expected.txt");
+                File.Delete(guid.ToString() + "_" + operation + "_" + testName + "_actual.txt");
+            }
+#endif
+
+            System.Diagnostics.Debug.WriteLine(string.Format("[MgTestRunner]: {0} - {1} - COMPARE: {2} with {3} = {4}", testName, operation, bExpected.Length, bActual.Length, (bRet ? 0 : 1)));
+            return bRet;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/ByteReaderTest.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/ByteReaderTest.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/ByteReaderTest.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common.ExternalTests
+{
+    internal class ByteReaderTestData
+    {
+        internal static byte[] testBytes;
+        internal static int nBytes = 32768;
+        internal static int nBlocks = 256;
+        internal static string testString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+        internal static string infileName;
+        internal static string outfileName;
+        private static bool bFirstTime = true;
+
+        public static void Init()
+        {
+            if (bFirstTime)
+            {
+                infileName = System.IO.Path.GetTempFileName();
+                outfileName = System.IO.Path.GetTempFileName();
+                testBytes = new byte[nBytes];
+                for (int i = 0; i < nBytes; i++)
+                {
+                    testBytes[i] = (byte)(i % 255);
+                }
+
+                using (var fp = System.IO.File.OpenWrite(infileName))
+                {
+                    for (int j = 0; j < nBlocks; j++)
+                    {
+                        fp.Write(testBytes, 0, nBytes);
+                    }
+                }
+
+                bFirstTime = false;
+            }
+        }
+    }
+
+    public class ByteReaderTest : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            ByteReaderTestData.Init();
+            var nBytes = ByteReaderTestData.nBytes;
+            var nBlocks = ByteReaderTestData.nBlocks;
+            var testBytes = ByteReaderTestData.testBytes;
+            var infileName = ByteReaderTestData.infileName;
+            var outfileName = ByteReaderTestData.outfileName;
+            byte[] buf = new byte[nBytes];
+            MgByteReader reader = new MgByteReader(infileName, "png", false);
+            Assert.AreEqual(nBlocks * nBytes, reader.GetLength());
+            reader.Read(buf, nBytes);
+            Assert.AreEqual(buf, testBytes);
+            Assert.AreEqual((nBlocks - 1) * nBytes, reader.GetLength());
+            reader.Rewind();
+            Assert.AreEqual(nBlocks * nBytes, reader.GetLength());
+            reader.ToFile(outfileName);
+            reader.Rewind();
+
+            byte[] buf2 = new byte[nBytes];
+            using (var fp = System.IO.File.OpenRead(outfileName))
+            {
+                for (int j = 0; j < nBlocks; j++)
+                {
+                    fp.Read(buf2, 0, nBytes);
+                    reader.Read(buf, nBytes);
+                    Assert.AreEqual(buf, buf2);
+                }
+            }
+        }
+    }
+
+    public class MemoryConstructor : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            ByteReaderTestData.Init();
+            var nBytes = ByteReaderTestData.nBytes;
+            var testBytes = ByteReaderTestData.testBytes;
+            var outfileName = ByteReaderTestData.outfileName;
+
+            byte[] buf = new byte[nBytes];
+            MgByteReader reader = new MgByteReader(testBytes, nBytes, "png");
+            Assert.AreEqual(nBytes, reader.GetLength());
+            reader.Read(buf, nBytes);
+            Assert.AreEqual(buf, testBytes);
+            Assert.AreEqual(0, reader.GetLength());
+            reader.Rewind();
+            Assert.AreEqual(nBytes, reader.GetLength());
+
+            reader.ToFile(outfileName);
+
+            using (var fp = System.IO.File.OpenRead(outfileName))
+            {
+                fp.Read(buf, 0, nBytes);
+                Assert.AreEqual(buf, testBytes);
+            }
+        }
+    }
+
+    public class StringConstructor : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            ByteReaderTestData.Init();
+            var testString = ByteReaderTestData.testString;
+            MgByteReader reader = new MgByteReader(testString, "text/html");
+            Assert.AreEqual(testString.Length, reader.GetLength());
+            string buf = reader.ToString();
+            Assert.AreEqual(testString, buf);
+            Assert.AreEqual(testString.Length, reader.GetLength());
+            reader.Rewind();
+            Assert.AreEqual(testString.Length, reader.GetLength());
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/CollectionTests.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/CollectionTests.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/CollectionTests.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,993 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common.ExternalTests
+{
+    public class GeomBuild
+    {
+        private MgGeometryFactory factory = new MgGeometryFactory();
+
+        public MgPoint CreatePoint()
+        {
+            MgCoordinate coord = factory.CreateCoordinateXYZ(5.0, 3.0, 2.0);
+            return factory.CreatePoint(coord);
+        }
+
+        public MgLineString CreateLineString(double offset)
+        {
+            MgCoordinateCollection coordCol = new MgCoordinateCollection();
+
+            MgCoordinate coord1 = factory.CreateCoordinateXY(offset + 0.0, 1.0);
+            MgCoordinate coord2 = factory.CreateCoordinateXY(offset + 2.0, 3.0);
+            MgCoordinate coord3 = factory.CreateCoordinateXY(offset + 4.0, 5.0);
+
+            coordCol.Add(coord1);
+            coordCol.Add(coord2);
+            coordCol.Add(coord3);
+
+            return factory.CreateLineString(coordCol);
+        }
+
+
+
+        public MgLinearRing CreateLinearRing(double offset)
+        {
+            MgCoordinate oCoord1 = factory.CreateCoordinateXY(0.0, offset);
+            MgCoordinate oCoord2 = factory.CreateCoordinateXY(5.0, offset);
+            MgCoordinate oCoord3 = factory.CreateCoordinateXY(5.0, offset + 5.0);
+            MgCoordinate oCoord4 = factory.CreateCoordinateXY(0.0, offset + 5.0);
+            MgCoordinate oCoord5 = factory.CreateCoordinateXY(0.0, offset);
+
+            MgCoordinateCollection outerRingCoord = new MgCoordinateCollection();
+            outerRingCoord.Add(oCoord1);
+            outerRingCoord.Add(oCoord2);
+            outerRingCoord.Add(oCoord3);
+            outerRingCoord.Add(oCoord4);
+            outerRingCoord.Add(oCoord5);
+
+            return factory.CreateLinearRing(outerRingCoord);
+        }
+
+
+        public MgPolygon CreatePolygon(double offset)
+        {
+
+
+            // OuterRing
+            MgCoordinate oCoord1 = factory.CreateCoordinateXY(offset + 0.0, 0.0);
+            MgCoordinate oCoord2 = factory.CreateCoordinateXY(offset + 5.0, 0.0);
+            MgCoordinate oCoord3 = factory.CreateCoordinateXY(offset + 5.0, 5.0);
+            MgCoordinate oCoord4 = factory.CreateCoordinateXY(offset + 0.0, 5.0);
+            MgCoordinate oCoord5 = factory.CreateCoordinateXY(offset + 0.0, 0.0);
+
+            MgCoordinateCollection outerRingCoord = new MgCoordinateCollection();
+            outerRingCoord.Add(oCoord1);
+            outerRingCoord.Add(oCoord2);
+            outerRingCoord.Add(oCoord3);
+            outerRingCoord.Add(oCoord4);
+            outerRingCoord.Add(oCoord5);
+
+            // Inner Ring1
+            MgCoordinate i1Coord1 = factory.CreateCoordinateXY(offset + 1.0, 1.0);
+            MgCoordinate i1Coord2 = factory.CreateCoordinateXY(offset + 2.0, 1.0);
+            MgCoordinate i1Coord3 = factory.CreateCoordinateXY(offset + 2.0, 2.0);
+            MgCoordinate i1Coord4 = factory.CreateCoordinateXY(offset + 1.0, 1.0);
+
+            MgCoordinateCollection inner1RingCoord = new MgCoordinateCollection();
+            inner1RingCoord.Add(i1Coord1);
+            inner1RingCoord.Add(i1Coord2);
+            inner1RingCoord.Add(i1Coord3);
+            inner1RingCoord.Add(i1Coord4);
+
+            // Inner Ring2
+            MgCoordinate i2Coord1 = factory.CreateCoordinateXY(offset + 3.0, 3.0);
+            MgCoordinate i2Coord2 = factory.CreateCoordinateXY(offset + 4.0, 3.0);
+            MgCoordinate i2Coord3 = factory.CreateCoordinateXY(offset + 4.0, 4.0);
+            MgCoordinate i2Coord4 = factory.CreateCoordinateXY(offset + 3.0, 3.0);
+
+            MgCoordinateCollection inner2RingCoord = new MgCoordinateCollection();
+            inner2RingCoord.Add(i2Coord1);
+            inner2RingCoord.Add(i2Coord2);
+            inner2RingCoord.Add(i2Coord3);
+            inner2RingCoord.Add(i2Coord4);
+
+            MgLinearRing extRing = factory.CreateLinearRing(outerRingCoord);
+            MgLinearRing intRing1 = factory.CreateLinearRing(inner1RingCoord);
+            MgLinearRing intRing2 = factory.CreateLinearRing(inner2RingCoord);
+
+            MgLinearRingCollection intRings = new MgLinearRingCollection();
+            intRings.Add(intRing1);
+            intRings.Add(intRing2);
+
+            return factory.CreatePolygon(extRing, intRings);
+        }
+
+
+        public MgCurveString CreateCurveString(double offset)
+        {
+
+
+            // Create and return a curvestring consisting of
+            // one circulararc segment and one linearstring segment
+
+            // arcseg  = (0,0), (0,1), (1,2)
+            // lineseg = (1,2), (3,0), (3,2)
+
+            // ArcSegment
+            MgCoordinate startPos = factory.CreateCoordinateXY(offset + 0.0, offset + 0.0);
+            MgCoordinate midPos = factory.CreateCoordinateXY(offset + 0.0, offset + 1.0);
+            MgCoordinate endPos = factory.CreateCoordinateXY(offset + 1.0, offset + 2.0);
+            MgArcSegment arcSeg = factory.CreateArcSegment(startPos, endPos, midPos);
+
+            // Linear Segment
+            MgCoordinateCollection points = new MgCoordinateCollection();
+            MgCoordinate pt1 = factory.CreateCoordinateXY(offset + 1.0, offset + 2.0);
+            MgCoordinate pt2 = factory.CreateCoordinateXY(offset + 3.0, offset + 0.0);
+            MgCoordinate pt3 = factory.CreateCoordinateXY(offset + 3.0, offset + 2.0);
+            points.Add(pt1);
+            points.Add(pt2);
+            points.Add(pt3);
+            MgLinearSegment lineSeg = factory.CreateLinearSegment(points);
+
+            // CurveSegment
+            MgCurveSegmentCollection curveSegs = new MgCurveSegmentCollection();
+            curveSegs.Add(arcSeg);
+            curveSegs.Add(lineSeg);
+
+            return factory.CreateCurveString(curveSegs);
+        }
+
+
+        public MgCurveRing CreateCurveRing(double offset)
+        {
+            // Ring is a closed entity.
+            // Create and return a ring consisting of
+            // one circulararc segment and one linearstring segment
+
+            // arcseg  = (0,0), (0,1), (1,2)
+            // lineseg = (1,2), (0,0)
+
+
+
+            // ArcSegment
+            MgCoordinate startPos = factory.CreateCoordinateXY(offset + 0.0, offset + 0.0);
+            MgCoordinate midPos = factory.CreateCoordinateXY(offset + 0.0, offset + 1.0);
+            MgCoordinate endPos = factory.CreateCoordinateXY(offset + 1.0, offset + 2.0);
+            MgArcSegment arcSeg = factory.CreateArcSegment(startPos, endPos, midPos);
+
+            // Linear Segment
+            MgCoordinateCollection points = new MgCoordinateCollection();
+            MgCoordinate fromPt = factory.CreateCoordinateXY(offset + 1.0, offset + 2.0);
+            MgCoordinate toPt = factory.CreateCoordinateXY(offset + 0.0, offset + 0.0);
+            points.Add(fromPt);
+            points.Add(toPt);
+            MgLinearSegment lineSeg = factory.CreateLinearSegment(points);
+
+            // Curve Segment
+            MgCurveSegmentCollection curveSegs = new MgCurveSegmentCollection();
+            curveSegs.Add(arcSeg);
+            curveSegs.Add(lineSeg);
+
+            return factory.CreateCurveRing(curveSegs);
+        }
+
+
+        public MgCurvePolygon CreateCurvePolygon(double offset, int increment)
+        {
+
+
+            MgCurveRing extRing = CreateCurveRing(offset + increment);
+
+            MgCurveRingCollection intRings = new MgCurveRingCollection();
+
+            MgCurveRing ring1 = CreateCurveRing(offset + 2 * increment);
+            MgCurveRing ring2 = CreateCurveRing(offset + 3 * increment);
+
+            intRings.Add(ring1);
+            intRings.Add(ring2);
+
+            return factory.CreateCurvePolygon(extRing, intRings);
+        }
+
+
+        public MgMultiPoint CreateMultiPoint()
+        {
+
+
+            MgCoordinate coord1 = factory.CreateCoordinateXYZ(1.0, 2.0, 3.0);
+            MgPoint point1 = factory.CreatePoint(coord1);
+
+            MgCoordinate coord2 = factory.CreateCoordinateXYZ(4.0, 5.0, 6.0);
+            MgPoint point2 = factory.CreatePoint(coord2);
+
+            MgCoordinate coord3 = factory.CreateCoordinateXYZ(7.0, 8.0, 9.0);
+            MgPoint point3 = factory.CreatePoint(coord3);
+
+            MgPointCollection pnts = new MgPointCollection();
+            pnts.Add(point1);
+            pnts.Add(point2);
+            pnts.Add(point3);
+
+            return factory.CreateMultiPoint(pnts);
+        }
+
+
+        public MgMultiLineString CreateMultiLineString()
+        {
+
+
+            MgCoordinate coord1 = factory.CreateCoordinateXYZ(0.0, 1.0, 2.0);
+            MgCoordinate coord2 = factory.CreateCoordinateXYZ(3.0, 4.0, 5.0);
+            MgCoordinate coord3 = factory.CreateCoordinateXYZ(6.0, 7.0, 8.0);
+
+            MgCoordinateCollection coordColA = new MgCoordinateCollection();
+            coordColA.Add(coord1);
+            coordColA.Add(coord2);
+            coordColA.Add(coord3);
+
+            MgCoordinate coord4 = factory.CreateCoordinateXYZ(9.0, 10.0, 11.0);
+            MgCoordinate coord5 = factory.CreateCoordinateXYZ(12.0, 13.0, 14.0);
+            MgCoordinate coord6 = factory.CreateCoordinateXYZ(15.0, 16.0, 17.0);
+
+            MgCoordinateCollection coordColB = new MgCoordinateCollection();
+            coordColB.Add(coord4);
+            coordColB.Add(coord5);
+            coordColB.Add(coord6);
+
+            MgLineString lineString1 = factory.CreateLineString(coordColA);
+            MgLineString lineString2 = factory.CreateLineString(coordColB);
+
+            MgLineStringCollection lineStrings = new MgLineStringCollection();
+            lineStrings.Add(lineString1);
+            lineStrings.Add(lineString2);
+
+            return factory.CreateMultiLineString(lineStrings);
+        }
+
+
+        public MgMultiPolygon CreateMultiPolygon()
+        {
+
+
+            MgPolygon polygon1 = CreatePolygon(0.0);
+            MgPolygon polygon2 = CreatePolygon(0.0);
+
+            MgPolygonCollection polygons = new MgPolygonCollection();
+            polygons.Add(polygon1);
+            polygons.Add(polygon2);
+
+            return factory.CreateMultiPolygon(polygons);
+        }
+
+
+        MgMultiCurveString CreateMultiCurveString()
+        {
+
+
+            MgCurveString curveString1 = CreateCurveString(100);
+            MgCurveString curveString2 = CreateCurveString(200);
+            MgCurveString curveString3 = CreateCurveString(300);
+
+            MgCurveStringCollection curveStrings = new MgCurveStringCollection();
+            curveStrings.Add(curveString1);
+            curveStrings.Add(curveString2);
+            curveStrings.Add(curveString3);
+
+            return factory.CreateMultiCurveString(curveStrings);
+        }
+
+
+        public MgMultiCurvePolygon CreateMultiCurvePolygon(int numCurvePolys, double offset)
+        {
+
+
+            MgCurvePolygonCollection curvePolys = new MgCurvePolygonCollection();
+            for (int i = 0; i < numCurvePolys; i++)
+            {
+                MgCurvePolygon curvePoly = CreateCurvePolygon(i + offset, 1);
+                curvePolys.Add(curvePoly);
+            }
+
+            return factory.CreateMultiCurvePolygon(curvePolys);
+        }
+
+
+        public MgMultiGeometry CreateMultiGeometry()
+        {
+
+
+            MgGeometryCollection geometries = new MgGeometryCollection();
+            MgGeometry geometry = null;
+
+            // CurvePolygon
+            geometry = (MgGeometry)CreateCurvePolygon(0, 1);
+            geometries.Add(geometry);
+
+            // CurveString
+            // Not doing CurveString because of some unfixed defect.
+            // It may be the same one that sometimes affects MultiPolygon.
+            geometry = (MgGeometry)CreateCurveString(100);
+            geometries.Add(geometry);
+
+            // LineString
+            geometry = (MgGeometry)CreateLineString(1.0);
+            geometries.Add(geometry);
+
+            // Point
+            geometry = (MgGeometry)CreatePoint();
+            geometries.Add(geometry);
+
+            // Polygon
+            geometry = CreatePolygon(0.0);
+            geometries.Add(geometry);
+
+            // Make MultiGeometry from the many geometries collected above.
+            return factory.CreateMultiGeometry(geometries);
+        }
+
+        public MgArcSegment CreateArcSegment(double offset)
+        {
+            MgCoordinate startPos = factory.CreateCoordinateXY(offset + 0.0, offset + 0.0);
+            MgCoordinate midPos = factory.CreateCoordinateXY(offset + 0.0, offset + 1.0);
+            MgCoordinate endPos = factory.CreateCoordinateXY(offset + 1.0, offset + 2.0);
+            return factory.CreateArcSegment(startPos, endPos, midPos);
+        }
+    }
+
+    public class CollectionTestsIntCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgIntCollection coll = new MgIntCollection();
+            coll.Add(10);
+            coll.Add(20);
+            coll.Add(30);
+            coll.Add(40);
+            coll.Add(50);
+            Assert.AreEqual(20, coll.GetItem(1));
+            Assert.AreEqual(30, coll[2]);
+            Assert.AreEqual(5, coll.Count);
+
+            int j = 0;
+            foreach (int i in coll)
+            {
+                j += i;
+            }
+            Assert.AreEqual(150, j);
+        }
+    }
+
+    public class CollectionTestsPropertyCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgPropertyCollection coll = new MgPropertyCollection();
+            MgDoubleProperty dblProp = new MgDoubleProperty("DoubleProp", 1.1111);
+            MgInt32Property intProp = new MgInt32Property("IntProp", 1);
+            MgDateTime dateTime = new MgDateTime(2006, 9, 21);
+            MgDateTimeProperty dateProp = new MgDateTimeProperty("DateProp", dateTime);
+            MgSingleProperty single = new MgSingleProperty("SingleProp", (float)2.2222);
+            coll.Add(dblProp);
+            coll.Add(intProp);
+            coll.Add(dateProp);
+            coll[2] = single;
+
+            Assert.AreEqual(1.1111, (coll[0] as MgDoubleProperty).GetValue());
+            Assert.AreEqual(MgPropertyType.Double, coll[0].GetPropertyType());
+            Assert.AreEqual(MgPropertyType.Int32, coll[1].GetPropertyType());
+            Assert.AreEqual(MgPropertyType.Single, coll[2].GetPropertyType());
+            Assert.AreEqual((float)2.2222, (coll[2] as MgSingleProperty).GetValue());
+            Assert.AreEqual(3, coll.Count);
+
+            Assert.AreEqual(MgPropertyType.Double, coll[0].GetPropertyType());
+
+            string str = "";
+            foreach (MgProperty prop in coll)
+            {
+                str = str + "[" + prop.GetName() + "]";
+            }
+            Assert.AreEqual("[DoubleProp][IntProp][SingleProp]", str);
+        }
+    }
+
+    public class CollectionTestsBatchPropertyCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgPropertyCollection coll1 = new MgPropertyCollection();
+            MgDoubleProperty dblProp = new MgDoubleProperty("DoubleProp", 1.1111);
+            coll1.Add(dblProp);
+
+            MgPropertyCollection coll2 = new MgPropertyCollection();
+            MgInt32Property intProp = new MgInt32Property("IntProp", 1);
+            coll2.Add(intProp);
+
+            MgPropertyCollection coll3 = new MgPropertyCollection();
+            MgSingleProperty single = new MgSingleProperty("SingleProp", (float)2.2222);
+            coll3.Add(single);
+
+
+            MgBatchPropertyCollection coll = new MgBatchPropertyCollection();
+            coll.Add(coll1);
+            coll.Add(coll2);
+
+            Assert.AreEqual(2, coll.Count);
+            Assert.AreEqual(MgPropertyType.Double, coll[0][0].GetPropertyType());
+            Assert.AreEqual(MgPropertyType.Int32, coll[1][0].GetPropertyType());
+
+            coll[1] = coll3;
+
+            string str = "";
+            foreach (MgPropertyCollection c in coll)
+            {
+                str = str + "[" + c[0].GetName() + "]";
+            }
+            Assert.AreEqual("[DoubleProp][SingleProp]", str);
+        }
+    }
+
+    public class CollectionTestsClassDefinitionCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgClassDefinition class1 = new MgClassDefinition();
+            class1.SetName("class1");
+
+            MgClassDefinition class2 = new MgClassDefinition();
+            class2.SetName("class2");
+
+            MgClassDefinition class3 = new MgClassDefinition();
+            class3.SetName("class3");
+
+            MgClassDefinitionCollection coll = new MgClassDefinitionCollection();
+            coll.Insert(0, class3);
+            coll.Insert(0, class2);
+            coll.Insert(0, class1);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.AreEqual("class2", coll[1].GetName());
+
+            MgClassDefinition tmp = coll[0];
+            coll[0] = coll[1];
+            coll[1] = coll[2];
+            coll[2] = tmp;
+
+            string str = "";
+            foreach (MgClassDefinition def in coll)
+            {
+                str = str + "[" + def.GetName() + "]";
+            }
+            Assert.AreEqual("[class2][class3][class1]", str);
+        }
+    }
+
+    public class CollectionTestsCoordinateCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate c1 = gf.CreateCoordinateXY(1.0, 1.0);
+            MgCoordinate c2 = gf.CreateCoordinateXY(2.0, 2.0);
+            MgCoordinate c3 = gf.CreateCoordinateXY(3.0, 3.0);
+            MgCoordinate c4 = gf.CreateCoordinateXY(4.0, 4.0);
+
+            MgCoordinateCollection coll = new MgCoordinateCollection();
+            coll.Add(c1);
+            coll.Insert(1, c2);
+            coll.Add(c3);
+            coll.Add(c4);
+
+            Assert.AreEqual(4, coll.Count);
+            Assert.AreEqual(1.0, coll[0].GetX());
+            coll[3] = coll[2];
+            Assert.AreEqual(3.0, coll[3].GetX());
+
+            double sum = 0.0;
+            foreach (MgCoordinate coord in coll)
+            {
+                sum += coord.GetX();
+            }
+            Assert.AreEqual(9.0, sum);
+        }
+    }
+
+
+    public class CollectionTestsCurvePolygonCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var build = new GeomBuild();
+            MgCurvePolygon geom1 = build.CreateCurvePolygon(2.0, 5);
+            MgCurvePolygon geom2 = build.CreateCurvePolygon(12.0, 5);
+            MgCurvePolygon geom3 = build.CreateCurvePolygon(2.0, 5);
+
+            MgCurvePolygonCollection coll = new MgCurvePolygonCollection();
+            coll.Add(geom1);
+            coll.Add(geom2);
+            coll.Add(geom3);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.IsTrue(geom1.Equals(coll[0]));
+            Assert.IsTrue(coll[0].Equals(coll[2]));
+            Assert.IsFalse(coll[0].Equals(coll[1]));
+            coll[0] = coll[1];
+            Assert.IsTrue(coll[0].Equals(coll[1]));
+
+            double width = 0.0;
+            foreach (MgCurvePolygon geom in coll)
+            {
+                width += geom.Envelope().GetWidth();
+            }
+            Assert.AreEqual(geom1.Envelope().GetWidth() * 3.0, width);
+        }
+    }
+
+    public class CollectionTestsCurveRingCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var build = new GeomBuild();
+            MgCurveRing geom1 = build.CreateCurveRing(2.0);
+            MgCurveRing geom2 = build.CreateCurveRing(12.0);
+            MgCurveRing geom3 = build.CreateCurveRing(2.0);
+
+            MgCurveRingCollection coll = new MgCurveRingCollection();
+            coll.Add(geom1);
+            coll.Add(geom2);
+            coll.Add(geom3);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.IsTrue(geom1.Envelope().Contains(coll[0].Envelope()));
+            Assert.IsTrue(coll[0].Envelope().Contains(coll[2].Envelope()));
+            Assert.IsFalse(coll[0].Envelope().Contains(coll[1].Envelope()));
+            coll[0] = coll[1];
+            Assert.IsTrue(coll[0].Envelope().Contains(coll[1].Envelope()));
+
+            double width = 0.0;
+            foreach (MgCurveRing geom in coll)
+            {
+                width += geom.Envelope().GetWidth();
+            }
+            Assert.AreEqual(geom1.Envelope().GetWidth() * 3.0, width);
+        }
+    }
+
+    public class CollectionTestsCurveSegmentCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var build = new GeomBuild();
+            MgArcSegment geom1 = build.CreateArcSegment(2.0);
+            MgArcSegment geom2 = build.CreateArcSegment(12.0);
+            MgArcSegment geom3 = build.CreateArcSegment(2.0);
+
+            MgCurveSegmentCollection coll = new MgCurveSegmentCollection();
+            coll.Add(geom1);
+            coll.Add(geom2);
+            coll.Add(geom3);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.IsTrue(geom1.Envelope().Contains(coll[0].Envelope()));
+            Assert.IsTrue(coll[0].Envelope().Contains(coll[2].Envelope()));
+            Assert.IsFalse(coll[0].Envelope().Contains(coll[1].Envelope()));
+            coll[0] = coll[1];
+            Assert.IsTrue(coll[0].Envelope().Contains(coll[1].Envelope()));
+
+            double width = 0.0;
+            foreach (MgCurveSegment geom in coll)
+            {
+                width += geom.Envelope().GetWidth();
+            }
+            Assert.AreEqual(geom1.Envelope().GetWidth() * 3.0, width);
+        }
+    }
+
+    public class CollectionTestsCurveStringCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var build = new GeomBuild();
+            MgCurveString geom1 = build.CreateCurveString(2.0);
+            MgCurveString geom2 = build.CreateCurveString(12.0);
+            MgCurveString geom3 = build.CreateCurveString(2.0);
+
+            MgCurveStringCollection coll = new MgCurveStringCollection();
+            coll.Add(geom1);
+            coll.Add(geom2);
+            coll.Add(geom3);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.IsTrue(geom1.Equals(coll[0]));
+            Assert.IsTrue(coll[0].Equals(coll[2]));
+            Assert.IsFalse(coll[0].Equals(coll[1]));
+            coll[0] = coll[1];
+            Assert.IsTrue(coll[0].Equals(coll[1]));
+
+            double width = 0.0;
+            foreach (MgCurveString geom in coll)
+            {
+                width += geom.Envelope().GetWidth();
+            }
+            Assert.AreEqual(geom1.Envelope().GetWidth() * 3.0, width);
+        }
+    }
+
+    public class CollectionTestsFeatureCommandCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgPropertyCollection propVals = new MgPropertyCollection();
+            MgInt32Property prop = new MgInt32Property("prop", 1);
+            propVals.Add(prop);
+            MgUpdateFeatures update = new MgUpdateFeatures("class2", propVals, "where cat < dog");
+            MgInsertFeatures insert = new MgInsertFeatures("class3", propVals);
+            MgDeleteFeatures del = new MgDeleteFeatures("class1", "where cat > dog");
+
+            MgFeatureCommandCollection coll = new MgFeatureCommandCollection();
+            coll.Add(update);
+            coll.Add(insert);
+            coll.Add(del);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.AreEqual(MgFeatureCommandType.DeleteFeatures, coll[2].GetCommandType());
+            coll[0] = coll[1];
+
+            string txt = "";
+            foreach (MgFeatureCommand cmd in coll)
+            {
+                txt += "[" + cmd.GetCommandType() + "]";
+            }
+            Assert.AreEqual("[0][0][2]", txt);
+        }
+    }
+
+    public class CollectionTestsFeatureSchemaCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgFeatureSchema schema1 = new MgFeatureSchema("schema1", "desc1");
+            MgFeatureSchema schema2 = new MgFeatureSchema("schema2", "desc2");
+            MgFeatureSchema schema3 = new MgFeatureSchema("schema3", "desc3");
+
+            MgFeatureSchemaCollection coll = new MgFeatureSchemaCollection();
+            coll.Add(schema1);
+            coll.Add(schema2);
+            coll.Add(schema3);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.AreEqual("schema3", coll[2].GetName());
+            coll[0] = coll[2];
+
+            string txt = "";
+            foreach (MgFeatureSchema schema in coll)
+            {
+                txt += "[" + schema.GetName() + "]";
+            }
+            Assert.AreEqual("[schema3][schema2][schema3]", txt);
+        }
+    }
+
+    public class CollectionTestsGeometryCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var wkt = new MgWktReaderWriter();
+            MgPoint geom1 = wkt.Read("POINT XY (1.0 1.0)") as MgPoint;
+            MgPoint geom2 = wkt.Read("POINT XY (2.0 2.0)") as MgPoint;
+            MgPoint geom3 = wkt.Read("POINT XY (1.0 1.0)") as MgPoint;
+
+            MgGeometryCollection coll = new MgGeometryCollection();
+            coll.Add(geom1);
+            coll.Add(geom2);
+            coll.Add(geom3);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.IsTrue(geom1.Equals(coll[0]));
+            Assert.IsTrue(coll[0].Equals(coll[2]));
+            Assert.IsFalse(coll[0].Equals(coll[1]));
+            coll[0] = coll[1];
+            Assert.IsTrue(coll[0].Equals(coll[1]));
+
+            double x = 0.0;
+            foreach (MgGeometry geom in coll)
+            {
+                x += (geom as MgPoint).GetCoordinate().GetX();
+            }
+            Assert.AreEqual(5.0, x);
+        }
+    }
+
+    public class CollectionTestsLayerCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            logger.WriteLine("Skipping CollectionTestsLayerCollection: Resource Service required to construct MgLayer");
+        }
+    }
+
+    public class CollectionTestsLayerGroupCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            logger.WriteLine("Skipping CollectionTestsLayerCollection: MgLayerGroupCollection constructor not visible");
+            /*
+            MgLayerGroup group1 = new MgLayerGroup("group1");
+            MgLayerGroup group2 = new MgLayerGroup("group2");
+            MgLayerGroup group3 = new MgLayerGroup("group3");
+            MgLayerGroup group4 = new MgLayerGroup("group4");
+
+            MgLayerGroupCollection coll = new MgLayerGroupCollection();
+            coll.Add(group1);
+            coll.Insert(1, group2);
+            coll.Add(group3);
+            coll.Insert(3, group4);
+
+            Assert.AreEqual(4, coll.Count);
+            Assert.AreEqual(coll[2].GetName(), "group3");
+
+            coll[1] = coll[2];
+
+            string txt = "";
+            foreach (MgLayerGroup group in coll)
+            {
+                txt += "[" + group.GetName() + "]";
+            }
+            Assert.AreEqual("[group1][group3][group3][group4]", txt);
+            */
+        }
+    }
+
+    public class CollectionTestsLinearRingCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var build = new GeomBuild();
+            MgLinearRing geom1 = build.CreateLinearRing(1.0);
+            MgLinearRing geom2 = build.CreateLinearRing(5.0);
+            MgLinearRing geom3 = build.CreateLinearRing(1.0);
+
+            MgLinearRingCollection coll = new MgLinearRingCollection();
+            coll.Add(geom1);
+            coll.Add(geom2);
+            coll.Add(geom3);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.IsTrue(geom1.Envelope().Contains(coll[0].Envelope()));
+            Assert.IsTrue(coll[0].Envelope().Contains(coll[2].Envelope()));
+            Assert.IsFalse(coll[0].Envelope().Contains(coll[1].Envelope()));
+            coll[0] = coll[1];
+            Assert.IsTrue(coll[0].Envelope().Contains(coll[1].Envelope()));
+
+            double width = 0.0;
+            foreach (MgLinearRing geom in coll)
+            {
+                width += geom.Envelope().GetWidth();
+            }
+            Assert.AreEqual(geom1.Envelope().GetWidth() * 3.0, width);
+        }
+    }
+
+    public class CollectionTestsLineStringCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var build = new GeomBuild();
+            MgLineString geom1 = build.CreateLineString(5.0);
+            MgLineString geom2 = build.CreateLineString(11.0);
+            MgLineString geom3 = build.CreateLineString(5.0);
+
+            MgLineStringCollection coll = new MgLineStringCollection();
+            coll.Add(geom1);
+            coll.Add(geom2);
+            coll.Add(geom3);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.IsTrue(geom1.Equals(coll[0]));
+            Assert.IsTrue(coll[0].Equals(coll[2]));
+            Assert.IsFalse(coll[0].Equals(coll[1]));
+            coll[0] = coll[1];
+            Assert.IsTrue(coll[0].Equals(coll[1]));
+
+            double width = 0.0;
+            foreach (MgLineString geom in coll)
+            {
+                width += geom.Envelope().GetWidth();
+            }
+            Assert.AreEqual(geom1.Envelope().GetWidth() * 3.0, width);
+        }
+    }
+
+    public class CollectionTestsMapCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            string coordsys = "GEOGCS[\"LL84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]";
+            MgEnvelope env = new MgEnvelope(10, 10, 20, 20);
+
+
+
+            MgMapBase map1 = factory.CreateMap(coordsys, env, "map1");
+            MgMapBase map2 = factory.CreateMap(coordsys, env, "map2");
+            MgMapBase map3 = factory.CreateMap(coordsys, env, "map3");
+            MgMapBase map4 = factory.CreateMap(coordsys, env, "map4");
+
+            MgMapCollection coll = new MgMapCollection();
+            coll.Add(map1);
+            coll.Insert(1, map2);
+            coll.Add(map3);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.AreEqual(coll[2].GetName(), "map3");
+
+            coll[1] = map4;
+
+
+            string txt = "";
+            foreach (MgMapBase map in coll)
+            {
+                txt += "[" + map.GetName() + "]";
+            }
+            Assert.AreEqual("[map1][map4][map3]", txt);
+        }
+    }
+
+    public class CollectionTestsPointCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var wkt = new MgWktReaderWriter();
+            MgPoint geom1 = wkt.Read("POINT XY (1.0 1.0)") as MgPoint;
+            MgPoint geom2 = wkt.Read("POINT XY (2.0 2.0)") as MgPoint;
+            MgPoint geom3 = wkt.Read("POINT XY (1.0 1.0)") as MgPoint;
+
+            MgPointCollection coll = new MgPointCollection();
+            coll.Add(geom1);
+            coll.Add(geom2);
+            coll.Add(geom3);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.IsTrue(geom1.Equals(coll[0]));
+            Assert.IsTrue(coll[0].Equals(coll[2]));
+            Assert.IsFalse(coll[0].Equals(coll[1]));
+            coll[0] = coll[1];
+            Assert.IsTrue(coll[0].Equals(coll[1]));
+
+            double x = 0.0;
+            foreach (MgPoint geom in coll)
+            {
+                x += geom.GetCoordinate().GetX();
+            }
+            Assert.AreEqual(5.0, x);
+        }
+    }
+
+    public class CollectionTestsPolygonCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var build = new GeomBuild();
+            MgPolygon geom1 = build.CreatePolygon(2.0);
+            MgPolygon geom2 = build.CreatePolygon(12.0);
+            MgPolygon geom3 = build.CreatePolygon(2.0);
+
+            MgPolygonCollection coll = new MgPolygonCollection();
+            coll.Add(geom1);
+            coll.Add(geom2);
+            coll.Add(geom3);
+
+            Assert.AreEqual(3, coll.Count);
+            Assert.IsTrue(geom1.Equals(coll[0]));
+            Assert.IsTrue(coll[0].Equals(coll[2]));
+            Assert.IsFalse(coll[0].Equals(coll[1]));
+            coll[0] = coll[1];
+            Assert.IsTrue(coll[0].Equals(coll[1]));
+
+            double width = 0.0;
+            foreach (MgPolygon geom in coll)
+            {
+                width += geom.Envelope().GetWidth();
+            }
+            Assert.AreEqual(geom1.Envelope().GetWidth() * 3.0, width);
+        }
+    }
+
+    public class CollectionTestsPropertyDefinitionCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgDataPropertyDefinition def1 = new MgDataPropertyDefinition("def1");
+            MgDataPropertyDefinition def2 = new MgDataPropertyDefinition("def2");
+            MgDataPropertyDefinition def3 = new MgDataPropertyDefinition("def3");
+            MgDataPropertyDefinition def4 = new MgDataPropertyDefinition("def4");
+
+            MgPropertyDefinitionCollection coll = new MgPropertyDefinitionCollection();
+            coll.Add(def1);
+            coll.Add(def2);
+            coll.Insert(2, def4);
+            coll.Insert(2, def3);
+
+            Assert.AreEqual(4, coll.Count);
+            Assert.AreEqual("def1", coll[0].GetName());
+
+            MgPropertyDefinition tmp = coll[0];
+            coll.Remove(def1);
+            Assert.AreEqual(3, coll.Count);
+            coll.Insert(0, tmp);
+
+
+            string txt = "";
+            foreach (MgPropertyDefinition def in coll)
+            {
+                txt += "[" + def.GetName() + "]";
+            }
+            Assert.AreEqual("[def1][def2][def3][def4]", txt);
+        }
+    }
+
+    public class CollectionTestsReadOnlyLayerCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            logger.WriteLine("Skipping CollectionTestsReadOnlyLayerCollection: Resource Service required to construct MgLayer");
+        }
+    }
+
+    public class CollectionTestsStringCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var strColl = new List<string>();
+            strColl.Add("string1");
+            strColl.Add("string2");
+            strColl.Add("string3");
+            strColl.Add("string3");
+
+            MgStringCollection coll1 = new MgStringCollection();
+            coll1.Add("Hello");
+
+            MgStringCollection coll2 = new MgStringCollection(strColl);
+            Assert.AreEqual(4, coll2.GetCount());
+            IList<string> coll3 = coll2;
+            Assert.AreEqual(4, coll3.Count);
+            for (int i = 0; i < 4; i++)
+            {
+                Assert.AreEqual(strColl[i], coll2.GetItem(i));
+                Assert.AreEqual(strColl[i], coll3[i]);
+            }
+
+        }
+    }
+
+    public class CollectionTestsStringPropertyCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgStringProperty prop1 = new MgStringProperty("prop1", "val1");
+            MgStringProperty prop2 = new MgStringProperty("prop2", "val2");
+            MgStringProperty prop3 = new MgStringProperty("prop3", "val3");
+            MgStringProperty prop4 = new MgStringProperty("prop4", "val4");
+
+            MgStringPropertyCollection coll = new MgStringPropertyCollection();
+            coll.Add(prop1);
+            coll.Add(prop2);
+            coll.Remove(prop3);
+            coll.Remove(coll[1]);
+            Assert.AreEqual(1, coll.Count);
+            coll.Add(prop3);
+            coll[1] = prop2;
+            coll.Insert(2, prop3);
+            Assert.AreEqual(2, coll.IndexOf(prop3));
+            coll.Add(prop4);
+
+            string txt = "";
+            foreach (MgStringProperty prop in coll)
+            {
+                txt += "[" + prop.GetName() + "]";
+            }
+            Assert.AreEqual("[prop1][prop2][prop3][prop4]", txt);
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/ConvenienceTests.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/ConvenienceTests.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/ConvenienceTests.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,1324 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common.ExternalTests
+{
+    public class FeatureServiceTestUtil
+    {
+        public static string CreateTestDataStore(MgResourceIdentifier fsId, string provider, MgFeatureService featSvc)
+        {
+            string scName = "Default";
+            var csFactory = new MgCoordinateSystemFactory();
+            string wkt = csFactory.ConvertCoordinateSystemCodeToWkt("LL84");
+            var schema = new MgFeatureSchema("Default", "Default Schema");
+            var cls = new MgClassDefinition();
+            cls.SetName("Test");
+            var classes = schema.GetClasses();
+            classes.Add(cls);
+
+            var clsProps = cls.GetProperties();
+            var idProps = cls.GetIdentityProperties();
+
+            var id = new MgDataPropertyDefinition("ID");
+            id.SetAutoGeneration(true);
+            id.SetDataType(MgPropertyType.Int32);
+
+            var name = new MgDataPropertyDefinition("Name");
+            name.SetDataType(MgPropertyType.String);
+            name.SetLength(255);
+            name.SetNullable(false);
+
+            var geom = new MgGeometricPropertyDefinition("Geometry");
+            geom.SetGeometryTypes(MgFeatureGeometricType.Point);
+            geom.SetSpatialContextAssociation(scName);
+
+            clsProps.Add(id);
+            clsProps.Add(name);
+            clsProps.Add(geom);
+
+            idProps.Add(id);
+
+            cls.SetDefaultGeometryPropertyName("Geometry");
+
+            var fsp = new MgFileFeatureSourceParams(provider, scName, wkt, schema);
+            featSvc.CreateFeatureSource(fsId, fsp);
+
+            var schemas = featSvc.DescribeSchema(fsId, "");
+            classes = schemas[0].GetClasses();
+            return schemas[0].Name + ":" + classes[0].Name;
+        }
+    }
+
+    public class FeatureServiceInsertFeatures : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/FeatureServiceInsertFeatures.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SDF", featSvc);
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var props = new MgPropertyCollection();
+                var nameP = new MgStringProperty("Name", "Test");
+                var geom = wktRw.Read("POINT (1 1)");
+                var agf = agfRw.Write(geom);
+                var geomP = new MgGeometryProperty("Geometry", agf);
+                props.Add(nameP);
+                props.Add(geomP);
+
+                var fr = featSvc.InsertFeatures(fsId, className, props);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(1, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class FeatureServiceInsertFeaturesWithTransaction : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/FeatureServiceInsertFeaturesWithTransaction.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SQLite", featSvc);
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var props = new MgPropertyCollection();
+                var nameP = new MgStringProperty("Name", "Test");
+                var geom = wktRw.Read("POINT (1 1)");
+                var agf = agfRw.Write(geom);
+                var geomP = new MgGeometryProperty("Geometry", agf);
+                props.Add(nameP);
+                props.Add(geomP);
+                var trans = featSvc.BeginTransaction(fsId);
+                var fr = featSvc.InsertFeatures(fsId, className, props, trans);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(1, count);
+                trans.Rollback();
+
+                fr = featSvc.SelectFeatures(fsId, className, null);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(0, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class FeatureServiceInsertFeaturesBatched : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/FeatureServiceInsertFeaturesBatched.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SDF", featSvc);
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var batchProps = new MgBatchPropertyCollection();
+                for (int i = 0; i < 5; i++)
+                {
+                    var props = new MgPropertyCollection();
+                    var nameP = new MgStringProperty("Name", "Test" + (i + 1));
+                    var geom = wktRw.Read("POINT (1 1)");
+                    var agf = agfRw.Write(geom);
+                    var geomP = new MgGeometryProperty("Geometry", agf);
+                    props.Add(nameP);
+                    props.Add(geomP);
+                    batchProps.Add(props);
+                }
+
+                var fr = featSvc.InsertFeatures(fsId, className, batchProps);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class FeatureServiceInsertFeaturesBatchedWithTransaction : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/FeatureServiceInsertFeaturesBatchedWithTransaction.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SQLite", featSvc);
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var batchProps = new MgBatchPropertyCollection();
+                for (int i = 0; i < 5; i++)
+                {
+                    var props = new MgPropertyCollection();
+                    var nameP = new MgStringProperty("Name", "Test" + (i + 1));
+                    var geom = wktRw.Read("POINT (1 1)");
+                    var agf = agfRw.Write(geom);
+                    var geomP = new MgGeometryProperty("Geometry", agf);
+                    props.Add(nameP);
+                    props.Add(geomP);
+                    batchProps.Add(props);
+                }
+
+                var trans = featSvc.BeginTransaction(fsId);
+                var fr = featSvc.InsertFeatures(fsId, className, batchProps, trans);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+                trans.Rollback();
+
+                fr = featSvc.SelectFeatures(fsId, className, null);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(0, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class FeatureServiceUpdateFeatures : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/FeatureServiceUpdateFeatures.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SDF", featSvc);
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var batchProps = new MgBatchPropertyCollection();
+                for (int i = 0; i < 5; i++)
+                {
+                    var props = new MgPropertyCollection();
+                    var nameP = new MgStringProperty("Name", "Test" + (i + 1));
+                    var geom = wktRw.Read("POINT (1 1)");
+                    var agf = agfRw.Write(geom);
+                    var geomP = new MgGeometryProperty("Geometry", agf);
+                    props.Add(nameP);
+                    props.Add(geomP);
+                    batchProps.Add(props);
+                }
+
+                var fr = featSvc.InsertFeatures(fsId, className, batchProps);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+
+                var updateProps = new MgPropertyCollection();
+                var newName = new MgStringProperty("Name", "Test3Updated");
+                updateProps.Add(newName);
+
+                int updated = featSvc.UpdateMatchingFeatures(fsId, className, updateProps, "Name = 'Test3'");
+                Assert.AreEqual(1, updated);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class FeatureServiceUpdateFeaturesWithTransaction : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/FeatureServiceUpdateFeaturesWithTransaction.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SQLite", featSvc);
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var batchProps = new MgBatchPropertyCollection();
+                for (int i = 0; i < 5; i++)
+                {
+                    var props = new MgPropertyCollection();
+                    var nameP = new MgStringProperty("Name", "Test" + (i + 1));
+                    var geom = wktRw.Read("POINT (1 1)");
+                    var agf = agfRw.Write(geom);
+                    var geomP = new MgGeometryProperty("Geometry", agf);
+                    props.Add(nameP);
+                    props.Add(geomP);
+                    batchProps.Add(props);
+                }
+
+                var fr = featSvc.InsertFeatures(fsId, className, batchProps);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+
+                var updateProps = new MgPropertyCollection();
+                var newName = new MgStringProperty("Name", "Test3Updated");
+                updateProps.Add(newName);
+
+                var trans = featSvc.BeginTransaction(fsId);
+                int updated = featSvc.UpdateMatchingFeatures(fsId, className, updateProps, "Name = 'Test3'", trans);
+                Assert.AreEqual(1, updated);
+                trans.Rollback();
+
+                var query = new MgFeatureQueryOptions();
+                query.SetFilter("Name = 'Test3Updated'");
+                fr = featSvc.SelectFeatures(fsId, className, query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(0, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class FeatureServiceDeleteFeatures : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/FeatureServiceDeleteFeatures.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SDF", featSvc);
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var batchProps = new MgBatchPropertyCollection();
+                for (int i = 0; i < 5; i++)
+                {
+                    var props = new MgPropertyCollection();
+                    var nameP = new MgStringProperty("Name", "Test" + (i + 1));
+                    var geom = wktRw.Read("POINT (1 1)");
+                    var agf = agfRw.Write(geom);
+                    var geomP = new MgGeometryProperty("Geometry", agf);
+                    props.Add(nameP);
+                    props.Add(geomP);
+                    batchProps.Add(props);
+                }
+
+                var fr = featSvc.InsertFeatures(fsId, className, batchProps);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+
+                int deleted = featSvc.DeleteFeatures(fsId, className, "Name = 'Test3'");
+                Assert.AreEqual(1, deleted);
+
+                var query = new MgFeatureQueryOptions();
+                fr = featSvc.SelectFeatures(fsId, className, query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(4, count);
+
+                query.SetFilter("Name = 'Test3'");
+                fr = featSvc.SelectFeatures(fsId, className, query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(0, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class FeatureServiceDeleteFeaturesWithTransaction : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/FeatureServiceDeleteFeaturesWithTransaction.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SQLite", featSvc);
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var batchProps = new MgBatchPropertyCollection();
+                for (int i = 0; i < 5; i++)
+                {
+                    var props = new MgPropertyCollection();
+                    var nameP = new MgStringProperty("Name", "Test" + (i + 1));
+                    var geom = wktRw.Read("POINT (1 1)");
+                    var agf = agfRw.Write(geom);
+                    var geomP = new MgGeometryProperty("Geometry", agf);
+                    props.Add(nameP);
+                    props.Add(geomP);
+                    batchProps.Add(props);
+                }
+
+                var fr = featSvc.InsertFeatures(fsId, className, batchProps);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+
+                var trans = featSvc.BeginTransaction(fsId);
+                int deleted = featSvc.DeleteFeatures(fsId, className, "Name = 'Test3'", trans);
+                Assert.AreEqual(1, deleted);
+
+                var query = new MgFeatureQueryOptions();
+                fr = featSvc.SelectFeatures(fsId, className, query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(4, count);
+                trans.Rollback();
+
+                query.SetFilter("Name = 'Test3'");
+                fr = featSvc.SelectFeatures(fsId, className, query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(1, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class LayerInsertFeatures : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/LayerInsertFeatures.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SDF", featSvc);
+
+                MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
+                MgMapBase map = factory.CreateMap(csFactory.ConvertCoordinateSystemCodeToWkt("LL84"), new MgEnvelope(-180, -90, 180, 90), "TestMap");
+                MgLayerCollection layers = map.GetLayers();
+
+                string layerXml = string.Format(PlatformResources.TestLayer, fsId.ToString(), className, "Geometry");
+                byte[] bytes = Encoding.UTF8.GetBytes(layerXml);
+                MgByteSource bs = new MgByteSource(bytes, bytes.Length);
+                MgByteReader br = bs.GetReader();
+
+                var ldfId = new MgResourceIdentifier("Library://UnitTests/Layers/TestLayer.LayerDefinition");
+                resSvc.SetResource(ldfId, br, null);
+
+                MgLayerBase layer = factory.CreateLayer(ldfId);
+                layers.Insert(0, layer);
+
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var props = new MgPropertyCollection();
+                var nameP = new MgStringProperty("Name", "Test");
+                var geom = wktRw.Read("POINT (1 1)");
+                var agf = agfRw.Write(geom);
+                var geomP = new MgGeometryProperty("Geometry", agf);
+                props.Add(nameP);
+                props.Add(geomP);
+
+                var fr = layer.InsertFeatures(props, null);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(1, count);
+
+                var query = new MgFeatureQueryOptions();
+                fr = layer.SelectFeatures(query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(1, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class LayerInsertFeaturesWithTransaction : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/LayerInsertFeaturesWithTransaction.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SQLite", featSvc);
+
+                MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
+                MgMapBase map = factory.CreateMap(csFactory.ConvertCoordinateSystemCodeToWkt("LL84"), new MgEnvelope(-180, -90, 180, 90), "TestMap");
+                MgLayerCollection layers = map.GetLayers();
+
+                string layerXml = string.Format(PlatformResources.TestLayer, fsId.ToString(), className, "Geometry");
+                byte[] bytes = Encoding.UTF8.GetBytes(layerXml);
+                MgByteSource bs = new MgByteSource(bytes, bytes.Length);
+                MgByteReader br = bs.GetReader();
+
+                var ldfId = new MgResourceIdentifier("Library://UnitTests/Layers/TestLayer.LayerDefinition");
+                resSvc.SetResource(ldfId, br, null);
+
+                MgLayerBase layer = factory.CreateLayer(ldfId);
+                layers.Insert(0, layer);
+
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var props = new MgPropertyCollection();
+                var nameP = new MgStringProperty("Name", "Test");
+                var geom = wktRw.Read("POINT (1 1)");
+                var agf = agfRw.Write(geom);
+                var geomP = new MgGeometryProperty("Geometry", agf);
+                props.Add(nameP);
+                props.Add(geomP);
+
+                var trans = layer.BeginTransaction();
+                var fr = layer.InsertFeatures(props, trans);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(1, count);
+                trans.Rollback();
+
+                var query = new MgFeatureQueryOptions();
+                fr = layer.SelectFeatures(query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(0, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class LayerInsertFeaturesBatched : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/LayerInsertFeaturesBatched.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SDF", featSvc);
+
+                MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
+                MgMapBase map = factory.CreateMap(csFactory.ConvertCoordinateSystemCodeToWkt("LL84"), new MgEnvelope(-180, -90, 180, 90), "TestMap");
+                MgLayerCollection layers = map.GetLayers();
+
+                string layerXml = string.Format(PlatformResources.TestLayer, fsId.ToString(), className, "Geometry");
+                byte[] bytes = Encoding.UTF8.GetBytes(layerXml);
+                MgByteSource bs = new MgByteSource(bytes, bytes.Length);
+                MgByteReader br = bs.GetReader();
+
+                var ldfId = new MgResourceIdentifier("Library://UnitTests/Layers/TestLayer.LayerDefinition");
+                resSvc.SetResource(ldfId, br, null);
+
+                MgLayerBase layer = factory.CreateLayer(ldfId);
+                layers.Insert(0, layer);
+
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var batchProps = new MgBatchPropertyCollection();
+                for (int i = 0; i < 5; i++)
+                {
+                    var props = new MgPropertyCollection();
+                    var nameP = new MgStringProperty("Name", "Test" + (i + 1));
+                    var geom = wktRw.Read("POINT (1 1)");
+                    var agf = agfRw.Write(geom);
+                    var geomP = new MgGeometryProperty("Geometry", agf);
+                    props.Add(nameP);
+                    props.Add(geomP);
+                    batchProps.Add(props);
+                }
+
+                var fr = layer.InsertFeatures(batchProps, null);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+
+                var query = new MgFeatureQueryOptions();
+                fr = layer.SelectFeatures(query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class LayerInsertFeaturesBatchedWithTransaction : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/LayerInsertFeaturesBatchedWithTransaction.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SQLite", featSvc);
+
+                MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
+                MgMapBase map = factory.CreateMap(csFactory.ConvertCoordinateSystemCodeToWkt("LL84"), new MgEnvelope(-180, -90, 180, 90), "TestMap");
+                MgLayerCollection layers = map.GetLayers();
+
+                string layerXml = string.Format(PlatformResources.TestLayer, fsId.ToString(), className, "Geometry");
+                byte[] bytes = Encoding.UTF8.GetBytes(layerXml);
+                MgByteSource bs = new MgByteSource(bytes, bytes.Length);
+                MgByteReader br = bs.GetReader();
+
+                var ldfId = new MgResourceIdentifier("Library://UnitTests/Layers/TestLayer.LayerDefinition");
+                resSvc.SetResource(ldfId, br, null);
+
+                MgLayerBase layer = factory.CreateLayer(ldfId);
+                layers.Insert(0, layer);
+
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var batchProps = new MgBatchPropertyCollection();
+                for (int i = 0; i < 5; i++)
+                {
+                    var props = new MgPropertyCollection();
+                    var nameP = new MgStringProperty("Name", "Test" + (i + 1));
+                    var geom = wktRw.Read("POINT (1 1)");
+                    var agf = agfRw.Write(geom);
+                    var geomP = new MgGeometryProperty("Geometry", agf);
+                    props.Add(nameP);
+                    props.Add(geomP);
+                    batchProps.Add(props);
+                }
+
+                var trans = layer.BeginTransaction();
+                var fr = layer.InsertFeatures(batchProps, trans);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+                trans.Rollback();
+
+                var query = new MgFeatureQueryOptions();
+                fr = layer.SelectFeatures(query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(0, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class LayerUpdateFeatures : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/LayerUpdateFeatures.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SDF", featSvc);
+
+                MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
+                MgMapBase map = factory.CreateMap(csFactory.ConvertCoordinateSystemCodeToWkt("LL84"), new MgEnvelope(-180, -90, 180, 90), "TestMap");
+                MgLayerCollection layers = map.GetLayers();
+
+                string layerXml = string.Format(PlatformResources.TestLayer, fsId.ToString(), className, "Geometry");
+                byte[] bytes = Encoding.UTF8.GetBytes(layerXml);
+                MgByteSource bs = new MgByteSource(bytes, bytes.Length);
+                MgByteReader br = bs.GetReader();
+
+                var ldfId = new MgResourceIdentifier("Library://UnitTests/Layers/TestLayer.LayerDefinition");
+                resSvc.SetResource(ldfId, br, null);
+
+                MgLayerBase layer = factory.CreateLayer(ldfId);
+                layers.Insert(0, layer);
+
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var batchProps = new MgBatchPropertyCollection();
+                for (int i = 0; i < 5; i++)
+                {
+                    var props = new MgPropertyCollection();
+                    var nameP = new MgStringProperty("Name", "Test" + (i + 1));
+                    var geom = wktRw.Read("POINT (1 1)");
+                    var agf = agfRw.Write(geom);
+                    var geomP = new MgGeometryProperty("Geometry", agf);
+                    props.Add(nameP);
+                    props.Add(geomP);
+                    batchProps.Add(props);
+                }
+
+                var fr = layer.InsertFeatures(batchProps, null);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+
+                var updateProps = new MgPropertyCollection();
+                var newName = new MgStringProperty("Name", "Test3Updated");
+                updateProps.Add(newName);
+
+                int updated = layer.UpdateMatchingFeatures(updateProps, "Name = 'Test3'", null);
+                Assert.AreEqual(1, updated);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class LayerUpdateFeaturesWithTransaction : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/LayerUpdateFeaturesWithTransaction.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SQLite", featSvc);
+
+                MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
+                MgMapBase map = factory.CreateMap(csFactory.ConvertCoordinateSystemCodeToWkt("LL84"), new MgEnvelope(-180, -90, 180, 90), "TestMap");
+                MgLayerCollection layers = map.GetLayers();
+
+                string layerXml = string.Format(PlatformResources.TestLayer, fsId.ToString(), className, "Geometry");
+                byte[] bytes = Encoding.UTF8.GetBytes(layerXml);
+                MgByteSource bs = new MgByteSource(bytes, bytes.Length);
+                MgByteReader br = bs.GetReader();
+
+                var ldfId = new MgResourceIdentifier("Library://UnitTests/Layers/TestLayer.LayerDefinition");
+                resSvc.SetResource(ldfId, br, null);
+
+                MgLayerBase layer = factory.CreateLayer(ldfId);
+                layers.Insert(0, layer);
+
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var batchProps = new MgBatchPropertyCollection();
+                for (int i = 0; i < 5; i++)
+                {
+                    var props = new MgPropertyCollection();
+                    var nameP = new MgStringProperty("Name", "Test" + (i + 1));
+                    var geom = wktRw.Read("POINT (1 1)");
+                    var agf = agfRw.Write(geom);
+                    var geomP = new MgGeometryProperty("Geometry", agf);
+                    props.Add(nameP);
+                    props.Add(geomP);
+                    batchProps.Add(props);
+                }
+
+                var fr = layer.InsertFeatures(batchProps, null);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+
+                var updateProps = new MgPropertyCollection();
+                var newName = new MgStringProperty("Name", "Test3Updated");
+                updateProps.Add(newName);
+
+                var trans = layer.BeginTransaction();
+                int updated = layer.UpdateMatchingFeatures(updateProps, "Name = 'Test3'", trans);
+                Assert.AreEqual(1, updated);
+
+                trans.Rollback();
+                var query = new MgFeatureQueryOptions();
+                query.SetFilter("Name = 'Test3Updated'");
+                fr = layer.SelectFeatures(query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(0, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class LayerDeleteFeatures : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/LayerDeleteFeatures.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SDF", featSvc);
+
+                MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
+                MgMapBase map = factory.CreateMap(csFactory.ConvertCoordinateSystemCodeToWkt("LL84"), new MgEnvelope(-180, -90, 180, 90), "TestMap");
+                MgLayerCollection layers = map.GetLayers();
+
+                string layerXml = string.Format(PlatformResources.TestLayer, fsId.ToString(), className, "Geometry");
+                byte[] bytes = Encoding.UTF8.GetBytes(layerXml);
+                MgByteSource bs = new MgByteSource(bytes, bytes.Length);
+                MgByteReader br = bs.GetReader();
+
+                var ldfId = new MgResourceIdentifier("Library://UnitTests/Layers/TestLayer.LayerDefinition");
+                resSvc.SetResource(ldfId, br, null);
+
+                MgLayerBase layer = factory.CreateLayer(ldfId);
+                layers.Insert(0, layer);
+
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var batchProps = new MgBatchPropertyCollection();
+                for (int i = 0; i < 5; i++)
+                {
+                    var props = new MgPropertyCollection();
+                    var nameP = new MgStringProperty("Name", "Test" + (i + 1));
+                    var geom = wktRw.Read("POINT (1 1)");
+                    var agf = agfRw.Write(geom);
+                    var geomP = new MgGeometryProperty("Geometry", agf);
+                    props.Add(nameP);
+                    props.Add(geomP);
+                    batchProps.Add(props);
+                }
+
+                var fr = layer.InsertFeatures(batchProps, null);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+
+                int deleted = layer.DeleteFeatures("Name = 'Test3'", null);
+                Assert.AreEqual(1, deleted);
+
+                var query = new MgFeatureQueryOptions();
+                fr = layer.SelectFeatures(query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(4, count);
+
+                query.SetFilter("Name = 'Test3'");
+                fr = layer.SelectFeatures(query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(0, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class LayerDeleteFeaturesWithTransaction : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/LayerDeleteFeaturesWithTransaction.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SQLite", featSvc);
+
+                MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
+                MgMapBase map = factory.CreateMap(csFactory.ConvertCoordinateSystemCodeToWkt("LL84"), new MgEnvelope(-180, -90, 180, 90), "TestMap");
+                MgLayerCollection layers = map.GetLayers();
+
+                string layerXml = string.Format(PlatformResources.TestLayer, fsId.ToString(), className, "Geometry");
+                byte[] bytes = Encoding.UTF8.GetBytes(layerXml);
+                MgByteSource bs = new MgByteSource(bytes, bytes.Length);
+                MgByteReader br = bs.GetReader();
+
+                var ldfId = new MgResourceIdentifier("Library://UnitTests/Layers/TestLayer.LayerDefinition");
+                resSvc.SetResource(ldfId, br, null);
+
+                MgLayerBase layer = factory.CreateLayer(ldfId);
+                layers.Insert(0, layer);
+
+                var agfRw = new MgAgfReaderWriter();
+                var wktRw = new MgWktReaderWriter();
+
+                var batchProps = new MgBatchPropertyCollection();
+                for (int i = 0; i < 5; i++)
+                {
+                    var props = new MgPropertyCollection();
+                    var nameP = new MgStringProperty("Name", "Test" + (i + 1));
+                    var geom = wktRw.Read("POINT (1 1)");
+                    var agf = agfRw.Write(geom);
+                    var geomP = new MgGeometryProperty("Geometry", agf);
+                    props.Add(nameP);
+                    props.Add(geomP);
+                    batchProps.Add(props);
+                }
+
+                var fr = layer.InsertFeatures(batchProps, null);
+                int count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+
+                var trans = layer.BeginTransaction();
+                int deleted = layer.DeleteFeatures("Name = 'Test3'", trans);
+                Assert.AreEqual(1, deleted);
+                trans.Rollback();
+
+                var query = new MgFeatureQueryOptions();
+                fr = layer.SelectFeatures(query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(5, count);
+
+                query.SetFilter("Name = 'Test3'");
+                fr = layer.SelectFeatures(query);
+                count = 0;
+                while (fr.ReadNext())
+                {
+                    count++;
+                }
+                fr.Close();
+                Assert.AreEqual(1, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class SelectFeaturesWithTransform : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/TransformTest.FeatureSource");
+            try
+            {
+                //Create our test data store
+                MgFeatureSchema fs = new MgFeatureSchema("Default", "");
+                MgClassDefinition cls = new MgClassDefinition();
+                cls.SetName("Test");
+
+                MgDataPropertyDefinition id = new MgDataPropertyDefinition("ID");
+                id.SetAutoGeneration(true);
+                id.SetDataType(MgPropertyType.Int32);
+
+                MgGeometricPropertyDefinition geom = new MgGeometricPropertyDefinition("Geometry");
+                geom.SetGeometryTypes(MgFeatureGeometricType.Point);
+                geom.SetSpatialContextAssociation("Default");
+
+                MgPropertyDefinitionCollection props = cls.GetProperties();
+                MgPropertyDefinitionCollection idProps = cls.GetIdentityProperties();
+
+                props.Add(id);
+                props.Add(geom);
+                idProps.Add(id);
+
+                cls.SetDefaultGeometryPropertyName("Geometry");
+                MgClassDefinitionCollection classes = fs.GetClasses();
+                classes.Add(cls);
+
+                //We'll use a transform guaranteed to work. ArbitraryXY (unitA to unitB)
+                //We just check that the transformed values are not the original, that way
+                //we know that CS-Map did its job
+                MgCoordinateSystemFactory csFact = new MgCoordinateSystemFactory();
+                string srcWkt = csFact.ConvertCoordinateSystemCodeToWkt("XY-M");
+                string dstWkt = csFact.ConvertCoordinateSystemCodeToWkt("XY-IN");
+
+                MgGeometryFactory geomFact = new MgGeometryFactory();
+                MgAgfReaderWriter agfRw = new MgAgfReaderWriter();
+                MgFileFeatureSourceParams create = new MgFileFeatureSourceParams("OSGeo.SDF", "Default", srcWkt, fs);
+                featSvc.CreateFeatureSource(fsId, create);
+
+                //Populate data store with test points
+
+                MgCoordinate coord1 = geomFact.CreateCoordinateXY(-37.1020, 144.0020);
+                MgPoint pt1 = geomFact.CreatePoint(coord1);
+                MgByteReader agf1 = agfRw.Write(pt1);
+
+                MgPropertyCollection propVals = new MgPropertyCollection();
+                MgGeometryProperty pGeom = new MgGeometryProperty("Geometry", agf1);
+                propVals.Add(pGeom);
+
+                MgFeatureReader fr = featSvc.InsertFeatures(fsId, "Default:Test", propVals);
+                fr.Close();
+
+                MgCoordinate coord2 = geomFact.CreateCoordinateXY(-37.2020, 144.2020);
+                MgPoint pt2 = geomFact.CreatePoint(coord2);
+                MgByteReader agf2 = agfRw.Write(pt2);
+
+                pGeom.SetValue(agf2);
+                fr = featSvc.InsertFeatures(fsId, "Default:Test", propVals);
+                fr.Close();
+
+                //Now select from this data store
+                MgFeatureQueryOptions query = new MgFeatureQueryOptions();
+                MgReader reader = featSvc.SelectFeatures(fsId, "Default:Test", query, dstWkt);
+
+                Assert.IsTrue(reader.ReadNext());
+                Assert.IsTrue(!reader.IsNull("Geometry"));
+
+                MgByteReader txAgf1 = reader.GetGeometry("Geometry");
+                MgGeometry txGeom1 = agfRw.Read(txAgf1);
+                MgPoint txPt1 = (MgPoint)txGeom1;
+                Assert.IsTrue(txPt1 != null);
+                MgCoordinate txCoord1 = txPt1.GetCoordinate();
+
+                //TODO: Maybe we should really check that it matches the expected transformed result
+                Assert.IsTrue(txCoord1.GetX() != -37.1020);
+                Assert.IsTrue(txCoord1.GetY() != 144.0020);
+
+                Assert.IsTrue(reader.ReadNext());
+                Assert.IsTrue(!reader.IsNull("Geometry"));
+
+                MgByteReader txAgf2 = reader.GetGeometry("Geometry");
+                MgGeometry txGeom2 = agfRw.Read(txAgf2);
+                MgPoint txPt2 = (MgPoint)txGeom2;
+                Assert.IsTrue(txPt2 != null);
+                MgCoordinate txCoord2 = txPt2.GetCoordinate();
+
+                //TODO: Maybe we should really check that it matches the expected transformed result
+                Assert.IsTrue(txCoord2.GetX() != -37.2020);
+                Assert.IsTrue(txCoord2.GetY() != 144.2020);
+
+                reader.Close();
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class LayerSelectFeaturesWithTransform : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/TransformTest.FeatureSource");
+            try
+            {
+                //Create our test data store
+                MgFeatureSchema fs = new MgFeatureSchema("Default", "");
+                MgClassDefinition cls = new MgClassDefinition();
+                cls.SetName("Test");
+
+                MgDataPropertyDefinition id = new MgDataPropertyDefinition("ID");
+                id.SetAutoGeneration(true);
+                id.SetDataType(MgPropertyType.Int32);
+
+                MgGeometricPropertyDefinition geom = new MgGeometricPropertyDefinition("Geometry");
+                geom.SetGeometryTypes(MgFeatureGeometricType.Point);
+                geom.SetSpatialContextAssociation("Default");
+
+                MgPropertyDefinitionCollection props = cls.GetProperties();
+                MgPropertyDefinitionCollection idProps = cls.GetIdentityProperties();
+
+                props.Add(id);
+                props.Add(geom);
+                idProps.Add(id);
+
+                cls.SetDefaultGeometryPropertyName("Geometry");
+                MgClassDefinitionCollection classes = fs.GetClasses();
+                classes.Add(cls);
+
+                //We'll use a transform guaranteed to work. ArbitraryXY (unitA to unitB)
+                //We just check that the transformed values are not the original, that way
+                //we know that CS-Map did its job
+                MgCoordinateSystemFactory csFact = new MgCoordinateSystemFactory();
+                string srcWkt = csFact.ConvertCoordinateSystemCodeToWkt("XY-M");
+                string dstWkt = csFact.ConvertCoordinateSystemCodeToWkt("XY-IN");
+
+                MgGeometryFactory geomFact = new MgGeometryFactory();
+                MgAgfReaderWriter agfRw = new MgAgfReaderWriter();
+                MgFileFeatureSourceParams create = new MgFileFeatureSourceParams("OSGeo.SDF", "Default", srcWkt, fs);
+
+                featSvc.CreateFeatureSource(fsId, create);
+
+                MgMapBase map = factory.CreateMap(srcWkt, new MgEnvelope(-180, -90, 180, 90), "TestMap");
+                MgLayerCollection layers = map.GetLayers();
+
+                string layerXml = string.Format(PlatformResources.TestLayer, fsId.ToString(), "Default:Test", "Geometry");
+                byte[] bytes = Encoding.UTF8.GetBytes(layerXml);
+                MgByteSource bs = new MgByteSource(bytes, bytes.Length);
+                MgByteReader br = bs.GetReader();
+
+                var ldfId = new MgResourceIdentifier("Library://UnitTests/Layers/TestLayer.LayerDefinition");
+                resSvc.SetResource(ldfId, br, null);
+
+                MgLayerBase layer = factory.CreateLayer(ldfId);
+                layers.Insert(0, layer);
+
+                //Populate data store with test points
+
+                MgCoordinate coord1 = geomFact.CreateCoordinateXY(-37.1020, 144.0020);
+                MgPoint pt1 = geomFact.CreatePoint(coord1);
+                MgByteReader agf1 = agfRw.Write(pt1);
+
+                MgPropertyCollection propVals = new MgPropertyCollection();
+                MgGeometryProperty pGeom = new MgGeometryProperty("Geometry", agf1);
+                propVals.Add(pGeom);
+
+                MgFeatureReader fr = layer.InsertFeatures(propVals, null);
+                fr.Close();
+
+                MgCoordinate coord2 = geomFact.CreateCoordinateXY(-37.2020, 144.2020);
+                MgPoint pt2 = geomFact.CreatePoint(coord2);
+                MgByteReader agf2 = agfRw.Write(pt2);
+
+                pGeom.SetValue(agf2);
+                fr = layer.InsertFeatures(propVals, null);
+                fr.Close();
+
+                //Now select from this data store
+                MgFeatureQueryOptions query = new MgFeatureQueryOptions();
+                MgReader reader = layer.SelectFeatures(query, dstWkt);
+
+                Assert.IsTrue(reader.ReadNext());
+                Assert.IsTrue(!reader.IsNull("Geometry"));
+
+                MgByteReader txAgf1 = reader.GetGeometry("Geometry");
+                MgGeometry txGeom1 = agfRw.Read(txAgf1);
+                MgPoint txPt1 = (MgPoint)txGeom1;
+                Assert.IsTrue(txPt1 != null);
+                MgCoordinate txCoord1 = txPt1.GetCoordinate();
+
+                //TODO: Maybe we should really check that it matches the expected transformed result
+                Assert.IsTrue(txCoord1.GetX() != -37.1020);
+                Assert.IsTrue(txCoord1.GetY() != 144.0020);
+
+                Assert.IsTrue(reader.ReadNext());
+                Assert.IsTrue(!reader.IsNull("Geometry"));
+
+                MgByteReader txAgf2 = reader.GetGeometry("Geometry");
+                MgGeometry txGeom2 = agfRw.Read(txAgf2);
+                MgPoint txPt2 = (MgPoint)txGeom2;
+                Assert.IsTrue(txPt2 != null);
+                MgCoordinate txCoord2 = txPt2.GetCoordinate();
+
+                //TODO: Maybe we should really check that it matches the expected transformed result
+                Assert.IsTrue(txCoord2.GetX() != -37.2020);
+                Assert.IsTrue(txCoord2.GetY() != 144.2020);
+
+                reader.Close();
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class LayerGetSpatialContexts : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/GetSpatialContexts.FeatureSource");
+            try
+            {
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SDF", featSvc);
+
+                MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
+                MgMapBase map = factory.CreateMap(csFactory.ConvertCoordinateSystemCodeToWkt("LL84"), new MgEnvelope(-180, -90, 180, 90), "TestMap");
+                MgLayerCollection layers = map.GetLayers();
+
+                string layerXml = string.Format(PlatformResources.TestLayer, fsId.ToString(), className, "Geometry");
+                byte[] bytes = Encoding.UTF8.GetBytes(layerXml);
+                MgByteSource bs = new MgByteSource(bytes, bytes.Length);
+                MgByteReader br = bs.GetReader();
+
+                var ldfId = new MgResourceIdentifier("Library://UnitTests/Layers/TestLayer.LayerDefinition");
+                resSvc.SetResource(ldfId, br, null);
+
+                MgLayerBase layer = factory.CreateLayer(ldfId);
+                layers.Insert(0, layer);
+
+                MgSpatialContextReader scReader = layer.GetSpatialContexts(false);
+                int count = 0;
+                try
+                {
+                    while (scReader.ReadNext())
+                    {
+                        Assert.AreEqual("Default", scReader.GetName());
+                        Assert.AreEqual(csFactory.ConvertCoordinateSystemCodeToWkt("LL84"), scReader.GetCoordinateSystemWkt());
+                        count++;
+                    }
+                }
+                finally
+                {
+                    scReader.Close();
+                }
+                Assert.AreEqual(1, count);
+            }
+            finally
+            {
+                resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/PropertiesTest.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/PropertiesTest.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ExternalTests/PropertiesTest.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,1079 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common.ExternalTests
+{
+    public class PropertiesTestColor : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            short nRed = 22;
+            short nGreen = 33;
+            short nBlue = 44;
+            short nAlpha = 55;
+            MgColor color = new MgColor(nRed, nGreen, nBlue, nAlpha);
+            Assert.AreEqual(nRed, color.Red);
+            Assert.AreEqual(nGreen, color.Green);
+            Assert.AreEqual(nBlue, color.Blue);
+            Assert.AreEqual(nAlpha, color.Alpha);
+            String scolor = nRed.ToString("x") + nGreen.ToString("x") + nBlue.ToString("x") + nAlpha.ToString("x");
+            Assert.AreEqual(scolor, color.Color);
+        }
+    }
+
+    public class PropertiesTestDateTime : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgDateTime dt = new MgDateTime();
+            dt.Year = 2006;
+            dt.Month = 9;
+            dt.Day = 21;
+            dt.Hour = 16;
+            dt.Minute = 4;
+            dt.Second = 25;
+            dt.Microsecond = 99;
+
+            Assert.AreEqual(2006, dt.Year);
+            Assert.AreEqual(9, dt.Month);
+            Assert.AreEqual(21, dt.Day);
+            Assert.AreEqual(16, dt.Hour);
+            Assert.AreEqual(4, dt.Minute);
+            Assert.AreEqual(25, dt.Second);
+            Assert.AreEqual(99, dt.Microsecond);
+        }
+    }
+
+    public class PropertiesTestByteReader : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgByteSource source = new MgByteSource($"{TestDataRoot.Path}/DrawingService/SpaceShip.dwf");
+            source.MimeType = MgMimeType.Dwf;
+            MgByteReader reader = source.GetReader();
+
+            Assert.AreEqual(MgMimeType.Dwf, reader.MimeType);
+        }
+    }
+
+    public class PropertiesTestByteSource : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            string tmp = Path.GetTempFileName();
+            try
+            {
+                File.WriteAllText(tmp, "Hello World");
+
+                MgByteSource source = new MgByteSource(tmp);
+                source.MimeType = "text/plain";
+
+                Assert.AreEqual(MgMimeType.Text, source.MimeType);
+            }
+            finally
+            {
+                try
+                {
+                    File.Delete(tmp);
+                }
+                catch { }
+            }
+        }
+    }
+
+    public class PropertiesTestPropertyDefinition : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgPropertyDefinition propDef = new MgPropertyDefinition("TestProp", MgPropertyType.Int32);
+            propDef.Description = "description";
+            propDef.QualifiedName = "qualifiedName";
+
+            Assert.AreEqual("TestProp", propDef.Name);
+            Assert.AreEqual(MgPropertyType.Int32, propDef.PropertyType);
+            Assert.AreEqual("description", propDef.Description);
+            Assert.AreEqual("qualifiedName", propDef.QualifiedName);
+        }
+    }
+
+    public class PropertiesTestStringProperty : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgStringProperty sp = new MgStringProperty("TestStringName", "TestStringValue");
+
+            Assert.AreEqual("TestStringValue", sp.Value);
+            Assert.AreEqual("TestStringName", sp.Name);
+        }
+    }
+
+    public class PropertiesTestBlobProperty : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            string tmp = Path.GetTempFileName();
+            try
+            {
+                File.WriteAllText(tmp, "Hello World");
+
+                MgByteSource bs = new MgByteSource(tmp);
+                MgByteReader reader = bs.GetReader();
+                MgBlobProperty bp = new MgBlobProperty("BlobPropName", reader);
+
+                Assert.AreEqual("BlobPropName", bp.Name);
+                Assert.Greater((int)bp.Value.GetLength(), 0);
+                Assert.AreEqual(reader.GetLength(), bp.Value.GetLength());
+                Assert.AreEqual(MgPropertyType.Blob, bp.PropertyType);
+
+                MgByteSource bs2 = new MgByteSource($"{TestDataRoot.Path}/DrawingService/SpaceShip.dwf");
+                MgByteReader reader2 = bs2.GetReader();
+                bp.Value = reader2;
+                Assert.AreEqual(reader2.GetLength(), bp.Value.GetLength());
+            }
+            finally
+            {
+                try
+                {
+                    File.Delete(tmp);
+                }
+                catch { }
+            }
+        }
+    }
+
+    public class PropertiesTestBooleanProperty : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgBooleanProperty bp = new MgBooleanProperty("BoolPropName", true);
+
+            Assert.IsTrue(bp.Value);
+            Assert.AreEqual("BoolPropName", bp.Name);
+            Assert.AreEqual(MgPropertyType.Boolean, bp.PropertyType);
+
+            bp.Value = false;
+            Assert.IsFalse(bp.Value);
+        }
+    }
+
+    public class PropertiesTestByteProperty : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            byte bpv = 123;
+            MgByteProperty bp = new MgByteProperty("BytePropName", bpv);
+
+            Assert.AreEqual("BytePropName", bp.Name);
+            Assert.AreEqual(bpv, bp.Value);
+            Assert.AreEqual(MgPropertyType.Byte, bp.PropertyType);
+        }
+    }
+
+    public class PropertiesTestClobProperty : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            string tmp = Path.GetTempFileName();
+            try
+            {
+                File.WriteAllText(tmp, "Hello World");
+
+                MgByteSource bs = new MgByteSource(tmp);
+                MgByteReader reader = bs.GetReader();
+                MgClobProperty cp = new MgClobProperty("ClobPropName", reader);
+
+                Assert.AreEqual("ClobPropName", cp.Name);
+                Assert.Greater((int)cp.Value.GetLength(), 0);
+                Assert.AreEqual(reader.GetLength(), cp.Value.GetLength());
+                Assert.AreEqual(MgPropertyType.Clob, cp.PropertyType);
+
+                MgByteSource bs2 = new MgByteSource($"{TestDataRoot.Path}/DrawingService/SpaceShip.dwf");
+                MgByteReader reader2 = bs2.GetReader();
+                cp.Value = reader2;
+                Assert.AreEqual(reader2.GetLength(), cp.Value.GetLength());
+            }
+            finally
+            {
+                try
+                {
+                    File.Delete(tmp);
+                }
+                catch { }
+            }
+        }
+    }
+
+    public class PropertiesTestDateTimeProperty : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgDateTime dt = new MgDateTime(2006, 9, 22, 8, 33, 44, 55);
+            MgDateTimeProperty dtp = new MgDateTimeProperty("DateTimePropName", dt);
+
+            Assert.AreEqual("DateTimePropName", dtp.Name);
+            Assert.AreEqual(MgPropertyType.DateTime, dtp.PropertyType);
+            Assert.AreEqual(dt.Year, dtp.Value.Year);
+            Assert.AreEqual(dt.Month, dtp.Value.Month);
+            Assert.AreEqual(dt.Day, dtp.Value.Day);
+            Assert.AreEqual(dt.Hour, dtp.Value.Hour);
+            Assert.AreEqual(dt.Minute, dtp.Value.Minute);
+            Assert.AreEqual(dt.Second, dtp.Value.Second);
+            Assert.AreEqual(dt.Microsecond, dtp.Value.Microsecond);
+        }
+    }
+
+    public class PropertiesTestDoubleProperty : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgDoubleProperty dp = new MgDoubleProperty("DoublePropName", 4.251973);
+
+            Assert.AreEqual("DoublePropName", dp.Name);
+            Assert.AreEqual(MgPropertyType.Double, dp.PropertyType);
+            Assert.AreEqual(4.251973, dp.Value);
+        }
+    }
+
+    public class PropertiesTestInt16Property : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgInt16Property ip = new MgInt16Property("Int16PropName", -32768);
+
+            Assert.AreEqual("Int16PropName", ip.Name);
+            Assert.AreEqual(MgPropertyType.Int16, ip.PropertyType);
+            Assert.AreEqual(Int16.MinValue, ip.Value);
+        }
+    }
+
+    public class PropertiesTestInt32Property : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgInt32Property ip = new MgInt32Property("Int32PropName", -2147483648);
+
+            Assert.AreEqual("Int32PropName", ip.Name);
+            Assert.AreEqual(MgPropertyType.Int32, ip.PropertyType);
+            Assert.AreEqual(Int32.MinValue, ip.Value);
+        }
+    }
+
+    public class PropertiesTestInt64Property : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgInt64Property ip = new MgInt64Property("Int64PropName", -9223372036854775808);
+
+            Assert.AreEqual("Int64PropName", ip.Name);
+            Assert.AreEqual(MgPropertyType.Int64, ip.PropertyType);
+            Assert.AreEqual(Int64.MinValue, ip.Value);
+        }
+    }
+
+    public class PropertiesTestSingleProperty : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgSingleProperty sp = new MgSingleProperty("SinglePropName", 4.251973f);
+
+            Assert.AreEqual("SinglePropName", sp.Name);
+            Assert.AreEqual(MgPropertyType.Single, sp.PropertyType);
+            Assert.AreEqual(4.251973f, sp.Value);
+        }
+    }
+
+    public class PropertiesTestLayerBase : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            logger.WriteLine("Skipping PropertiesTestLayerBase: Resource Service required to construct MgLayer");
+            //MgUserInformation user = new MgUserInformation("Administrator", "admin");
+            //MgSiteConnection siteConnection = new MgSiteConnection();
+            //siteConnection.Open(user);
+            //MgResourceService resourceService = (MgResourceService)siteConnection.CreateService(MgServiceType.ResourceService);
+
+            //MgResourceIdentifier id = new MgResourceIdentifier("Library://UnitTests/Data/Shuttle.DrawingSource");
+
+            //MgLayerBase lb = new MgLayerBase(id, resourceService);
+
+            //lb.Name = "TestLayerName";
+            //Assert.AreEqual("TestLayerName", lb.Name);
+
+            //Assert.AreEqual(MgLayerType.Dynamic, lb.LayerType);
+
+            //MgLayerGroup lg = new MgLayerGroup("TestLayerGroup");
+            //lb.Group = lg;
+            //Assert.AreEqual(lg.Name, lb.Group.Name);
+
+            //lb.Visible = true;
+            //Assert.AreEqual(true, lb.Visible);
+            //lb.Visible = false;
+            //Assert.AreEqual(false, lb.Visible);
+
+            //lb.Selectable = true;
+            //Assert.AreEqual(true, lb.Selectable);
+            //lb.Selectable = false;
+            //Assert.AreEqual(false, lb.Selectable);
+
+            //Assert.AreEqual(id.ToString(), lb.LayerDefinition.ToString());
+
+            //lb.DisplayInLegend = true;
+            //Assert.AreEqual(true, lb.DisplayInLegend);
+            //lb.DisplayInLegend = false;
+            //Assert.AreEqual(false, lb.DisplayInLegend);
+
+            //Assert.AreEqual(false, lb.ExpandInLegend);
+
+            //lb.LegendLabel = "TestLegendLabel";
+            //Assert.AreEqual("TestLegendLabel", lb.LegendLabel);
+
+            //Assert.AreEqual("", lb.FeatureSourceId);
+            //Assert.AreEqual("", lb.FeatureClassName);
+        }
+    }
+
+    public class PropertiesTestLayerGroup : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgLayerGroup lg = new MgLayerGroup("TestLayerGroupName");
+
+            Assert.AreEqual("TestLayerGroupName", lg.Name);
+            Assert.AreEqual(MgLayerGroupType.Normal, lg.LayerGroupType);
+
+            lg.Group = new MgLayerGroup("AnotherTestLayerGroupName");
+            Assert.AreEqual("AnotherTestLayerGroupName", lg.Group.Name);
+
+            lg.Visible = true;
+            Assert.IsTrue(lg.Visible);
+            lg.Visible = false;
+            Assert.IsFalse(lg.Visible);
+
+            lg.DisplayInLegend = true;
+            Assert.IsTrue(lg.DisplayInLegend);
+            lg.DisplayInLegend = false;
+            Assert.IsFalse(lg.DisplayInLegend);
+
+            Assert.IsFalse(lg.ExpandInLegend);
+
+            lg.LegendLabel = "TestLegendLabel";
+            Assert.AreEqual("TestLegendLabel", lg.LegendLabel);
+        }
+    }
+
+    public class PropertiesTestMapBase : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgMapBase mb = new MgMapBase();
+            Assert.AreEqual("", mb.Name);
+            Assert.AreEqual("", mb.SessionId);
+            Assert.IsNull(mb.MapDefinition);
+            Assert.AreEqual("", mb.MapSRS);
+            Assert.IsNull(mb.MapExtent);
+            Assert.IsNull(mb.ViewCenter);
+            Assert.AreEqual(1.0, mb.ViewScale);
+            Assert.IsNull(mb.DataExtent);
+            Assert.AreEqual(96, mb.DisplayDpi);
+            Assert.AreEqual(0, mb.DisplayWidth);
+            Assert.AreEqual(0, mb.DisplayHeight);
+            Assert.AreEqual(0, mb.FiniteDisplayScaleCount);
+        }
+    }
+
+    public class PropertiesTestClassDefinition : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgClassDefinition classDef = new MgClassDefinition();
+            classDef.DefaultGeometryPropertyName = "TestDefaultGeometryPropName";
+            Assert.AreEqual("TestDefaultGeometryPropName", classDef.DefaultGeometryPropertyName);
+
+            classDef.Name = "TestName";
+            Assert.AreEqual("TestName", classDef.Name);
+
+            classDef.Description = "TestDescription";
+            Assert.AreEqual("TestDescription", classDef.Description);
+
+            Assert.IsNull(classDef.BaseClassDefinition);
+        }
+    }
+
+    public class PropertiesTestFileFeatureSourceParams : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgFileFeatureSourceParams csp = new MgFileFeatureSourceParams();
+            csp.ProviderName = "OSGeo.SDF";
+            Assert.AreEqual("OSGeo.SDF", csp.ProviderName);
+
+            csp.SpatialContextName = "TestSpatialContextName";
+            Assert.AreEqual("TestSpatialContextName", csp.SpatialContextName);
+
+            csp.SpatialContextDescription = "TestSpatialContextDescription";
+            Assert.AreEqual("TestSpatialContextDescription", csp.SpatialContextDescription);
+
+            csp.CoordinateSystemWkt = "TestCoordSysWkt";
+            Assert.AreEqual("TestCoordSysWkt", csp.CoordinateSystemWkt);
+
+            csp.XYTolerance = 0.0001;
+            Assert.AreEqual(0.0001, csp.XYTolerance);
+
+            csp.ZTolerance = 0.01;
+            Assert.AreEqual(0.01, csp.ZTolerance);
+
+            MgFeatureSchema schema = new MgFeatureSchema("SchemaName", "SchemaDescription");
+            csp.FeatureSchema = schema;
+            Assert.AreEqual(schema.Name, csp.FeatureSchema.Name);
+            Assert.AreEqual(schema.Description, csp.FeatureSchema.Description);
+        }
+    }
+
+    public class PropertiesTestDataPropertyDefinition : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgDataPropertyDefinition dpd = new MgDataPropertyDefinition("DataPropDefName");
+
+            dpd.DataType = MgPropertyType.Int32;
+            Assert.AreEqual(MgPropertyType.Int32, dpd.DataType);
+
+            dpd.Length = 0;
+            Assert.AreEqual(0, dpd.Length);
+
+            dpd.Precision = 0;
+            Assert.AreEqual(0, dpd.Precision);
+
+            dpd.Scale = 0;
+            Assert.AreEqual(0, dpd.Scale);
+
+            dpd.DefaultValue = "123";
+            Assert.AreEqual("123", dpd.DefaultValue);
+
+            dpd.Nullable = true;
+            Assert.IsTrue(dpd.Nullable);
+
+            dpd.ReadOnly = false;
+            Assert.IsFalse(dpd.ReadOnly);
+        }
+    }
+
+    public class PropertiesTestDeleteFeatures : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgDeleteFeatures df = new MgDeleteFeatures("dfClassName", "dfFilterText");
+            Assert.AreEqual("dfClassName", df.FeatureClassName);
+            Assert.AreEqual("dfFilterText", df.FilterText);
+        }
+    }
+
+    public class PropertiesTestFeatureProperty : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgFeatureProperty fp = new MgFeatureProperty("FeatureProp", null);
+            Assert.AreEqual("FeatureProp", fp.Name);
+            Assert.AreEqual(MgPropertyType.Feature, fp.PropertyType);
+            Assert.IsNull(fp.Value);
+        }
+    }
+
+    public class PropertiesTestFeatureSchema : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgFeatureSchema fs = new MgFeatureSchema();
+            fs.Name = "fsName";
+            Assert.AreEqual("fsName", fs.Name);
+
+            fs.Description = "fsDescription";
+            Assert.AreEqual("fsDescription", fs.Description);
+        }
+    }
+
+    public class PropertiesTestGeometryPropertyDefinition : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometricPropertyDefinition gpd = new MgGeometricPropertyDefinition("gdpName");
+
+            gpd.GeometryTypes = MgGeometryType.LineString;
+            Assert.AreEqual(MgGeometryType.LineString, gpd.GeometryTypes);
+
+            gpd.ReadOnly = false;
+            Assert.IsFalse(gpd.ReadOnly);
+            gpd.ReadOnly = true;
+            Assert.IsTrue(gpd.ReadOnly);
+
+            gpd.HasElevation = true;
+            Assert.IsTrue(gpd.HasElevation);
+
+            gpd.HasMeasure = true;
+            Assert.IsTrue(gpd.HasMeasure);
+
+            gpd.SpatialContextAssociation = "spatialContextAssociation";
+            Assert.AreEqual("spatialContextAssociation", gpd.SpatialContextAssociation);
+        }
+    }
+
+    public class PropertiesTestGeometryProperty : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            string tmp = Path.GetTempFileName();
+            try
+            {
+                File.WriteAllText(tmp, "Hello World");
+
+                MgByteSource bs = new MgByteSource(tmp);
+                MgByteReader reader = bs.GetReader();
+
+                MgGeometryProperty gp = new MgGeometryProperty("GeomPropName", reader);
+                MgByteSource bs2 = new MgByteSource($"{TestDataRoot.Path}/DrawingService/SpaceShip.dwf");
+                MgByteReader reader2 = bs2.GetReader();
+
+                gp.Value = reader2;
+                Assert.AreEqual(reader2.GetLength(), gp.Value.GetLength());
+
+                Assert.AreEqual(MgPropertyType.Geometry, gp.PropertyType);
+            }
+            finally
+            {
+                try
+                {
+                    File.Delete(tmp);
+                }
+                catch { }
+            }
+        }
+    }
+
+    public class PropertiesTestInsertFeatures : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgPropertyCollection propColl = new MgPropertyCollection();
+            propColl.Add(new MgInt32Property("intProp", 10));
+            MgInsertFeatures inf = new MgInsertFeatures("ClassName", propColl);
+            Assert.AreEqual("ClassName", inf.FeatureClassName);
+        }
+    }
+
+    public class PropertiesTestObjectPropertyDefinition : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgObjectPropertyDefinition opd = new MgObjectPropertyDefinition("odpName");
+            MgClassDefinition classDef = new MgClassDefinition();
+            classDef.Name = "testClassDef";
+
+            opd.ClassDefinition = classDef;
+            Assert.AreEqual(classDef.Name, opd.ClassDefinition.Name);
+
+            opd.ObjectType = MgObjectPropertyType.OrderedCollection;
+            Assert.AreEqual(MgObjectPropertyType.OrderedCollection, opd.ObjectType);
+
+            opd.OrderType = MgOrderingOption.Ascending;
+            Assert.AreEqual(MgOrderingOption.Ascending, opd.OrderType);
+
+            MgDataPropertyDefinition dpd = new MgDataPropertyDefinition("dpdName");
+            opd.IdentityProperty = dpd;
+            Assert.AreEqual(dpd.Name, opd.IdentityProperty.Name);
+        }
+    }
+
+    public class PropertiesTestRasterProperty : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgRasterProperty rp = new MgRasterProperty("RasterPropName", null);
+
+            Assert.AreEqual("RasterPropName", rp.Name);
+            Assert.AreEqual(MgPropertyType.Raster, rp.PropertyType);
+            Assert.IsNull(rp.Value);
+        }
+    }
+
+    public class PropertiesTestRasterPropertyDefinition : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgRasterPropertyDefinition rpd = new MgRasterPropertyDefinition("rasterPropDef");
+
+            rpd.ReadOnly = true;
+            Assert.IsTrue(rpd.ReadOnly);
+
+            rpd.Nullable = true;
+            Assert.IsTrue(rpd.Nullable);
+
+            rpd.DefaultImageXSize = 600;
+            Assert.AreEqual(600, rpd.DefaultImageXSize);
+
+            rpd.DefaultImageYSize = 600;
+            Assert.AreEqual(600, rpd.DefaultImageYSize);
+
+            Assert.AreEqual("rasterPropDef", rpd.Name);
+        }
+    }
+
+    public class PropertiesTestResourceIdentifier : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgResourceIdentifier resId = new MgResourceIdentifier("Library://UnitTests/Data/Shuttle.DrawingSource");
+
+            resId.RepositoryType = MgRepositoryType.Library;
+            Assert.AreEqual(MgRepositoryType.Library, resId.RepositoryType);
+
+            resId.RepositoryName = "resName";
+            Assert.AreEqual("resName", resId.RepositoryName);
+
+            resId.Path = "Data";
+            Assert.AreEqual("Data", resId.Path);
+
+            resId.Name = "Shuttle";
+            Assert.AreEqual("Shuttle", resId.Name);
+
+            resId.ResourceType = MgResourceType.DrawingSource;
+            Assert.AreEqual(MgResourceType.DrawingSource, resId.ResourceType);
+        }
+    }
+
+    public class PropertiesTestUpdateFeatures : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgPropertyCollection propColl = new MgPropertyCollection();
+            propColl.Add(new MgInt32Property("intProp", 10));
+            MgUpdateFeatures uf = new MgUpdateFeatures("ClassName", propColl, "filter");
+            Assert.AreEqual("ClassName", uf.FeatureClassName);
+            Assert.AreEqual("ClassName", uf.FeatureClassName);
+        }
+    }
+
+    public class PropertiesTestCoordinateSystem : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
+            MgCoordinateSystem coordSys = csFactory.CreateFromCode("LL84");
+            Assert.AreEqual(MgCoordinateSystemType.Geographic, coordSys.Type);
+            Assert.AreEqual("DEGREE", coordSys.Units);
+            Assert.AreEqual(-180, coordSys.MinX);
+            Assert.AreEqual(-90, coordSys.MinY);
+            Assert.AreEqual(180, coordSys.MaxX);
+            Assert.AreEqual(90, coordSys.MaxY);
+            Assert.AreEqual("LL84", coordSys.CsCode);
+            Assert.AreEqual("WGS84 datum, Latitude-Longitude; Degrees", coordSys.Description);
+            Assert.AreEqual("LL", coordSys.Projection);
+            Assert.AreEqual("Null Projection, produces/processes Latitude & Longitude", coordSys.ProjectionDescription);
+            Assert.AreEqual("WGS84", coordSys.Datum);
+            Assert.AreEqual("World Geodetic System of 1984", coordSys.DatumDescription);
+            Assert.AreEqual("WGS84", coordSys.Ellipsoid);
+            Assert.AreEqual("World Geodetic System of 1984, GEM 10C", coordSys.EllipsoidDescription);
+        }
+    }
+
+    public class PropertiesTestArcSegment : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinateXY start = gf.CreateCoordinateXY(0, 0) as MgCoordinateXY;
+            MgCoordinateXY end = gf.CreateCoordinateXY(10, 10) as MgCoordinateXY;
+            MgCoordinateXY control = gf.CreateCoordinateXY(5, 5) as MgCoordinateXY;
+            MgArcSegment arcSegment = gf.CreateArcSegment(start, end, control);
+
+            Assert.AreEqual(MgGeometryComponentType.ArcSegment, arcSegment.ComponentType);
+            Assert.AreEqual(control.ToString(), arcSegment.ControlCoordinate.ToString());
+            Assert.AreEqual(start.ToString(), arcSegment.StartCoordinate.ToString());
+            Assert.AreEqual(end.ToString(), arcSegment.EndCoordinate.ToString());
+        }
+    }
+
+    public class PropertiesTestCoordinateXY : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinateXY coord = gf.CreateCoordinateXY(100, 100) as MgCoordinateXY;
+            Assert.AreEqual(100, coord.X);
+            Assert.AreEqual(100, coord.Y);
+            MgCoordinate coord2 = coord;
+            Assert.AreEqual(100, coord2.X);
+            Assert.AreEqual(100, coord2.Y);
+        }
+    }
+
+    public class PropertiesTestCoordinateXYM : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinateXYM coord = gf.CreateCoordinateXYM(100, 100, 10) as MgCoordinateXYM;
+            Assert.AreEqual(100, coord.X);
+            Assert.AreEqual(100, coord.Y);
+            Assert.AreEqual(10, coord.M);
+            MgCoordinate coord2 = coord;
+            Assert.AreEqual(100, coord2.X);
+            Assert.AreEqual(100, coord2.Y);
+            Assert.AreEqual(10, coord2.M);
+        }
+    }
+
+    public class PropertiesTestCoordinateXYZ : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinateXYZ coord = gf.CreateCoordinateXYZ(100, 100, 10) as MgCoordinateXYZ;
+            Assert.AreEqual(100, coord.X);
+            Assert.AreEqual(100, coord.Y);
+            Assert.AreEqual(10, coord.Z);
+            MgCoordinate coord2 = coord;
+            Assert.AreEqual(100, coord2.X);
+            Assert.AreEqual(100, coord2.Y);
+            Assert.AreEqual(10, coord2.Z);
+        }
+    }
+
+    public class PropertiesTestCoordinateXYZM : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinateXYZM coord = gf.CreateCoordinateXYZM(100, 100, 10, 5) as MgCoordinateXYZM;
+            Assert.AreEqual(100, coord.X);
+            Assert.AreEqual(100, coord.Y);
+            Assert.AreEqual(10, coord.Z);
+            Assert.AreEqual(5, coord.M);
+            MgCoordinate coord2 = coord;
+            Assert.AreEqual(100, coord2.X);
+            Assert.AreEqual(100, coord2.Y);
+            Assert.AreEqual(10, coord2.Z);
+            Assert.AreEqual(5, coord2.M);
+        }
+    }
+
+    public class PropertiesTestCurvePolygon : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate pt1 = gf.CreateCoordinateXY(0, 0);
+            MgCoordinate pt2 = gf.CreateCoordinateXY(0, 10);
+            MgCoordinate pt3 = gf.CreateCoordinateXY(10, 10);
+            MgCoordinate pt4 = gf.CreateCoordinateXY(10, 0);
+            MgCoordinateCollection coordinates = new MgCoordinateCollection();
+            coordinates.Add(pt1);
+            coordinates.Add(pt2);
+            coordinates.Add(pt3);
+            coordinates.Add(pt4);
+            MgLinearSegment linearSegment = gf.CreateLinearSegment(coordinates);
+            MgCurveSegmentCollection curveSegments = new MgCurveSegmentCollection();
+            curveSegments.Add(linearSegment);
+            MgCurveRing outerRing = gf.CreateCurveRing(curveSegments);
+            MgCurvePolygon cp = gf.CreateCurvePolygon(outerRing, null);
+
+            Assert.AreEqual(outerRing.ToString(), cp.ExteriorRing.ToString());
+            Assert.AreEqual(0, cp.InteriorRingCount);
+            Assert.AreEqual(MgGeometryType.CurvePolygon, cp.GeometryType);
+            Assert.AreEqual(2, cp.Dimension);
+        }
+    }
+
+    public class PropertiesTestCurveRing : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate pt1 = gf.CreateCoordinateXY(0, 0);
+            MgCoordinate pt2 = gf.CreateCoordinateXY(0, 10);
+            MgCoordinate pt3 = gf.CreateCoordinateXY(10, 10);
+            MgCoordinate pt4 = gf.CreateCoordinateXY(10, 0);
+            MgCoordinateCollection coordinates = new MgCoordinateCollection();
+            coordinates.Add(pt1);
+            coordinates.Add(pt2);
+            coordinates.Add(pt3);
+            coordinates.Add(pt4);
+            MgLinearSegment linearSegment = gf.CreateLinearSegment(coordinates);
+            MgCurveSegmentCollection curveSegments = new MgCurveSegmentCollection();
+            curveSegments.Add(linearSegment);
+            MgCurveRing outerRing = gf.CreateCurveRing(curveSegments);
+
+            Assert.AreEqual(MgGeometryComponentType.CurveRing, outerRing.ComponentType);
+            Assert.AreEqual(1, outerRing.Count);
+            Assert.AreEqual(2, outerRing.Dimension);
+        }
+    }
+
+    public class PropertiesTestCurveString : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate pt1 = gf.CreateCoordinateXY(0, 0);
+            MgCoordinate pt2 = gf.CreateCoordinateXY(0, 10);
+            MgCoordinate pt3 = gf.CreateCoordinateXY(10, 10);
+            MgCoordinate pt4 = gf.CreateCoordinateXY(10, 0);
+            MgCoordinateCollection coordinates = new MgCoordinateCollection();
+            coordinates.Add(pt1);
+            coordinates.Add(pt2);
+            coordinates.Add(pt3);
+            coordinates.Add(pt4);
+            MgLinearSegment linearSegment = gf.CreateLinearSegment(coordinates);
+            MgCurveSegmentCollection curveSegments = new MgCurveSegmentCollection();
+            curveSegments.Add(linearSegment);
+            MgCurveString curveString = gf.CreateCurveString(curveSegments);
+
+            Assert.AreEqual(1, curveString.Count);
+            Assert.AreEqual(pt1.ToString(), curveString.StartCoordinate.ToString());
+            Assert.AreEqual(pt4.ToString(), curveString.EndCoordinate.ToString());
+            Assert.AreEqual(MgGeometryType.CurveString, curveString.GeometryType);
+            Assert.AreEqual(1, curveString.Dimension);
+        }
+    }
+
+    public class PropertiesTestEnvelope : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgEnvelope env = new MgEnvelope(-180, -90, 180, 90);
+
+            Assert.AreEqual(-180, env.LowerLeftCoordinate.GetX());
+            Assert.AreEqual(-90, env.LowerLeftCoordinate.GetY());
+            Assert.AreEqual(180, env.UpperRightCoordinate.GetX());
+            Assert.AreEqual(90, env.UpperRightCoordinate.GetY());
+            Assert.AreEqual(360, env.Width);
+            Assert.AreEqual(180, env.Height);
+            Assert.IsNaN(env.Depth);
+        }
+    }
+
+    public class PropertiesTestLinearRing : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate pt1 = gf.CreateCoordinateXY(0, 0);
+            MgCoordinate pt2 = gf.CreateCoordinateXY(0, 10);
+            MgCoordinate pt3 = gf.CreateCoordinateXY(10, 10);
+            MgCoordinate pt4 = gf.CreateCoordinateXY(10, 0);
+            MgCoordinateCollection coordinates = new MgCoordinateCollection();
+            coordinates.Add(pt1);
+            coordinates.Add(pt2);
+            coordinates.Add(pt3);
+            coordinates.Add(pt4);
+            MgLinearRing linearRing = gf.CreateLinearRing(coordinates);
+
+            Assert.AreEqual(MgGeometryComponentType.LinearRing, linearRing.ComponentType);
+            Assert.AreEqual(2, linearRing.Dimension);
+        }
+    }
+
+    public class PropertiesTestLinearSegment : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate pt1 = gf.CreateCoordinateXY(0, 0);
+            MgCoordinate pt2 = gf.CreateCoordinateXY(0, 10);
+            MgCoordinate pt3 = gf.CreateCoordinateXY(10, 10);
+            MgCoordinate pt4 = gf.CreateCoordinateXY(10, 0);
+            MgCoordinateCollection coordinates = new MgCoordinateCollection();
+            coordinates.Add(pt1);
+            coordinates.Add(pt2);
+            coordinates.Add(pt3);
+            coordinates.Add(pt4);
+            MgLinearSegment linearSegment = gf.CreateLinearSegment(coordinates);
+
+            Assert.AreEqual(MgGeometryComponentType.LinearSegment, linearSegment.ComponentType);
+            Assert.AreEqual(pt1.ToString(), linearSegment.StartCoordinate.ToString());
+            Assert.AreEqual(pt4.ToString(), linearSegment.EndCoordinate.ToString());
+            Assert.AreEqual(1, linearSegment.Dimension);
+        }
+    }
+
+    public class PropertiesTestLineString : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate pt1 = gf.CreateCoordinateXY(0, 0);
+            MgCoordinate pt2 = gf.CreateCoordinateXY(0, 10);
+            MgCoordinate pt3 = gf.CreateCoordinateXY(10, 10);
+            MgCoordinate pt4 = gf.CreateCoordinateXY(10, 0);
+            MgCoordinateCollection coordinates = new MgCoordinateCollection();
+            coordinates.Add(pt1);
+            coordinates.Add(pt2);
+            coordinates.Add(pt3);
+            coordinates.Add(pt4);
+            MgLineString linestring = gf.CreateLineString(coordinates);
+
+            Assert.AreEqual(pt1.ToString(), linestring.StartCoordinate.ToString());
+            Assert.AreEqual(pt4.ToString(), linestring.EndCoordinate.ToString());
+            Assert.AreEqual(MgGeometryType.LineString, linestring.GeometryType);
+            Assert.AreEqual(1, linestring.Dimension);
+        }
+    }
+
+    public class PropertiesTestMultiCurvePolygon : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate pt1 = gf.CreateCoordinateXY(0, 0);
+            MgCoordinate pt2 = gf.CreateCoordinateXY(0, 10);
+            MgCoordinate pt3 = gf.CreateCoordinateXY(10, 10);
+            MgCoordinate pt4 = gf.CreateCoordinateXY(10, 0);
+            MgCoordinateCollection coordinates = new MgCoordinateCollection();
+            coordinates.Add(pt1);
+            coordinates.Add(pt2);
+            coordinates.Add(pt3);
+            coordinates.Add(pt4);
+            MgLinearSegment linearSegment = gf.CreateLinearSegment(coordinates);
+            MgCurveSegmentCollection curveSegments = new MgCurveSegmentCollection();
+            MgCurveRing curveRing = gf.CreateCurveRing(curveSegments);
+            MgCurvePolygon cp = gf.CreateCurvePolygon(curveRing, null);
+            MgCurvePolygonCollection cpc = new MgCurvePolygonCollection();
+            cpc.Add(cp);
+            MgMultiCurvePolygon mcp = gf.CreateMultiCurvePolygon(cpc);
+
+            Assert.AreEqual(1, mcp.Count);
+            Assert.AreEqual(MgGeometryType.MultiCurvePolygon, mcp.GeometryType);
+            Assert.AreEqual(2, mcp.Dimension);
+        }
+    }
+
+    public class PropertiesTestMultiCurveString : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate pt1 = gf.CreateCoordinateXY(0, 0);
+            MgCoordinate pt2 = gf.CreateCoordinateXY(0, 10);
+            MgCoordinate pt3 = gf.CreateCoordinateXY(10, 10);
+            MgCoordinate pt4 = gf.CreateCoordinateXY(10, 0);
+            MgCoordinateCollection coordinates = new MgCoordinateCollection();
+            coordinates.Add(pt1);
+            coordinates.Add(pt2);
+            coordinates.Add(pt3);
+            coordinates.Add(pt4);
+            MgLinearSegment linearSegment = gf.CreateLinearSegment(coordinates);
+            MgCurveSegmentCollection curveSegments = new MgCurveSegmentCollection();
+            curveSegments.Add(linearSegment);
+            MgCurveString curveString = gf.CreateCurveString(curveSegments);
+            MgCurveStringCollection csc = new MgCurveStringCollection();
+            csc.Add(curveString);
+            MgMultiCurveString mcs = gf.CreateMultiCurveString(csc);
+
+            Assert.AreEqual(1, mcs.Count);
+            Assert.AreEqual(MgGeometryType.MultiCurveString, mcs.GeometryType);
+            Assert.AreEqual(1, mcs.Dimension);
+        }
+    }
+
+    public class PropertiesTestMultiGeometry : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate pt1 = gf.CreateCoordinateXY(0, 0);
+            MgCoordinate pt2 = gf.CreateCoordinateXY(0, 10);
+            MgCoordinate pt3 = gf.CreateCoordinateXY(10, 10);
+            MgCoordinate pt4 = gf.CreateCoordinateXY(10, 0);
+            MgCoordinateCollection coordinates = new MgCoordinateCollection();
+            coordinates.Add(pt1);
+            coordinates.Add(pt2);
+            coordinates.Add(pt3);
+            coordinates.Add(pt4);
+            MgLineString ls = gf.CreateLineString(coordinates);
+            MgGeometryCollection geometries = new MgGeometryCollection();
+            geometries.Add(ls);
+            MgMultiGeometry mg = gf.CreateMultiGeometry(geometries);
+
+            Assert.AreEqual(1, mg.Count);
+            Assert.AreEqual(MgGeometryType.MultiGeometry, mg.GeometryType);
+            Assert.AreEqual(4, mg.Dimension);
+        }
+    }
+
+    public class PropertiesTestMultiLineString : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate pt1 = gf.CreateCoordinateXY(0, 0);
+            MgCoordinate pt2 = gf.CreateCoordinateXY(0, 10);
+            MgCoordinate pt3 = gf.CreateCoordinateXY(10, 10);
+            MgCoordinate pt4 = gf.CreateCoordinateXY(10, 0);
+            MgCoordinateCollection coordinates = new MgCoordinateCollection();
+            coordinates.Add(pt1);
+            coordinates.Add(pt2);
+            coordinates.Add(pt3);
+            coordinates.Add(pt4);
+            MgLineString ls = gf.CreateLineString(coordinates);
+            MgLineStringCollection lineStringCollection = new MgLineStringCollection();
+            lineStringCollection.Add(ls);
+            MgMultiLineString mls = gf.CreateMultiLineString(lineStringCollection);
+
+            Assert.AreEqual(1, mls.Count);
+            Assert.AreEqual(MgGeometryType.MultiLineString, mls.GeometryType);
+            Assert.AreEqual(1, mls.Dimension);
+        }
+    }
+
+    public class PropertiesTestMultiPoint : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate pt1 = gf.CreateCoordinateXY(0, 0);
+            MgCoordinate pt2 = gf.CreateCoordinateXY(0, 10);
+            MgCoordinate pt3 = gf.CreateCoordinateXY(10, 10);
+            MgCoordinate pt4 = gf.CreateCoordinateXY(10, 0);
+            MgPoint mgpt1 = gf.CreatePoint(pt1);
+            MgPoint mgpt2 = gf.CreatePoint(pt2);
+            MgPoint mgpt3 = gf.CreatePoint(pt3);
+            MgPoint mgpt4 = gf.CreatePoint(pt4);
+            MgPointCollection points = new MgPointCollection();
+            points.Add(mgpt1);
+            points.Add(mgpt2);
+            points.Add(mgpt3);
+            points.Add(mgpt4);
+            MgMultiPoint mp = gf.CreateMultiPoint(points);
+
+            Assert.AreEqual(4, mp.Count);
+            Assert.AreEqual(MgGeometryType.MultiPoint, mp.GeometryType);
+            Assert.AreEqual(0, mp.Dimension);
+        }
+    }
+
+    public class PropertiesTestPolygon : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            MgGeometryFactory gf = new MgGeometryFactory();
+            MgCoordinate pt1 = gf.CreateCoordinateXY(0, 0);
+            MgCoordinate pt2 = gf.CreateCoordinateXY(0, 10);
+            MgCoordinate pt3 = gf.CreateCoordinateXY(10, 10);
+            MgCoordinate pt4 = gf.CreateCoordinateXY(10, 0);
+            MgCoordinateCollection coordinates = new MgCoordinateCollection();
+            coordinates.Add(pt1);
+            coordinates.Add(pt2);
+            coordinates.Add(pt3);
+            coordinates.Add(pt4);
+            MgLinearRing linearRing = gf.CreateLinearRing(coordinates);
+            MgPolygon polygon = gf.CreatePolygon(linearRing, null);
+
+            Assert.AreEqual(linearRing.ToString(), polygon.ExteriorRing.ToString());
+            Assert.AreEqual(0, polygon.InteriorRingCount);
+            Assert.AreEqual(MgGeometryType.Polygon, polygon.GeometryType);
+            Assert.AreEqual(2, polygon.Dimension);
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/FeatureService/FeatureServiceOperationExecutor.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/FeatureService/FeatureServiceOperationExecutor.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/FeatureService/FeatureServiceOperationExecutor.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,23 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    /// <summary>
+    /// The base class of all test executors using the MgFeatureService
+    /// </summary>
+    /// <typeparam name="T">The test executor subclass type</typeparam>
+    public abstract class FeatureServiceOperationExecutor<T> : PlatformApiTestExecutor
+    {
+        protected MgFeatureService _featureService;
+
+        protected FeatureServiceOperationExecutor(MgFeatureService featSvc, string unitTestVm)
+            : base(typeof(T).Name.ToUpper(), ApiTypes.Platform, unitTestVm)
+        {
+            _featureService = featSvc;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/FeatureService/Operations.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/FeatureService/Operations.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/FeatureService/Operations.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,468 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    public class GetFeatureProviders : FeatureServiceOperationExecutor<GetFeatureProviders>
+    {
+        public GetFeatureProviders(MgFeatureService featSvc, string unitTestVm)
+            : base(featSvc, unitTestVm)
+        {
+
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgByteReader reader = _featureService.GetFeatureProviders();
+                return TestResult.FromByteReader(null);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class GetProviderCapabilities : FeatureServiceOperationExecutor<GetProviderCapabilities>
+    {
+        public GetProviderCapabilities(MgFeatureService featSvc, string unitTestVm)
+            : base(featSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "PROVIDER" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgByteReader reader = _featureService.GetCapabilities(param["PROVIDER"] ?? "");
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class GetConnectionPropertyValues : FeatureServiceOperationExecutor<GetConnectionPropertyValues>
+    {
+        public GetConnectionPropertyValues(MgFeatureService featSvc, string unitTestVm)
+            : base(featSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "PROVIDER", "PROPERTY", "CONNECTIONSTRING" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgStringCollection result = _featureService.GetConnectionPropertyValues(param["PROVIDER"], param["PROPERTY"], param["CONNECTIONSTRING"]);
+                return new TestResult(CommonUtility.MgStringCollectionToString(result), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class DescribeFeatureSchema : FeatureServiceOperationExecutor<DescribeFeatureSchema>
+    {
+        public DescribeFeatureSchema(MgFeatureService featSvc, string unitTestVm)
+            : base(featSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "SCHEMA" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                string result = _featureService.DescribeSchemaAsXml(resId, param["SCHEMA"]);
+
+                return new TestResult(result, "text/xml");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class SelectFeatures : FeatureServiceOperationExecutor<SelectFeatures>
+    {
+        public SelectFeatures(MgFeatureService featSvc, string unitTestVm)
+            : base(featSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "CLASSNAME", "PROPERTIES", "FILTER" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgFeatureQueryOptions query = new MgFeatureQueryOptions();
+                //TODO: Set query options
+                if (param["FILTER"] != null)
+                {
+                    query.SetFilter(param["FILTER"]);
+                }
+
+                //PROPERTIES should be stored in the database as comma separated string without spaces
+                if (param["PROPERTIES"] != null)
+                {
+                    string[] props = param["PROPERTIES"].Split(',');
+                    foreach (var p in props)
+                    {
+                        query.AddFeatureProperty(p);
+                    }
+                }
+
+                MgFeatureReader reader = _featureService.SelectFeatures(resId, param["CLASSNAME"], query);
+                MgByteReader byteReader = reader.ToXml();
+                reader.Close();
+                return TestResult.FromByteReader(byteReader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class SelectAggregates : FeatureServiceOperationExecutor<SelectAggregates>
+    {
+        public SelectAggregates(MgFeatureService featSvc, string unitTestVm)
+            : base(featSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "CLASSNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgFeatureAggregateOptions query = new MgFeatureAggregateOptions();
+                //TODO: Set query options
+                /*
+                if (param["FILTER"] != null)
+                {
+                    query.SetFilter(param["FILTER"]);
+                }
+                */
+                //PROPERTIES should be stored in the database as comma separated string without spaces
+                /*
+                if (param["PROPERTIES"] != null)
+                {
+                    string[] props = param["PROPERTIES"].Split(',');
+                    foreach (var p in props)
+                    {
+                        query.AddFeatureProperty(p);
+                    }
+                }*/
+
+                MgDataReader reader = _featureService.SelectAggregate(resId, param["CLASSNAME"], query);
+                MgByteReader byteReader = reader.ToXml();
+                reader.Close();
+                return TestResult.FromByteReader(byteReader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class ExecuteSqlQuery : FeatureServiceOperationExecutor<ExecuteSqlQuery>
+    {
+        public ExecuteSqlQuery(MgFeatureService featSvc, string unitTestVm)
+            : base(featSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "SQL" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgSqlDataReader reader = _featureService.ExecuteSqlQuery(resId, param["SQL"]);
+                MgByteReader byteReader = reader.ToXml();
+                reader.Close();
+                return TestResult.FromByteReader(byteReader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class GetSpatialContexts : FeatureServiceOperationExecutor<GetSpatialContexts>
+    {
+        public GetSpatialContexts(MgFeatureService featSvc, string unitTestVm)
+            : base(featSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "ACTIVEONLY" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgSpatialContextReader reader = _featureService.GetSpatialContexts(resId, (param["ACTIVEONLY"] == "1"));
+                MgByteReader byteReader = reader.ToXml();
+                reader.Close();
+                return TestResult.FromByteReader(byteReader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class GetLongTransactions : FeatureServiceOperationExecutor<GetLongTransactions>
+    {
+        public GetLongTransactions(MgFeatureService featSvc, string unitTestVm)
+            : base(featSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "ACTIVEONLY" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgLongTransactionReader reader = _featureService.GetLongTransactions(resId, (param["ACTIVEONLY"] == "1"));
+                MgByteReader byteReader = reader.ToXml();
+                reader.Close();
+                return TestResult.FromByteReader(byteReader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public interface ISessionCreator
+    {
+        string CreateSession();
+    }
+
+    public interface IApplySession
+    {
+        void SetSessionId(string sessionId);
+    }
+
+    public class SetLongTransaction : FeatureServiceOperationExecutor<SetLongTransaction>
+    {
+        private ISessionCreator _creator;
+        private IApplySession _apply;
+
+        public SetLongTransaction(MgFeatureService featSvc, string unitTestVm, ISessionCreator creator, IApplySession apply)
+            : base(featSvc, unitTestVm)
+        {
+            _creator = creator;
+            _apply = apply;
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "LONGTRANSACTIONNAME", "CREATESESSION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                if (param["CREATESESSION"] == "TRUE")
+                {
+                    string sessionId = _creator.CreateSession();
+                    _apply.SetSessionId(sessionId);
+                }
+
+                bool bRes = _featureService.SetLongTransaction(resId, param["LONGTRANSACTIONNAME"]);
+
+                return new TestResult(CommonUtility.BooleanToString(bRes), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class TestConnection : FeatureServiceOperationExecutor<TestConnection>
+    {
+        public TestConnection(MgFeatureService featSvc, string unitTestVm)
+            : base(featSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "PROVIDER", "CONNECTIONSTRING" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                bool bRes = false;
+                MgResourceIdentifier resId = null;
+                if (!string.IsNullOrEmpty(param["RESOURCEID"]))
+                {
+                    resId = new MgResourceIdentifier(param["RESOURCEID"]);
+                    bRes = _featureService.TestConnection(resId);
+                }
+                else
+                {
+                    bRes = _featureService.TestConnection(param["PROVIDER"] ?? "", param["CONNECTIONSTRING"] ?? "");
+                }
+
+                return new TestResult(CommonUtility.BooleanToString(bRes), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class GetSchemas : FeatureServiceOperationExecutor<GetSchemas>
+    {
+        public GetSchemas(MgFeatureService featSvc, string unitTestVm)
+            : base(featSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgStringCollection result = _featureService.GetSchemas(resId);
+                return new TestResult(CommonUtility.MgStringCollectionToString(result), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class GetClasses : FeatureServiceOperationExecutor<GetClasses>
+    {
+        public GetClasses(MgFeatureService featSvc, string unitTestVm)
+            : base(featSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "SCHEMANAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgStringCollection result = _featureService.GetClasses(resId, param["SCHEMANAME"]);
+                return new TestResult(CommonUtility.MgStringCollectionToString(result), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ITestExecutor.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ITestExecutor.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ITestExecutor.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    /// <summary>
+    /// Executes a particular MapGuide API
+    /// </summary>
+    public interface ITestExecutor : IDisposable
+    {
+        /// <summary>
+        /// The API flavour we're executing. See <see cref="OSGeo.MapGuide.Test.Common.ApiTypes"/>
+        /// </summary>
+        string Api { get; }
+
+        /// <summary>
+        /// The name of the operation
+        /// </summary>
+        string OperationName { get; }
+
+        /// <summary>
+        /// Executes the API with the particular parameter set
+        /// </summary>
+        /// <param name="paramSetId"></param>
+        /// <param name="logger"></param>
+        /// <returns></returns>
+        TestResult Execute(int paramSetId, ITestLogger logger);
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ITestExecutorCollection.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ITestExecutorCollection.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ITestExecutorCollection.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    /// <summary>
+    /// A collection of test executors
+    /// </summary>
+    public interface ITestExecutorCollection
+    {
+        /// <summary>
+        /// Initializes this collection of test executors
+        /// </summary>
+        /// <param name="testExecutionMode"></param>
+        /// <param name="dumpFile"></param>
+        void Initialize(string testExecutionMode, string dumpFile);
+
+        /// <summary>
+        /// Gets the dump file path
+        /// </summary>
+        string DumpFile { get; }
+
+        /// <summary>
+        /// Gets the test execution mode
+        /// </summary>
+        string TestExecutionMode { get; }
+
+        /// <summary>
+        /// Gets the test executor of the specified operation name
+        /// </summary>
+        /// <param name="opName"></param>
+        /// <returns></returns>
+        ITestExecutor GetTestExecutor(string opName);
+
+        /// <summary>
+        /// Executes the test suite as defined in the dump file
+        /// </summary>
+        /// <param name="testsRun"></param>
+        /// <param name="logger"></param>
+        /// <param name="isEnterprise"></param>
+        /// <returns></returns>
+        int Execute(ref int testsRun, ITestLogger logger, bool isEnterprise);
+
+        /// <summary>
+        /// Validates a test execution result
+        /// </summary>
+        /// <param name="db"></param>
+        /// <param name="testName"></param>
+        /// <param name="paramSetId"></param>
+        /// <param name="operation"></param>
+        /// <param name="actualResult"></param>
+        /// <param name="logger"></param>
+        /// <returns></returns>
+        int ValidateRequest(SqliteDb db, string testName, int paramSetId, string operation, TestResult actualResult, ITestLogger logger);
+
+        /// <summary>
+        /// Performs necessary cleanup
+        /// </summary>
+        void Cleanup();
+    }
+
+    public abstract class TestExecutorCollectionBase : ITestExecutorCollection
+    {
+        public string TestExecutionMode { get; protected set; }
+
+        public string DumpFile { get; protected set; }
+
+        public abstract string ApiType { get; }
+
+        public void Initialize(string testExecutionMode, string dumpFile)
+        {
+            this.TestExecutionMode = testExecutionMode;
+            this.DumpFile = dumpFile;
+        }
+
+        protected abstract void SetupExecutors(string dbPath);
+
+        public int Execute(ref int testsRun, ITestLogger logger, bool isEnterprise)
+        {
+            int exitStatus = 0;
+            string dbPath = CommonUtility.GetDbPath(this.DumpFile);
+            string dbName = Path.GetFullPath(CommonUtility.GetPath(dbPath));
+
+            if (File.Exists(dbName))
+            {
+                var db = new SqliteDb();
+                db.Open(dbName);
+
+                var vm = new SqliteVm(db, true);
+
+                int status = vm.Execute($"Select TestName, TestType from TestCase where TestType=\"{ApiType}\" order by ExecuteSequence");
+
+                //NOTE: We can't share the SqliteVm instance among our executor objects as this messes up query results
+                //we must be able to re-create a new SqliteVm for each executor, so we pass down the db path
+                SetupExecutors(dbName);
+
+                while (status == Sqlite.Row)
+                {
+                    string testName = vm.GetString("TestName");
+                    string testType = vm.GetString("TestType");
+
+                    Console.WriteLine("Executing {0} test: {1}", testType, testName);
+                    using (var run = new TestExecutionRun(dbPath, this))
+                    {
+                        try
+                        {
+                            exitStatus += run.RunTests(testName, logger, ref testsRun);
+                        }
+                        catch (Exception ex)
+                        {
+                            Console.WriteLine(ex.ToString());
+                            exitStatus += 1;
+                        }
+                    }
+                    status = vm.NextRow();
+                }
+                vm.SqlFinalize();
+                vm = null;
+                db = null;
+            }
+            return exitStatus;
+        }
+
+        public abstract ITestExecutor GetTestExecutor(string opName);
+
+        public abstract int ValidateRequest(SqliteDb db, string testName, int paramSetId, string operation, TestResult actualResult, ITestLogger logger);
+
+        public abstract void Cleanup();
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ITestLogger.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ITestLogger.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ITestLogger.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    /// <summary>
+    /// A simple logger interface
+    /// </summary>
+    public interface ITestLogger : IDisposable
+    {
+        void Write(string format, params object[] args);
+        void WriteLine(string format, params object[] args);
+    }
+
+    /// <summary>
+    /// A logger for command line output
+    /// </summary>
+    public class TestLoggerConsole : ITestLogger
+    {
+        public void Write(string format, params object[] args)
+        {
+            Console.Write(format, args);
+        }
+
+        public void WriteLine(string format, params object[] args)
+        {
+            Console.WriteLine(format, args);
+        }
+
+        public void Dispose()
+        {
+
+        }
+    }
+
+    /// <summary>
+    /// A logger for file output
+    /// </summary>
+    public class TestLoggerFile : ITestLogger
+    {
+        private StreamWriter sw;
+
+        public TestLoggerFile(string file, bool append)
+        {
+            FileMode mode = FileMode.Append;
+            if (!append)
+            {
+                if (File.Exists(file))
+                    mode = FileMode.Truncate;
+                else
+                    mode = FileMode.OpenOrCreate;
+            }
+
+            var fs = new FileStream(file, mode);
+            sw = new StreamWriter(fs);
+            sw.AutoFlush = true;
+        }
+
+        public void Write(string format, params object[] args)
+        {
+            sw.Write(format, args);
+        }
+
+        public void WriteLine(string format, params object[] args)
+        {
+            sw.WriteLine(format, args);
+        }
+
+        public void Dispose()
+        {
+            sw?.Dispose();
+            sw = null;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/MapLayer/MapLayerOperationExecutor.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/MapLayer/MapLayerOperationExecutor.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/MapLayer/MapLayerOperationExecutor.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,73 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    public interface IMapCreator
+    {
+        MgMapBase CreateMap(MgResourceIdentifier resId);
+        MgLayerBase CreateLayer(MgResourceIdentifier resId);
+    }
+
+    /// <summary>
+    /// The base class of all test executors operating with the runtime map
+    /// </summary>
+    /// <typeparam name="T">The test executor subclass type</typeparam>
+    public abstract class MapLayerOperationExecutor<T> : PlatformApiTestExecutor
+    {
+        protected MgMapBase _map;
+        protected MgResourceService _resourceService;
+        protected IMapCreator _creator;
+
+        protected MapLayerOperationExecutor(MgResourceService resSvc, string unitTestVm, IMapCreator creator)
+            : base(typeof(T).Name.ToUpper(), ApiTypes.Platform, unitTestVm)
+        {
+            _resourceService = resSvc;
+            _creator = creator;
+        }
+
+        /*
+        protected void CreateMapFromResource(int paramSetId)
+        {
+            if (_map != null)
+                return;
+
+            _unitTestVm.Execute("Select ParamValue from Params WHERE ParamSet={0} AND ParamName=\"MAPDEFINITION\"", paramSetId);
+            string mapName = _unitTestVm.GetString("ParamValue");
+            if (string.IsNullOrEmpty(mapName))
+            {
+                mapName = "Library://maplayertest/World.MapDefinition";
+            }
+
+            MgResourceIdentifier mdfId = new MgResourceIdentifier(mapName);
+            _map = _creator.CreateMap(mdfId);
+        }
+        */
+
+        protected void CreateMapFromResource(NameValueCollection param)
+        {
+            if (_map != null)
+                return;
+
+            string mapName = param["MAPDEFINITION"] ?? "Library://maplayertest/World.MapDefinition";
+            MgResourceIdentifier mdfId = new MgResourceIdentifier(mapName);
+            _map = _creator.CreateMap(mdfId);
+        }
+
+        protected MgLayerBase FindLayer(string name)
+        {
+            if (_map != null)
+            {
+                var layers = _map.GetLayers();
+                var lidx = layers.IndexOf(name);
+                if (lidx >= 0)
+                    return layers.GetItem(lidx);
+            }
+            return null;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/MapLayer/Operations.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/MapLayer/Operations.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/MapLayer/Operations.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,599 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Globalization;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    public class GetMapExtent : MapLayerOperationExecutor<GetMapExtent>
+    {
+        public GetMapExtent(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                var extent = _map.GetMapExtent();
+                return new TestResult(CommonUtility.MgEnvelopeToString(extent), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetCoordinateSystem : MapLayerOperationExecutor<GetCoordinateSystem>
+    {
+        public GetCoordinateSystem(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                return new TestResult(_map.GetMapSRS(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetMapName : MapLayerOperationExecutor<GetMapName>
+    {
+        public GetMapName(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                return new TestResult(_map.GetName(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetLayers : MapLayerOperationExecutor<GetLayers>
+    {
+        public GetLayers(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                var layers = _map.GetLayers();
+                var names = new List<string>();
+                for (int i = 0; i < layers.GetCount(); i++)
+                {
+                    var layer = layers.GetItem(i);
+                    names.Add(layer.GetName());
+                }
+
+                return new TestResult(string.Join("/", names.ToArray()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetGroups : MapLayerOperationExecutor<GetGroups>
+    {
+        public GetGroups(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                var groups = _map.GetLayerGroups();
+                var names = new List<string>();
+                for (int i = 0; i < groups.GetCount(); i++)
+                {
+                    var group = groups.GetItem(i);
+                    names.Add(group.GetName());
+                }
+
+                return new TestResult(string.Join("/", names.ToArray()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetLayerVisibility : MapLayerOperationExecutor<GetLayerVisibility>
+    {
+        public GetLayerVisibility(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "LAYERNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                string layerName = param["LAYERNAME"];
+
+                var layer = this.FindLayer(layerName);
+                if (layer == null)
+                    return new TestResult("***Unknown Layer***", "text/plain");
+                return new TestResult(CommonUtility.BooleanToString(layer.GetVisible()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetDisplayInLegend : MapLayerOperationExecutor<GetDisplayInLegend>
+    {
+        public GetDisplayInLegend(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "LAYERNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                string layerName = param["LAYERNAME"];
+
+                var layer = this.FindLayer(layerName);
+                if (layer == null)
+                    return new TestResult("***Unknown Layer***", "text/plain");
+                return new TestResult(CommonUtility.BooleanToString(layer.GetDisplayInLegend()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetLegendLabel : MapLayerOperationExecutor<GetLegendLabel>
+    {
+        public GetLegendLabel(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "LAYERNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                string layerName = param["LAYERNAME"];
+
+                var layer = this.FindLayer(layerName);
+                if (layer == null)
+                    return new TestResult("***Unknown Layer***", "text/plain");
+                return new TestResult(layer.GetLegendLabel(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetLayerDefinition : MapLayerOperationExecutor<GetLayerDefinition>
+    {
+        public GetLayerDefinition(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "LAYERNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                string layerName = param["LAYERNAME"];
+
+                var layer = this.FindLayer(layerName);
+                if (layer == null)
+                    return new TestResult("***Unknown Layer***", "text/plain");
+                var ldfId = layer.GetLayerDefinition();
+                return new TestResult(ldfId.ToString(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class IsLayerVisible : MapLayerOperationExecutor<IsLayerVisible>
+    {
+        public IsLayerVisible(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "LAYERNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                string layerName = param["LAYERNAME"];
+
+                var layer = this.FindLayer(layerName);
+                if (layer == null)
+                    return new TestResult("***Unknown Layer***", "text/plain");
+                return new TestResult(CommonUtility.BooleanToString(layer.IsVisible()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class AddLayerGroup : MapLayerOperationExecutor<AddLayerGroup>
+    {
+        public AddLayerGroup(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "GROUPNAME", "PARENTGROUPNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                
+                string groupName = param["GROUPNAME"];
+                string parentGroupName = param["PARENTGROUPNAME"];
+
+                var groups = _map.GetLayerGroups();
+                var group = new MgLayerGroup(groupName);
+                if (!string.IsNullOrEmpty(parentGroupName))
+                {
+                    var parentGroup = groups.GetItem(parentGroupName);
+                    group.SetGroup(parentGroup);
+                }
+                groups.Add(group);
+
+                return new TestResult(groups.GetCount().ToString(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class AddLayer : MapLayerOperationExecutor<AddLayer>
+    {
+        public AddLayer(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "LAYERDEFINITION", "GROUPNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                string layerDefinition = param["LAYERDEFINITION"];
+                string groupName = param["GROUPNAME"];
+
+                var ldfId = new MgResourceIdentifier(layerDefinition);
+                var layer = _creator.CreateLayer(ldfId);
+
+                if (!string.IsNullOrEmpty(groupName))
+                {
+                    var groups = _map.GetLayerGroups();
+                    var gidx = groups.IndexOf(groupName);
+                    if (gidx >= 0)
+                        layer.SetGroup(groups.GetItem(gidx));
+                }
+
+                var layers = _map.GetLayers();
+                layers.Add(layer);
+
+                return new TestResult(layers.GetCount().ToString(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class HideGroup : MapLayerOperationExecutor<HideGroup>
+    {
+        public HideGroup(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "GROUPNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                string groupName = param["GROUPNAME"];
+
+                var groups = _map.GetLayerGroups();
+                var group = groups.GetItem(groupName);
+
+                group.SetVisible(false);
+                return new TestResult(CommonUtility.BooleanToString(group.GetVisible()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class ShowGroup : MapLayerOperationExecutor<ShowGroup>
+    {
+        public ShowGroup(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "GROUPNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                string groupName = param["GROUPNAME"];
+
+                var groups = _map.GetLayerGroups();
+                var group = groups.GetItem(groupName);
+
+                group.SetVisible(true);
+                return new TestResult(CommonUtility.BooleanToString(group.GetVisible()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetViewCenter : MapLayerOperationExecutor<GetViewCenter>
+    {
+        public GetViewCenter(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                return new TestResult(CommonUtility.MgPointToString(_map.GetViewCenter()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetViewScale : MapLayerOperationExecutor<GetViewScale>
+    {
+        public GetViewScale(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                return new TestResult(_map.GetViewScale().ToString(CultureInfo.InvariantCulture), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetDataExtent : MapLayerOperationExecutor<GetDataExtent>
+    {
+        public GetDataExtent(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+
+                var extent = _map.GetDataExtent();
+                return new TestResult(CommonUtility.MgEnvelopeToString(extent), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class RemoveGroup : MapLayerOperationExecutor<RemoveGroup>
+    {
+        public RemoveGroup(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "GROUPNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                string groupName = param["GROUPNAME"];
+
+                var groups = _map.GetLayerGroups();
+                var group = groups.GetItem(groupName);
+
+                groups.Remove(group);
+
+                return new TestResult("", "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class LayerExists : MapLayerOperationExecutor<LayerExists>
+    {
+        public LayerExists(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "LAYERNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                string layerName = param["LAYERNAME"];
+
+                var layer = this.FindLayer(layerName);
+
+                return new TestResult(CommonUtility.BooleanToString(layer != null), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetLayerFeatureSource : MapLayerOperationExecutor<GetLayerFeatureSource>
+    {
+        public GetLayerFeatureSource(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "LAYERNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                string layerName = param["LAYERNAME"];
+
+                var layer = this.FindLayer(layerName);
+                if (layer == null)
+                    return new TestResult("***Unknown Layer***", "text/plain");
+                return new TestResult(layer.GetFeatureSourceId(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetLayerFeatureClass : MapLayerOperationExecutor<GetLayerFeatureClass>
+    {
+        public GetLayerFeatureClass(MgResourceService resSvc, string vm, IMapCreator creator)
+            : base(resSvc, vm, creator)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "MAPDEFINITION", "LAYERNAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                this.CreateMapFromResource(param);
+                string layerName = param["LAYERNAME"];
+
+                var layer = this.FindLayer(layerName);
+                return new TestResult(layer.GetFeatureClassName(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/PlatformApiTestExecutor.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/PlatformApiTestExecutor.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/PlatformApiTestExecutor.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,82 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Collections.Specialized;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    /// <summary>
+    /// The base class of all MapGuide API test executors
+    /// </summary>
+    public abstract class PlatformApiTestExecutor : TestExecutorBase
+    {
+        protected string _unitTestVmPath;
+        private SqliteDb _unitTestDb;
+        protected SqliteVm _unitTestVm;
+
+        protected PlatformApiTestExecutor(string opName, string apiType, string unitTestVm)
+        {
+            _opName = opName;
+            _apiType = apiType;
+            _unitTestVmPath = unitTestVm;
+
+            _unitTestDb = new SqliteDb();
+            _unitTestDb.Open(_unitTestVmPath);
+
+            _unitTestVm = new SqliteVm(_unitTestDb, true);
+        }
+        
+        protected override NameValueCollection CollectParameters(int paramSetId)
+        {
+            var param = new NameValueCollection();
+            var fp = this.PathParameterNames;
+
+            foreach (var name in this.ParameterNames.Concat(new string[] { "OPERATION" }))
+            {
+                var isFile = fp.Contains(name);
+                _unitTestVm.ReadParameterValue(paramSetId, name, param, isFile);
+                if (isFile && string.IsNullOrEmpty(param[name]))
+                    param.Remove(name);
+            }
+
+            return param;
+        }
+
+        protected override void CleanParamSet(NameValueCollection param)
+        {
+            foreach (var name in this.PathParameterNames)
+            {
+                if (param[name] != null)
+                    param[name] = CommonUtility.GetPath(param[name]);
+            }
+        }
+
+        public override void Dispose()
+        {
+            _unitTestVm.SqlFinalize();
+            _unitTestVm = null;
+            try
+            {
+                _unitTestDb.Close();
+            }
+            catch { }
+            _unitTestDb = null;
+        }
+
+        private string _apiType;
+
+        public override string Api
+        {
+            get { return _apiType; }
+        }
+
+        private string _opName;
+
+        public override string OperationName
+        {
+            get { return _opName; }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/PlatformResources.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/PlatformResources.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/PlatformResources.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    public static class PlatformResources
+    {
+        public const string TestLayer = @"<?xml version=""1.0"" encoding=""utf-8""?>
+<LayerDefinition xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" version=""1.0.0"" xsi:noNamespaceSchemaLocation=""LayerDefinition-1.0.0.xsd"">
+  <VectorLayerDefinition>
+    <ResourceId>{0}</ResourceId>
+    <FeatureName>{1}</FeatureName>
+    <FeatureNameType>FeatureClass</FeatureNameType>
+    <Geometry>{2}</Geometry>
+    <VectorScaleRange>
+      <PointTypeStyle>
+        <DisplayAsText>false</DisplayAsText>
+        <AllowOverpost>false</AllowOverpost>
+        <PointRule>
+          <LegendLabel />
+          <PointSymbolization2D>
+            <Mark>
+              <Unit>Points</Unit>
+              <SizeContext>DeviceUnits</SizeContext>
+              <SizeX>10</SizeX>
+              <SizeY>10</SizeY>
+              <Rotation>0</Rotation>
+              <Shape>Square</Shape>
+              <Fill>
+                <FillPattern>Solid</FillPattern>
+                <ForegroundColor>ffffffff</ForegroundColor>
+                <BackgroundColor>ffffffff</BackgroundColor>
+              </Fill>
+              <Edge>
+                <LineStyle>Solid</LineStyle>
+                <Thickness>1</Thickness>
+                <Color>ff000000</Color>
+                <Unit>Points</Unit>
+              </Edge>
+            </Mark>
+          </PointSymbolization2D>
+        </PointRule>
+      </PointTypeStyle>
+      <LineTypeStyle>
+        <LineRule>
+          <LegendLabel />
+          <LineSymbolization2D>
+            <LineStyle>Solid</LineStyle>
+            <Thickness>1</Thickness>
+            <Color>ff000000</Color>
+            <Unit>Points</Unit>
+          </LineSymbolization2D>
+        </LineRule>
+      </LineTypeStyle>
+      <AreaTypeStyle>
+        <AreaRule>
+          <LegendLabel />
+          <AreaSymbolization2D>
+            <Fill>
+              <FillPattern>Solid</FillPattern>
+              <ForegroundColor>ffffffff</ForegroundColor>
+              <BackgroundColor>ffffffff</BackgroundColor>
+            </Fill>
+            <Stroke>
+              <LineStyle>Solid</LineStyle>
+              <Thickness>1</Thickness>
+              <Color>ff000000</Color>
+              <Unit>Points</Unit>
+            </Stroke>
+          </AreaSymbolization2D>
+        </AreaRule>
+      </AreaTypeStyle>
+    </VectorScaleRange>
+  </VectorLayerDefinition>
+</LayerDefinition>";
+
+        public const string TestMapDef = @"<?xml version=""1.0"" encoding=""utf-8""?>
+<MapDefinition xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" version=""2.4.0"" xsi:noNamespaceSchemaLocation=""MapDefinition-2.4.0.xsd"">
+  <Name>Test Map</Name>
+  <CoordinateSystem>{0}</CoordinateSystem>
+  <Extents>
+    <MinX>{1}</MinX>
+    <MaxX>{2}</MaxX>
+    <MinY>{3}</MinY>
+    <MaxY>{4}</MaxY>
+  </Extents>
+  <BackgroundColor>ffffffff</BackgroundColor>
+</MapDefinition>";
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Properties/AssemblyInfo.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Properties/AssemblyInfo.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Properties/AssemblyInfo.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,23 @@
+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("TestCommon")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TestCommon")]
+[assembly: AssemblyCopyright("Copyright ©  2015")]
+[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("75850376-f5cc-48e2-a527-79c9613a04da")]

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ResourceService/Operations.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ResourceService/Operations.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ResourceService/Operations.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,647 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    public class EnumerateResources : ResourceServiceOperationExecutor<EnumerateResources>
+    {
+        public EnumerateResources(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "TYPE", "DEPTH" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+                MgByteReader byteReader = _resourceService.EnumerateResources(resId, Convert.ToInt32(param["DEPTH"]), param["TYPE"] ?? "");
+
+                return TestResult.FromByteReader(byteReader, "GETRESOURCEDATA");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class SetResource : ResourceServiceOperationExecutor<SetResource>
+    {
+        public SetResource(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "CONTENT", "HEADER" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader content = param.TryGetByteReader("CONTENT");
+                MgByteReader header = param.TryGetByteReader("HEADER");
+
+                _resourceService.SetResource(resId, content, header);
+
+                return TestResult.FromByteReader(null);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class DeleteResource : ResourceServiceOperationExecutor<DeleteResource>
+    {
+        public DeleteResource(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                _resourceService.DeleteResource(resId);
+
+                return TestResult.FromByteReader(null);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class GetResourceContent : ResourceServiceOperationExecutor<GetResourceContent>
+    {
+        public GetResourceContent(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "PREPROCESS" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader reader = _resourceService.GetResourceContent(resId);
+
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class GetResourceHeader : ResourceServiceOperationExecutor<GetResourceHeader>
+    {
+        public GetResourceHeader(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader reader = _resourceService.GetResourceHeader(resId);
+
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class EnumerateResourceData : ResourceServiceOperationExecutor<EnumerateResourceData>
+    {
+        public EnumerateResourceData(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader reader = _resourceService.EnumerateResourceData(resId);
+
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class GetResourceData : ResourceServiceOperationExecutor<GetResourceData>
+    {
+        public GetResourceData(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "DATANAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader reader = _resourceService.GetResourceData(resId, param["DATANAME"]);
+
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class SetResourceData : ResourceServiceOperationExecutor<SetResourceData>
+    {
+        public SetResourceData(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "DATANAME", "DATATYPE", "DATA" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                string extension = CommonUtility.GetExtension(param["DATANAME"]);
+                string mimeType = CommonUtility.GetMimeType(extension);
+                MgByteSource dataSource = new MgByteSource(CommonUtility.FixRelativePath(param["DATA"]));
+                dataSource.SetMimeType(mimeType);
+                MgByteReader dataReader = dataSource.GetReader();
+
+                _resourceService.SetResourceData(resId, param["DATANAME"], param["DATATYPE"], dataReader);
+
+                return TestResult.FromByteReader(null);
+            }
+            catch (MgException ex)
+            {
+                //HACK/FIXME: The test suite is passing paths with incorrect case to this operation (presumably to exercise
+                //this operation in a Linux environment where case-sensitive paths matter), but there's no way in my knowledge 
+                //to perform platform-specific verification of test results. So what we have is an intentionally failing test 
+                //for a platform that has no means to verify that.
+                //
+                //As a workaround, when such bad paths are encountered (that should present themselves as thrown
+                //MgFileNotFoundException objects), return the result that is expected on Windows: An empty result.
+                if (!CommonUtility.IsWindows() && (ex is MgFileNotFoundException)) {
+                    return TestResult.FromByteReader(null);
+                } else {
+                    return TestResult.FromMgException(ex, param);
+                }
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class RenameResourceData : ResourceServiceOperationExecutor<RenameResourceData>
+    {
+        public RenameResourceData(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "OLDDATANAME", "NEWDATANAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                _resourceService.RenameResourceData(resId, param["OLDDATANAME"], param["NEWDATANAME"], false);
+
+                return TestResult.FromByteReader(null);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class DeleteResourceData : ResourceServiceOperationExecutor<DeleteResourceData>
+    {
+        public DeleteResourceData(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "DATANAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                _resourceService.DeleteResourceData(resId, param["DATANAME"] ?? "");
+
+                return TestResult.FromByteReader(null);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class GetRepositoryContent : ResourceServiceOperationExecutor<GetRepositoryContent>
+    {
+        public GetRepositoryContent(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader result = _resourceService.GetRepositoryContent(resId);
+
+                return TestResult.FromByteReader(result);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class GetRepositoryHeader : ResourceServiceOperationExecutor<GetRepositoryHeader>
+    {
+        public GetRepositoryHeader(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader result = _resourceService.GetRepositoryHeader(resId);
+
+                return TestResult.FromByteReader(result);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class UpdateRepository : ResourceServiceOperationExecutor<UpdateRepository>
+    {
+        public UpdateRepository(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "CONTENT", "HEADER" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader content = param.TryGetByteReader("CONTENT");
+                MgByteReader header = param.TryGetByteReader("HEADER");
+
+                _resourceService.UpdateRepository(resId, content, header);
+
+                return TestResult.FromByteReader(null);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class EnumerateResourceReferences : ResourceServiceOperationExecutor<EnumerateResourceReferences>
+    {
+        public EnumerateResourceReferences(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader reader = _resourceService.EnumerateReferences(resId);
+
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class MoveResource : ResourceServiceOperationExecutor<MoveResource>
+    {
+        public MoveResource(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "SOURCE", "DESTINATION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier srcId = null;
+                if (param["SOURCE"] != null)
+                {
+                    srcId = new MgResourceIdentifier(param["SOURCE"]);
+                }
+
+                MgResourceIdentifier dstId = null;
+                if (param["DESTINATION"] != null)
+                {
+                    dstId = new MgResourceIdentifier(param["DESTINATION"]);
+                }
+
+                _resourceService.MoveResource(srcId, dstId, false);
+
+                return TestResult.FromByteReader(null);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class CopyResource : ResourceServiceOperationExecutor<CopyResource>
+    {
+        public CopyResource(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "SOURCE", "DESTINATION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier srcId = null;
+                if (param["SOURCE"] != null)
+                {
+                    srcId = new MgResourceIdentifier(param["SOURCE"]);
+                }
+
+                MgResourceIdentifier dstId = null;
+                if (param["DESTINATION"] != null)
+                {
+                    dstId = new MgResourceIdentifier(param["DESTINATION"]);
+                }
+
+                _resourceService.CopyResource(srcId, dstId, false);
+
+                return TestResult.FromByteReader(null);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class ChangeResourceOwner : ResourceServiceOperationExecutor<ChangeResourceOwner>
+    {
+        public ChangeResourceOwner(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "OWNER", "INCLUDEDESCENDANTS" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                _resourceService.ChangeResourceOwner(resId, param["OWNER"], (param["INCLUDEDESCENDANTS"] == "1"));
+
+                return TestResult.FromByteReader(null);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class InheritPermissionsFrom : ResourceServiceOperationExecutor<InheritPermissionsFrom>
+    {
+        public InheritPermissionsFrom(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                _resourceService.InheritPermissionsFrom(resId);
+
+                return TestResult.FromByteReader(null);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+
+    public class ApplyResourcePackage : ResourceServiceOperationExecutor<ApplyResourcePackage>
+    {
+        public ApplyResourcePackage(MgResourceService resSvc, string unitTestVm)
+            : base(resSvc, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "PACKAGE" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                if (CommonUtility.IsWindows())
+                {
+                    MgByteReader reader = param.TryGetByteReader("PACKAGE", false);
+    
+                    _resourceService.ApplyResourcePackage(reader);
+    
+                    return TestResult.FromByteReader(null);
+                }
+                else
+                {
+                    throw new Exception("FIXME: ApplyResourcePackage will kill the mgserver on invalid package files");
+                }
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+            catch (Exception ex)
+            {
+                return TestResult.FromException(ex);
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ResourceService/ResourceServiceOperationExecutor.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ResourceService/ResourceServiceOperationExecutor.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/ResourceService/ResourceServiceOperationExecutor.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,25 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    /// <summary>
+    /// The base class of all test executors using the MgResourceService
+    /// </summary>
+    /// <typeparam name="T">The test executor subclass type</typeparam>
+    public abstract class ResourceServiceOperationExecutor<T> : PlatformApiTestExecutor
+    {
+        protected MgResourceService _resourceService;
+
+        protected ResourceServiceOperationExecutor(MgResourceService resSvc, string unitTestVm)
+            : base(typeof(T).Name.ToUpper(), ApiTypes.Platform, unitTestVm)
+        {
+            _resourceService = resSvc;
+        }
+
+        protected override string[] PathParameterNames => new string[] { "HEADER", "CONTENT", "DATA", "PACKAGE" };
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Sqlite.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Sqlite.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/Sqlite.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,131 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Data.Sqlite;
+using System.IO;
+using System.Text;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    public class SqliteDb
+    {
+        private SqliteConnection _conn;
+
+        public void Open(string dbName)
+        {
+            _conn = new SqliteConnection($"Data Source={dbName}");
+            _conn.Open();
+        }
+
+        internal SqliteConnection Connection => _conn;
+
+        public void Close()
+        {
+            _conn?.Close();
+        }
+
+        public void GenerateDatabase(string dumpFileName, string dbName)
+        {
+            using (var conn = new SqliteConnection($"Data Source={dbName}"))
+            {
+                conn.Open();
+
+                using (var cmd = conn.CreateCommand())
+                {
+                    cmd.CommandText = File.ReadAllText(dumpFileName);
+                    cmd.ExecuteNonQuery();
+                }
+            }
+        }
+    }
+
+    public class SqliteGcBlob
+    {
+        private byte[] _bytes;
+
+        public SqliteGcBlob(byte[] bytes)
+        {
+            _bytes = bytes;
+        }
+
+        public byte[] Read() => _bytes;
+    }
+
+    public class SqliteVm
+    {
+        private SqliteDataReader _rdr;
+        private SqliteDb _db;
+
+        public SqliteVm(SqliteDb db, bool b)
+        {
+            _db = db;
+        }
+
+        public int Execute(string sql)
+        {
+            var cmd = _db.Connection.CreateCommand();
+            cmd.CommandText = sql;
+            _rdr = cmd.ExecuteReader();
+            return NextRow();
+        }
+
+        public SqliteGcBlob GetBlob(string name)
+        {
+            if (_rdr == null)
+                return null;
+
+            int ordinal = _rdr.GetOrdinal(name);
+            if (ordinal >= 0)
+            {
+                if (!_rdr.IsDBNull(ordinal))
+                {
+                    var type = _rdr.GetFieldType(ordinal);
+                    if (type == typeof(byte[]))
+                    {
+                        return new SqliteGcBlob(_rdr.GetFieldValue<byte[]>(ordinal));
+                    }
+                    else
+                    {
+                        var result = GetString(name);
+                        return new SqliteGcBlob(Encoding.UTF8.GetBytes(result));
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        public string GetString(string name)
+        {
+            if (_rdr == null)
+                return string.Empty;
+
+            int ordinal = _rdr.GetOrdinal(name);
+            if (ordinal >= 0)
+            {
+                if (!_rdr.IsDBNull(ordinal))
+                {
+                    return _rdr[ordinal].ToString();
+                }
+            }
+
+            return string.Empty;
+        }
+
+        public int NextRow() => (_rdr?.Read() == true) ? Sqlite.Row : Sqlite.EndOfReader;
+
+        public void SqlFinalize()
+        {
+            _rdr?.Dispose();
+            _rdr = null;
+        }
+    }
+
+    public class Sqlite
+    {
+        public const int OK = 0;
+        public const int Row = 1;
+        public const int EndOfReader = 2;
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestCommon.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestCommon.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestCommon.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <Description>TestCommon Class Library</Description>
+    <Authors>OSGeo</Authors>
+    <TargetFrameworks>netstandard2.0</TargetFrameworks>
+    <AssemblyName>TestCommon</AssemblyName>
+    <PackageId>TestCommon</PackageId>
+    <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
+    <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
+    <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
+    <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
+    <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
+    <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Data.SQLite" Version="2.0.1" />
+  </ItemGroup>
+</Project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestDataRoot.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestDataRoot.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestDataRoot.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    public static class TestDataRoot
+    {
+        public static string Path { get; set; }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestExecutionRun.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestExecutionRun.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestExecutionRun.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    /// <summary>
+    /// Executes a <see cref="T:OSGeo.MapGuide.Test.Common.ITestExecutorCollection"/> instance
+    /// </summary>
+    public class TestExecutionRun : IDisposable
+    {
+        private ITestExecutorCollection _executors;
+        private SqliteDb _unitTestDb;
+        private SqliteVm _unitTestVm;
+        private SqliteVm _unitTestResultVm;
+        private bool _generateDump;
+
+        public TestExecutionRun(string dbPath, ITestExecutorCollection exec, bool generateDump = false)
+        {
+            _executors = exec;
+            _generateDump = generateDump;
+            _unitTestDb = new SqliteDb();
+            _unitTestDb.Open(dbPath);
+
+            _unitTestVm = new SqliteVm(_unitTestDb, false);
+            _unitTestResultVm = new SqliteVm(_unitTestDb, false);
+        }
+
+        public void Dispose()
+        {
+            _unitTestResultVm.SqlFinalize();
+            _unitTestResultVm = null;
+            _unitTestVm.SqlFinalize();
+            _unitTestVm = null;
+            try
+            {
+                _unitTestDb.Close();
+            }
+            catch { }
+            _unitTestDb = null;
+        }
+
+        public int RunTests(string testName, ITestLogger logger, ref int testsRun)
+        {
+            TestResult actualResult = null;
+            int exitStatus = 0;
+            try
+            {
+                //Add the test in the log file
+                string msg = "\n\n************************************************************\n" +
+                             string.Format("Executing {0}\n", testName) +
+                             "************************************************************\n\n";
+
+                logger.Write(msg);
+
+                //Get the list of operations to be executed for this test. This list should comma separated, no spaces.
+                _unitTestVm.Execute($"Select ParamSets from TestCase where TestName=\"{testName}\"");
+                string sets = _unitTestVm.GetString("ParamSets");
+
+                //Extract the paramset ids
+                int[] paramSet = sets.Split(',').Where(x => !string.IsNullOrEmpty(x)).Select(x => Convert.ToInt32(x)).ToArray();
+                foreach (int paramSetId in paramSet)
+                {
+                    _unitTestVm.Execute($"Select ParamValue from Params where ParamSet={paramSetId} AND ParamName=\"OPERATION\"");
+                    string paramValue = _unitTestVm.GetString("ParamValue");
+
+                    testsRun++;
+
+                    //Add the operation to the log file
+                    //AddLogFileEntry(logger, paramValue, paramSetId, _unitTestResultVm);
+
+                    var exec = _executors.GetTestExecutor(paramValue);
+                    actualResult = exec.Execute(paramSetId, logger);
+
+                    exitStatus += _executors.ValidateRequest(_unitTestDb, testName, paramSetId, paramValue, actualResult, logger);
+                }
+
+                return exitStatus;
+            }
+            catch (MgException ex)
+            {
+                throw new UnitTestException(string.Format("Exception from MapGuide:\n{0}", ex.GetDetails()));
+            }
+        }
+
+        /*
+        private static void AddLogFileEntry(ITestLogger logger, string operation, int paramSetId, SqliteVm vm)
+        {
+            logger.Write("\nParamSet: {0}\n", paramSetId);
+            int status = vm.Execute($"Select * from Params where ParamSet={paramSetId}");
+
+            while (status == Sqlite.Row)
+            {
+                string paramName = vm.GetString("ParamName");
+                string paramValue = vm.GetString("ParamValue");
+
+                logger.Write("{0}: {1}\n", paramName, paramValue);
+                status = vm.NextRow();
+            }
+        }
+        */
+
+        public void Cleanup()
+        {
+            if (_executors != null)
+            {
+                _executors.Cleanup();
+                _executors = null;
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestExecutorBase.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestExecutorBase.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestExecutorBase.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    public abstract class TestExecutorBase : ITestExecutor
+    {
+        public abstract string Api
+        {
+            get;
+        }
+
+        public abstract string OperationName
+        {
+            get;
+        }
+
+        protected virtual string[] ParameterNames => new string[0];
+
+        protected virtual string[] PathParameterNames => new string[0];
+
+        protected abstract NameValueCollection CollectParameters(int paramSetId);
+
+        protected virtual void CleanParamSet(NameValueCollection param) { }
+
+        protected abstract TestResult ExecuteInternal(NameValueCollection param);
+
+        static bool PathExists(string val) => !string.IsNullOrEmpty(val) ? System.IO.File.Exists(val) : false;
+
+        public TestResult Execute(int paramSetId, ITestLogger logger)
+        {
+            var param = CollectParameters(paramSetId);
+            var fp = this.PathParameterNames;
+
+            CleanParamSet(param);
+
+            //Log param set
+            logger.WriteLine($"ParamSet: {paramSetId}");
+            foreach (string key in param.Keys)
+            {
+                if (fp.Contains(key))
+                    logger.WriteLine($"    {key}: {(param[key] == null ? "<null>" : param[key])} [Exists: {PathExists(param[key])}]");
+                else
+                    logger.WriteLine($"    {key}: {(param[key] == null ? "<null>" : param[key])}");
+            }
+            logger.WriteLine("\n\n");
+
+            return ExecuteInternal(param);
+        }
+
+        public abstract void Dispose();
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestResult.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestResult.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/TestResult.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    /// <summary>
+    /// Encapsulates a unit test result
+    /// </summary>
+    public class TestResult
+    {
+        public object ResultData
+        {
+            get;
+            private set;
+        }
+
+        public string ContentType
+        {
+            get;
+            private set;
+        }
+
+        public string HttpStatusCode
+        {
+            get;
+            private set;
+        }
+
+        public TestResult(string resultData = "", string contentType = "", string statusCode = "")
+        {
+            this.IsException = false;
+            this.ResultData = resultData;
+            this.ContentType = contentType;
+            this.HttpStatusCode = statusCode;
+        }
+
+        public static TestResult FromByteReader(MgByteReader byteReader, string operation = "")
+        {
+            try
+            {
+                TestResult res = new TestResult();
+                if (byteReader != null)
+                {
+                    res.ContentType = byteReader.GetMimeType();
+                    if (res.ContentType == MgMimeType.Html ||
+                        res.ContentType == MgMimeType.Json ||
+                        res.ContentType == MgMimeType.Kml ||
+                        res.ContentType == MgMimeType.Text ||
+                        res.ContentType == MgMimeType.Xml)
+                    {
+                        res.ResultData = byteReader.ToString();
+                    }
+                    else
+                    {
+                        MgByteSink sink = new MgByteSink(byteReader);
+                        string path = operation + Guid.NewGuid().ToString() + "Result.bin";
+                        if (string.IsNullOrEmpty(operation))
+                            path = Path.GetTempFileName();
+                        sink.ToFile(path);
+                        res.ResultData = File.ReadAllBytes(path);
+                        if (string.IsNullOrEmpty(operation))
+                            File.Delete(path);
+                        else
+                            System.Diagnostics.Debug.WriteLine(string.Format("[MgTestRunner]: Check out {0} if binary comparison results are strange", path));
+                        /*
+                        byte[] bytes = new byte[byteReader.GetLength()];
+                        byteReader.Read(bytes, bytes.Length);
+                        res.ResultData = bytes;
+                        */
+                    }
+                }
+                return res;
+            }
+            catch (MgException ex)
+            {
+                return FromMgException(ex, null);
+            }
+        }
+
+        public bool IsException
+        {
+            get;
+            private set;
+        }
+
+        public string FullExceptionDetails
+        {
+            get;
+            private set;
+        }
+
+        private void AppendExceptionDetails(string data)
+        {
+            this.FullExceptionDetails += data;
+        }
+
+        public static TestResult FromMgException(MgException ex, NameValueCollection param)
+        {
+            //Need to be lowercase to satisfy a PHP-ism. Ugh!
+            var res = new TestResult(ex.GetType().Name.ToLower(), "text/plain");
+            res.IsException = true;
+            res.FullExceptionDetails = ex.ToString();
+            if (param != null)
+            {
+                var strp = new StringBuilder();
+                foreach (string key in param.Keys)
+                {
+                    strp.AppendLine($"{key} = {param[key]}");
+                }
+
+                res.AppendExceptionDetails($"\n\n{strp.ToString()}");
+            }
+            return res;
+        }
+
+        public static TestResult FromException(Exception ex)
+        {
+            var res = new TestResult(ex.Message, "text/plain");
+            res.IsException = true;
+            res.FullExceptionDetails = ex.ToString();
+            return res;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/UnitTestException.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/UnitTestException.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommon/UnitTestException.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Common
+{
+    public class UnitTestException : Exception
+    {
+        public UnitTestException() { }
+        public UnitTestException(string message) : base(message) { }
+        public UnitTestException(string message, Exception inner) : base(message, inner) { }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommonFull/Properties/AssemblyInfo.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommonFull/Properties/AssemblyInfo.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommonFull/Properties/AssemblyInfo.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -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("TestCommonFull")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TestCommonFull")]
+[assembly: AssemblyCopyright("Copyright ©  2018")]
+[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("eb8713c5-0284-47c5-950a-90e4eb326669")]
+
+// 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/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommonFull/TestCommonFull.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommonFull/TestCommonFull.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestCommonFull/TestCommonFull.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,40 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <Description>TestCommon Class Library (Full Framework)</Description>
+    <Authors>OSGeo</Authors>
+    <TargetFramework>net461</TargetFramework>
+    <AssemblyName>TestCommon</AssemblyName>
+    <PackageId>TestCommon</PackageId>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="..\TestCommon\ApiTypes.cs" Link="ApiTypes.cs" />
+    <Compile Include="..\TestCommon\Assert.cs" Link="Assert.cs" />
+    <Compile Include="..\TestCommon\CommonTests.cs" Link="CommonTests.cs" />
+    <Compile Include="..\TestCommon\CommonUtility.cs" Link="CommonUtility.cs" />
+    <Compile Include="..\TestCommon\ExternalTests\ByteReaderTest.cs" Link="ExternalTests\ByteReaderTest.cs" />
+    <Compile Include="..\TestCommon\ExternalTests\CollectionTests.cs" Link="ExternalTests\CollectionTests.cs" />
+    <Compile Include="..\TestCommon\ExternalTests\ConvenienceTests.cs" Link="ExternalTests\ConvenienceTests.cs" />
+    <Compile Include="..\TestCommon\ExternalTests\PropertiesTest.cs" Link="ExternalTests\PropertiesTest.cs" />
+    <Compile Include="..\TestCommon\FeatureService\FeatureServiceOperationExecutor.cs" Link="FeatureService\FeatureServiceOperationExecutor.cs" />
+    <Compile Include="..\TestCommon\FeatureService\Operations.cs" Link="FeatureService\Operations.cs" />
+    <Compile Include="..\TestCommon\ITestExecutor.cs" Link="ITestExecutor.cs" />
+    <Compile Include="..\TestCommon\ITestExecutorCollection.cs" Link="ITestExecutorCollection.cs" />
+    <Compile Include="..\TestCommon\ITestLogger.cs" Link="ITestLogger.cs" />
+    <Compile Include="..\TestCommon\MapLayer\MapLayerOperationExecutor.cs" Link="MapLayer\MapLayerOperationExecutor.cs" />
+    <Compile Include="..\TestCommon\MapLayer\Operations.cs" Link="MapLayer\Operations.cs" />
+    <Compile Include="..\TestCommon\PlatformApiTestExecutor.cs" Link="PlatformApiTestExecutor.cs" />
+    <Compile Include="..\TestCommon\PlatformResources.cs" Link="PlatformResources.cs" />
+    <Compile Include="..\TestCommon\ResourceService\Operations.cs" Link="ResourceService\Operations.cs" />
+    <Compile Include="..\TestCommon\ResourceService\ResourceServiceOperationExecutor.cs" Link="ResourceService\ResourceServiceOperationExecutor.cs" />
+    <Compile Include="..\TestCommon\Sqlite.cs" Link="Sqlite.cs" />
+    <Compile Include="..\TestCommon\TestDataRoot.cs" Link="TestDataRoot.cs" />
+    <Compile Include="..\TestCommon\TestExecutionRun.cs" Link="TestExecutionRun.cs" />
+    <Compile Include="..\TestCommon\TestExecutorBase.cs" Link="TestExecutorBase.cs" />
+    <Compile Include="..\TestCommon\TestResult.cs" Link="TestResult.cs" />
+    <Compile Include="..\TestCommon\UnitTestException.cs" Link="UnitTestException.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Data.SQLite" Version="2.0.1" />
+  </ItemGroup>
+</Project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/DrawingService/DrawingServiceOperationExecutor.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/DrawingService/DrawingServiceOperationExecutor.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/DrawingService/DrawingServiceOperationExecutor.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,23 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    /// <summary>
+    /// The base class of all test executors using the MgDrawingService
+    /// </summary>
+    /// <typeparam name="T">The test executor subclass type</typeparam>
+    public abstract class DrawingServiceOperationExecutor<T> : PlatformApiTestExecutor
+    {
+        protected MgDrawingService _drawingService;
+
+        protected DrawingServiceOperationExecutor(MgDrawingService drawSvc, string unitTestVm)
+            : base(typeof(T).Name.ToUpper(), ApiTypes.Platform, unitTestVm)
+        {
+            _drawingService = drawSvc;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/DrawingService/Operations.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/DrawingService/Operations.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/DrawingService/Operations.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,210 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    public class DescribeDrawing : DrawingServiceOperationExecutor<DescribeDrawing>
+    {
+        public DescribeDrawing(MgDrawingService drawSvc, string vm)
+            : base(drawSvc, vm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader reader = _drawingService.DescribeDrawing(resId);
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetDrawing : DrawingServiceOperationExecutor<GetDrawing>
+    {
+        public GetDrawing(MgDrawingService drawSvc, string vm)
+            : base(drawSvc, vm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader reader = _drawingService.GetDrawing(resId);
+                return TestResult.FromByteReader(reader, "GETDRAWING");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class EnumerateDrawingLayers : DrawingServiceOperationExecutor<EnumerateDrawingLayers>
+    {
+        public EnumerateDrawingLayers(MgDrawingService drawSvc, string vm)
+            : base(drawSvc, vm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "SECTION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgStringCollection coll = _drawingService.EnumerateLayers(resId, param["SECTION"] ?? "");
+                MgByteReader reader = coll?.ToXml();
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetDrawingLayer : DrawingServiceOperationExecutor<GetDrawingLayer>
+    {
+        public GetDrawingLayer(MgDrawingService drawSvc, string vm)
+            : base(drawSvc, vm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "SECTION", "LAYER" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader reader = _drawingService.GetLayer(resId, param["SECTION"], param["LAYER"]);
+                return TestResult.FromByteReader(reader, "GETDRAWINGLAYER");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetDrawingSection : DrawingServiceOperationExecutor<GetDrawingSection>
+    {
+        public GetDrawingSection(MgDrawingService drawSvc, string vm)
+            : base(drawSvc, vm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "SECTION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader reader = _drawingService.GetSection(resId, param["SECTION"]);
+                return TestResult.FromByteReader(reader, "GETDRAWINGSECTION");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class EnumerateDrawingSections : DrawingServiceOperationExecutor<EnumerateDrawingSections>
+    {
+        public EnumerateDrawingSections(MgDrawingService drawSvc, string vm)
+            : base(drawSvc, vm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader reader = _drawingService.EnumerateSections(resId);
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class EnumerateDrawingSectionResources : DrawingServiceOperationExecutor<EnumerateDrawingSectionResources>
+    {
+        public EnumerateDrawingSectionResources(MgDrawingService drawSvc, string vm)
+            : base(drawSvc, vm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "SECTION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader reader = _drawingService.EnumerateSectionResources(resId, param["SECTION"] ?? "");
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetDrawingSectionResource : DrawingServiceOperationExecutor<GetDrawingSectionResource>
+    {
+        public GetDrawingSectionResource(MgDrawingService drawSvc, string vm)
+            : base(drawSvc, vm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "RESOURCEID", "RESOURCENAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgResourceIdentifier resId = param.TryGetResourceId("RESOURCEID");
+
+                MgByteReader reader = _drawingService.GetSectionResource(resId, param["RESOURCENAME"]);
+                return TestResult.FromByteReader(reader, "GETDRAWINGSECTIONRESOURCE");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/CollectionTests.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/CollectionTests.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/CollectionTests.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,54 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Web.ExternalTests
+{
+    public class CollectionTestsMapPlotCollection : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            string coordsys = "GEOGCS[\"LL84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]";
+            MgEnvelope env = new MgEnvelope(10, 10, 20, 20);
+            MgMap map1 = (MgMap)factory.CreateMap(coordsys, env, "map1");
+            MgMap map2 = (MgMap)factory.CreateMap(coordsys, env, "map2");
+            MgMap map3 = (MgMap)factory.CreateMap(coordsys, env, "map3");
+
+            MgPlotSpecification spec = new MgPlotSpecification((float)8.0, (float)11.0, "in", (float)0.0, (float)0.0, (float)5.0, (float)5.0);
+            MgResourceIdentifier resId = new MgResourceIdentifier("Library://test.WebLayout");
+            MgLayout layout1 = new MgLayout(resId, "Title1", "in");
+            MgMapPlot plot1 = new MgMapPlot(map1, spec, layout1);
+
+            MgLayout layout2 = new MgLayout(resId, "Title2", "in");
+            MgMapPlot plot2 = new MgMapPlot(map2, spec, layout2);
+
+            MgLayout layout3 = new MgLayout(resId, "Title3", "in");
+            MgMapPlot plot3 = new MgMapPlot(map3, spec, layout3);
+
+            MgMapPlotCollection coll = new MgMapPlotCollection();
+            coll.Add(plot1);
+            coll.Add(plot2);
+            coll.Remove(plot1);
+
+            Assert.AreEqual(1, coll.Count);
+
+            coll.Insert(0, plot1);
+            coll.Insert(2, plot3);
+            MgMapPlot tmp = coll[2];
+            coll[2] = tmp;
+
+            Assert.AreEqual(3, coll.Count);
+
+
+            string txt = "";
+            foreach (MgMapPlot plot in coll)
+            {
+                txt += "[" + plot.GetLayout().GetTitle() + "]";
+            }
+            Assert.AreEqual("[Title1][Title2][Title3]", txt);
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/MapTests.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/MapTests.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/MapTests.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,146 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Web.ExternalTests
+{
+    public abstract class ExternalTestBase : IExternalTest
+    {
+        protected static void LoadResource(MgResourceService resSvc, string resIdStr, string path)
+        {
+            MgResourceIdentifier resId = new MgResourceIdentifier(resIdStr);
+            MgByteSource bs = new MgByteSource(path);
+            MgByteReader br = bs.GetReader();
+            resSvc.SetResource(resId, br, null);
+        }
+
+        protected static void LoadResourceData(MgResourceService resSvc, string resIdStr, string dataName, string dataType, string path)
+        {
+            MgResourceIdentifier resId = new MgResourceIdentifier(resIdStr);
+            MgByteSource bs = new MgByteSource(path);
+            MgByteReader br = bs.GetReader();
+            resSvc.SetResourceData(resId, dataName, dataType, br);
+        }
+
+        public abstract void Execute(IPlatformFactory factory, ITestLogger logger);
+    }
+
+    public class LinkedTileSetMapTest : ExternalTestBase
+    {
+        public override void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            try
+            {
+                MgResourceService resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+
+                var root = $"{TestDataRoot.Path}/TileService/";
+                LoadResource(resSvc, "Library://UnitTests/Data/RoadCenterLines.FeatureSource", root + "UT_RoadCenterLines.fs");
+                LoadResourceData(resSvc, "Library://UnitTests/Data/RoadCenterLines.FeatureSource", "UT_RoadCenterLines.sdf", MgResourceDataType.File, root + "UT_RoadCenterLines.sdf");
+                LoadResource(resSvc, "Library://UnitTests/Layers/RoadCenterLines.LayerDefinition", root + "UT_RoadCenterLines.ldf");
+
+                LoadResource(resSvc, "Library://UnitTests/Data/VotingDistricts.FeatureSource", root + "UT_VotingDistricts.fs");
+                LoadResourceData(resSvc, "Library://UnitTests/Data/VotingDistricts.FeatureSource", "UT_VotingDistricts.sdf", MgResourceDataType.File, root + "UT_VotingDistricts.sdf");
+                LoadResource(resSvc, "Library://UnitTests/Layers/VotingDistricts.LayerDefinition", root + "UT_VotingDistricts.ldf");
+
+                LoadResource(resSvc, "Library://UnitTests/Data/Parcels.FeatureSource", root + "UT_Parcels.fs");
+                LoadResourceData(resSvc, "Library://UnitTests/Data/Parcels.FeatureSource", "UT_Parcels.sdf", MgResourceDataType.File, root + "UT_Parcels.sdf");
+                LoadResource(resSvc, "Library://UnitTests/Layers/Parcels.LayerDefinition", root + "UT_Parcels.ldf");
+
+                string str = MapGuideResources.UT_XYZ;
+                byte[] tsd = Encoding.UTF8.GetBytes(str);
+                MgByteSource sourceTSD = new MgByteSource(tsd, tsd.Length);
+                MgByteReader contentTSD = sourceTSD.GetReader();
+                MgResourceIdentifier resTSD = new MgResourceIdentifier("Library://UnitTests/TileSets/Test.TileSetDefinition");
+
+                resSvc.SetResource(resTSD, contentTSD, null);
+
+                string mdf = MapGuideResources.UT_BaseMap;
+                mdf = string.Format(mdf, resTSD.ToString());
+                byte[] mdfBytes = Encoding.UTF8.GetBytes(mdf);
+                MgByteSource sourceMDF = new MgByteSource(mdfBytes, mdfBytes.Length);
+                MgByteReader contentMDF = sourceMDF.GetReader();
+                MgResourceIdentifier resMDF = new MgResourceIdentifier("Library://UnitTests/Maps/LinkedTileSet.MapDefinition");
+
+                resSvc.SetResource(resMDF, contentMDF, null);
+
+                //This should throw because making a MgMap from a Map Defintion that links to a XYZ tile set
+                //is not supported
+                MgMapBase map = factory.CreateMap(resMDF);
+
+                Assert.Fail("Expected MgUnsupportedTileProviderException to be thrown");
+            }
+            catch (MgUnsupportedTileProviderException)
+            {
+
+            }
+        }
+    }
+
+    public class TestTicket2538 : ExternalTestBase
+    {
+        public override void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            //For a map definition that links to a default provider tile set and both have the same coordinate
+            //system, it should be using the map definition's extents and we should not have null extents or view
+            //center
+
+            MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
+            string csWkt = csFactory.ConvertCoordinateSystemCodeToWkt("LL84");
+
+            MgResourceService resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+
+            var root = $"{TestDataRoot.Path}/TileService/";
+            LoadResource(resSvc, "Library://UnitTests/Data/RoadCenterLines.FeatureSource", root + "UT_RoadCenterLines.fs");
+            LoadResourceData(resSvc, "Library://UnitTests/Data/RoadCenterLines.FeatureSource", "UT_RoadCenterLines.sdf", MgResourceDataType.File, root + "UT_RoadCenterLines.sdf");
+            LoadResource(resSvc, "Library://UnitTests/Layers/RoadCenterLines.LayerDefinition", root + "UT_RoadCenterLines.ldf");
+
+            LoadResource(resSvc, "Library://UnitTests/Data/VotingDistricts.FeatureSource", root + "UT_VotingDistricts.fs");
+            LoadResourceData(resSvc, "Library://UnitTests/Data/VotingDistricts.FeatureSource", "UT_VotingDistricts.sdf", MgResourceDataType.File, root + "UT_VotingDistricts.sdf");
+            LoadResource(resSvc, "Library://UnitTests/Layers/VotingDistricts.LayerDefinition", root + "UT_VotingDistricts.ldf");
+
+            LoadResource(resSvc, "Library://UnitTests/Data/Parcels.FeatureSource", root + "UT_Parcels.fs");
+            LoadResourceData(resSvc, "Library://UnitTests/Data/Parcels.FeatureSource", "UT_Parcels.sdf", MgResourceDataType.File, root + "UT_Parcels.sdf");
+            LoadResource(resSvc, "Library://UnitTests/Layers/Parcels.LayerDefinition", root + "UT_Parcels.ldf");
+
+            string tsd = MapGuideResources.UT_BaseMap_TSD;
+            tsd = string.Format(tsd, csWkt, -87.5, 43.5, -86.5, 44.5);
+            byte[] tsdBytes = Encoding.UTF8.GetBytes(tsd);
+            MgByteSource sourceTSD = new MgByteSource(tsdBytes, tsdBytes.Length);
+            MgByteReader contentTSD = sourceTSD.GetReader();
+            MgResourceIdentifier resTSD = new MgResourceIdentifier("Library://UnitTests/TileSets/Test.TileSetDefinition");
+
+            resSvc.SetResource(resTSD, contentTSD, null);
+
+            string mdf = MapGuideResources.UT_LinkedTileSet;
+            mdf = string.Format(mdf, csWkt, -87.0, 43.0, -86.0, 44.0, resTSD.ToString());
+            byte[] mdfBytes = Encoding.UTF8.GetBytes(mdf);
+            MgByteSource sourceMDF = new MgByteSource(mdfBytes, mdfBytes.Length);
+            MgByteReader contentMDF = sourceMDF.GetReader();
+            MgResourceIdentifier resMDF = new MgResourceIdentifier("Library://UnitTests/Maps/LinkedTileSet.MapDefinition");
+
+            resSvc.SetResource(resMDF, contentMDF, null);
+
+            MgMapBase map = factory.CreateMap(resMDF);
+            MgEnvelope extent = map.GetMapExtent();
+            Assert.IsNotNull(extent);
+            Assert.IsNotNull(map.MapExtent);
+            MgPoint center = map.GetViewCenter();
+            Assert.IsNotNull(center);
+            Assert.IsNotNull(map.ViewCenter);
+
+            MgCoordinate ll = extent.GetLowerLeftCoordinate();
+            MgCoordinate ur = extent.GetUpperRightCoordinate();
+
+            Assert.IsNotNull(ll);
+            Assert.IsNotNull(ur);
+
+            Assert.AreEqual(-87.0, ll.X);
+            Assert.AreEqual(43.0, ll.Y);
+            Assert.AreEqual(-86.0, ur.X);
+            Assert.AreEqual(44.0, ur.Y);
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/RenderingServiceTests.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/RenderingServiceTests.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/RenderingServiceTests.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,130 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+#if HAVE_SYSTEM_DRAWING
+using System.Drawing;
+using System.Drawing.Imaging;
+#endif
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Web.ExternalTests
+{
+    public class RenderTileTest : IExternalTest
+    {
+        private static void LoadResource(MgResourceService resSvc, string resIdStr, string path)
+        {
+            MgResourceIdentifier resId = new MgResourceIdentifier(resIdStr);
+            MgByteSource bs = new MgByteSource(path);
+            MgByteReader br = bs.GetReader();
+            resSvc.SetResource(resId, br, null);
+        }
+
+        private static void LoadResourceData(MgResourceService resSvc, string resIdStr, string dataName, string dataType, string path)
+        {
+            MgResourceIdentifier resId = new MgResourceIdentifier(resIdStr);
+            MgByteSource bs = new MgByteSource(path);
+            MgByteReader br = bs.GetReader();
+            resSvc.SetResourceData(resId, dataName, dataType, br);
+        }
+
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var renderSvc = (MgRenderingService)factory.CreateService(MgServiceType.RenderingService);
+            var root = $"{TestDataRoot.Path}/TileService/";
+
+            LoadResource(resSvc, "Library://UnitTests/Data/RoadCenterLines.FeatureSource", root + "UT_RoadCenterLines.fs");
+            LoadResourceData(resSvc, "Library://UnitTests/Data/RoadCenterLines.FeatureSource", "UT_RoadCenterLines.sdf", MgResourceDataType.File, root + "UT_RoadCenterLines.sdf");
+            LoadResource(resSvc, "Library://UnitTests/Layers/RoadCenterLines.LayerDefinition", root + "UT_RoadCenterLines.ldf");
+
+            LoadResource(resSvc, "Library://UnitTests/Data/VotingDistricts.FeatureSource", root + "UT_VotingDistricts.fs");
+            LoadResourceData(resSvc, "Library://UnitTests/Data/VotingDistricts.FeatureSource", "UT_VotingDistricts.sdf", MgResourceDataType.File, root + "UT_VotingDistricts.sdf");
+            LoadResource(resSvc, "Library://UnitTests/Layers/VotingDistricts.LayerDefinition", root + "UT_VotingDistricts.ldf");
+
+            LoadResource(resSvc, "Library://UnitTests/Data/Parcels.FeatureSource", root + "UT_Parcels.fs");
+            LoadResourceData(resSvc, "Library://UnitTests/Data/Parcels.FeatureSource", "UT_Parcels.sdf", MgResourceDataType.File, root + "UT_Parcels.sdf");
+            LoadResource(resSvc, "Library://UnitTests/Layers/Parcels.LayerDefinition", root + "UT_Parcels.ldf");
+
+            LoadResource(resSvc, "Library://UnitTests/Maps/BaseMap.MapDefinition", root + "UT_BaseMap.mdf");
+
+            var map = (MgMap)factory.CreateMap(new MgResourceIdentifier("Library://UnitTests/Maps/BaseMap.MapDefinition"));
+            var img = renderSvc.RenderTile(map, "BaseLayers", 0, 0);
+            CheckValidImage(img);
+            img = renderSvc.RenderTile(map, "BaseLayers", 0, 0, 256, 256, 96, MgImageFormats.Png);
+            CheckValidImage(img, 256, 256, MgImageFormats.Png);
+            img = renderSvc.RenderTile(map, "BaseLayers", 0, 0, 256, 256, 96, MgImageFormats.Gif);
+            CheckValidImage(img, 256, 256, MgImageFormats.Gif);
+            img = renderSvc.RenderTile(map, "BaseLayers", 0, 0, 256, 256, 96, MgImageFormats.Jpeg);
+            CheckValidImage(img, 256, 256, MgImageFormats.Jpeg);
+            img = renderSvc.RenderTile(map, "BaseLayers", 0, 0, 256, 256, 96, MgImageFormats.Png8);
+            CheckValidImage(img, 256, 256, MgImageFormats.Png8);
+            img = renderSvc.RenderTileXYZ(map, "BaseLayers", 2099, 2985, 13);
+            CheckValidImage(img, 256, 256);
+            img = renderSvc.RenderTileXYZ(map, "BaseLayers", 2099, 2985, 13, 96, MgImageFormats.Png);
+            CheckValidImage(img, 256, 256, MgImageFormats.Png);
+            img = renderSvc.RenderTileXYZ(map, "BaseLayers", 2099, 2985, 13, 96, MgImageFormats.Png8);
+            CheckValidImage(img, 256, 256, MgImageFormats.Png8);
+            img = renderSvc.RenderTileXYZ(map, "BaseLayers", 2099, 2985, 13, 96, MgImageFormats.Gif);
+            CheckValidImage(img, 256, 256, MgImageFormats.Gif);
+            img = renderSvc.RenderTileXYZ(map, "BaseLayers", 2099, 2985, 13, 96, MgImageFormats.Jpeg);
+            CheckValidImage(img, 256, 256, MgImageFormats.Jpeg);
+        }
+
+        private void CheckValidImage(MgByteReader img, int? imageWidth = null, int? imageHeight = null, string format = null)
+        {
+            try
+            {
+                MgByteSink sink = new MgByteSink(img);
+                sink.ToFile("tile.img");
+#if HAVE_SYSTEM_DRAWING
+                using (Image image = Image.FromFile("tile.img"))
+                {
+                    Assert.IsNotNull(image);
+                    if (imageWidth != null)
+                        Assert.Equals(image.Width, imageWidth.Value);
+                    if (imageHeight != null)
+                        Assert.Equals(image.Height, imageHeight.Value);
+
+                    if (format != null)
+                    {
+                        switch(format)
+                        {
+                            case MgImageFormats.Gif:
+                                Assert.Equals(image.RawFormat, ImageFormat.Gif);
+                                break;
+                            case MgImageFormats.Jpeg:
+                                Assert.Equals(image.RawFormat, ImageFormat.Jpeg);
+                                break;
+                            case MgImageFormats.Png:
+                            case MgImageFormats.Png8:
+                                Assert.Equals(image.RawFormat, ImageFormat.Png);
+                                break;
+                            case MgImageFormats.Tiff:
+                                Assert.Equals(image.RawFormat, ImageFormat.Tiff);
+                                break;
+                        }
+                    }
+                }
+#endif
+            }
+            catch (Exception ex)
+            {
+                throw new AssertException("Invalid image found: " + ex.Message);
+            }
+            finally
+            {
+                if (File.Exists("tile.img"))
+                {
+                    try
+                    {
+                        File.Delete("tile.img");
+                    }
+                    catch { }
+                }
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/ResourceServiceTests.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/ResourceServiceTests.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ExternalTests/ResourceServiceTests.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,66 @@
+using OSGeo.MapGuide.Test.Common;
+using OSGeo.MapGuide.Test.Common.ExternalTests;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Web.ExternalTests
+{
+    public class ResourceMetadataTest : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            var fsId = new MgResourceIdentifier("Library://UnitTests/Data/Metadata.FeatureSource");
+            try
+            {
+                if (resSvc.ResourceExists(fsId))
+                    resSvc.DeleteResource(fsId);
+
+                string metadata = resSvc.EnumerateResourceDocuments(null, MgResourceType.FeatureSource, MgResourceHeaderProperties.Metadata);
+                File.WriteAllText("metadata.txt", metadata);
+
+                string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SDF", featSvc);
+                string headerXml = string.Format(MapGuideResources.ResourceHeaderTemplate, "Foo", "Bar");
+                var bytes = Encoding.UTF8.GetBytes(headerXml);
+                var source = new MgByteSource(bytes, bytes.Length);
+                var rdr = source.GetReader();
+
+                resSvc.SetResource(fsId, null, rdr);
+                string metadata2 = resSvc.EnumerateResourceDocuments(null, MgResourceType.FeatureSource, MgResourceHeaderProperties.Metadata);
+                File.WriteAllText("metadata2.txt", metadata2);
+
+                Assert.IsFalse(metadata == metadata2);
+            }
+            finally
+            {
+                if (resSvc.ResourceExists(fsId))
+                    resSvc.DeleteResource(fsId);
+            }
+        }
+    }
+
+    public class ResourceModifiedTest : IExternalTest
+    {
+        public void Execute(IPlatformFactory factory, ITestLogger logger)
+        {
+            var resSvc = (MgResourceService)factory.CreateService(MgServiceType.ResourceService);
+            var featSvc = (MgFeatureService)factory.CreateService(MgServiceType.FeatureService);
+            MgResourceIdentifier fsId = new MgResourceIdentifier("Library://UnitTests/Data/Timestamp.FeatureSource");
+            string className = FeatureServiceTestUtil.CreateTestDataStore(fsId, "OSGeo.SDF", featSvc);
+            MgByteReader content = resSvc.GetResourceContent(fsId);
+            MgDateTime mod1 = resSvc.GetResourceModifiedDate(fsId);
+            //Thread.Sleep(1500);
+            Task.Delay(1500).Wait();
+            resSvc.SetResource(fsId, content, null);
+            MgDateTime mod2 = resSvc.GetResourceModifiedDate(fsId);
+
+            Assert.IsFalse(mod1.ToString() == mod2.ToString());
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MapGuideResources.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MapGuideResources.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MapGuideResources.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,161 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test
+{
+    public static class MapGuideResources
+    {
+        public const string ResourceHeaderTemplate = @"<?xml version=""1.0""?>
+<ResourceDocumentHeader xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xsi:noNamespaceSchemaLocation=""ResourceDocumentHeader-1.0.0.xsd"">
+  <Security>
+    <Inherited>true</Inherited>
+  </Security>
+  <Metadata>
+    <Simple>
+      <Property>
+        <Name>{0}</Name>
+        <Value>{1}</Value>
+      </Property>
+    </Simple>
+  </Metadata>
+</ResourceDocumentHeader>";
+
+        public const string UT_BaseMap = @"<?xml version=""1.0"" encoding=""UTF-8""?>
+<MapDefinition xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:noNamespaceSchemaLocation=""MapDefinition-3.0.0.xsd"" version=""3.0.0"">
+  <Name>Base Map</Name>
+  <CoordinateSystem>GEOGCS[" LL84",DATUM["WGS 84",SPHEROID["WGS 84",6378137,298.25722293287],TOWGS84[0, 0, 0, 0, 0, 0, 0]],PRIMEM["Greenwich",0],UNIT["Degrees",0.01745329252]]</CoordinateSystem>
+  <Extents>
+    <MinX>-87.79786601383196</MinX>
+    <MaxX>-87.66452777186925</MaxX>
+    <MinY>43.6868578621819</MinY>
+    <MaxY>43.8037962206133</MaxY>
+  </Extents>
+  <BackgroundColor>FFF7E1D2</BackgroundColor>
+  <TileSetSource>
+    <ResourceId>{0}</ResourceId>
+  </TileSetSource>
+</MapDefinition>";
+
+        public const string UT_BaseMap_TSD = @"<?xml version=""1.0"" encoding=""UTF-8""?>
+<TileSetDefinition xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:noNamespaceSchemaLocation=""TileSetDefinition-3.0.0.xsd"">
+  <TileStoreParameters>
+    <TileProvider>Default</TileProvider>
+    <Parameter>
+      <Name>TilePath</Name>
+      <Value>%MG_TILE_CACHE_PATH%</Value>
+    </Parameter>
+    <Parameter>
+      <Name>TileWidth</Name>
+      <Value>256</Value>
+    </Parameter>
+    <Parameter>
+      <Name>TileHeight</Name>
+      <Value>256</Value>
+    </Parameter>
+    <Parameter>
+      <Name>TileFormat</Name>
+      <Value>PNG</Value>
+    </Parameter>
+    <Parameter>
+      <Name>FiniteScaleList</Name>
+      <Value>200000,100000,50000,25000,12500,6250,3125,1562.5,781.25,390.625</Value>
+    </Parameter>
+    <Parameter>
+      <Name>CoordinateSystem</Name>
+      <Value>{0}</Value>
+    </Parameter>
+  </TileStoreParameters>
+  <Extents>
+    <MinX>{1}</MinX>
+    <MaxX>{3}</MaxX>
+    <MinY>{2}</MinY>
+    <MaxY>{4}</MaxY>
+  </Extents>
+  <BaseMapLayerGroup>
+    <Name>BaseLayers</Name>
+    <Visible>true</Visible>
+    <ShowInLegend>true</ShowInLegend>
+    <ExpandInLegend>true</ExpandInLegend>
+    <LegendLabel>Base Layers</LegendLabel>
+    <BaseMapLayer>
+      <Name>Parcels</Name>
+      <ResourceId>Library://UnitTests/Layers/Parcels.LayerDefinition</ResourceId>
+      <Selectable>true</Selectable>
+      <ShowInLegend>true</ShowInLegend>
+      <LegendLabel>Parcels</LegendLabel>
+      <ExpandInLegend>false</ExpandInLegend>
+    </BaseMapLayer>
+    <BaseMapLayer>
+      <Name>VotingDistricts</Name>
+      <ResourceId>Library://UnitTests/Layers/VotingDistricts.LayerDefinition</ResourceId>
+      <Selectable>true</Selectable>
+      <ShowInLegend>true</ShowInLegend>
+      <LegendLabel>Voting Districts</LegendLabel>
+      <ExpandInLegend>false</ExpandInLegend>
+    </BaseMapLayer>
+  </BaseMapLayerGroup>
+</TileSetDefinition>";
+
+        public const string UT_LinkedTileSet = @"<?xml version=""1.0"" encoding=""UTF-8""?>
+<MapDefinition xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:noNamespaceSchemaLocation=""MapDefinition-3.0.0.xsd"" version=""3.0.0"">
+  <Name>Base Map linked to Tile Set</Name>
+  <CoordinateSystem>{0}</CoordinateSystem>
+  <Extents>
+    <MinX>{1}</MinX>
+    <MaxX>{3}</MaxX>
+    <MinY>{2}</MinY>
+    <MaxY>{4}</MaxY>
+  </Extents>
+  <BackgroundColor>FFF7E1D2</BackgroundColor>
+  <TileSetSource>
+    <ResourceId>{5}</ResourceId>
+  </TileSetSource>
+</MapDefinition>";
+
+        public const string UT_XYZ = @"<?xml version=""1.0"" encoding=""UTF-8""?>
+<TileSetDefinition xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:noNamespaceSchemaLocation=""TileSetDefinition-3.0.0.xsd"">
+  <TileStoreParameters>
+    <TileProvider>XYZ</TileProvider>
+    <Parameter>
+      <Name>TilePath</Name>
+      <Value>%MG_TILE_CACHE_PATH%</Value>
+    </Parameter>
+    <Parameter>
+      <Name>TileFormat</Name>
+      <Value>PNG</Value>
+    </Parameter>
+  </TileStoreParameters>
+  <Extents>
+    <MinX>-87.79786601383196</MinX>
+    <MaxX>-87.66452777186925</MaxX>
+    <MinY>43.6868578621819</MinY>
+    <MaxY>43.8037962206133</MaxY>
+  </Extents>
+  <BaseMapLayerGroup>
+    <Name>BaseLayers</Name>
+    <Visible>true</Visible>
+    <ShowInLegend>true</ShowInLegend>
+    <ExpandInLegend>true</ExpandInLegend>
+    <LegendLabel>Base Layers</LegendLabel>
+    <BaseMapLayer>
+      <Name>Parcels</Name>
+      <ResourceId>Library://UnitTests/Layers/Parcels.LayerDefinition</ResourceId>
+      <Selectable>true</Selectable>
+      <ShowInLegend>true</ShowInLegend>
+      <LegendLabel>Parcels</LegendLabel>
+      <ExpandInLegend>false</ExpandInLegend>
+    </BaseMapLayer>
+    <BaseMapLayer>
+      <Name>VotingDistricts</Name>
+      <ResourceId>Library://UnitTests/Layers/VotingDistricts.LayerDefinition</ResourceId>
+      <Selectable>true</Selectable>
+      <ShowInLegend>true</ShowInLegend>
+      <LegendLabel>Voting Districts</LegendLabel>
+      <ExpandInLegend>false</ExpandInLegend>
+    </BaseMapLayer>
+  </BaseMapLayerGroup>
+</TileSetDefinition>";
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MapGuideTestExecutorCollection.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MapGuideTestExecutorCollection.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MapGuideTestExecutorCollection.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,497 @@
+using OSGeo.MapGuide;
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test
+{
+    public class MapGuideTestExecutorCollection : TestExecutorCollectionBase
+    {
+        private Dictionary<string, ITestExecutor> _executors;
+
+        private MgUserInformation _userInfo;
+        private MgSiteConnection _conn;
+
+        public MapGuideTestExecutorCollection(MgUserInformation userInfo, MgSiteConnection conn)
+        {
+            _userInfo = userInfo;
+            _conn = conn;
+        }
+
+        protected override void SetupExecutors(string dbPath)
+        {
+            //NOTE: We can't share the SqliteVm instance among our executor objects as this messes up query results
+            //we must be able to re-create a new SqliteVm for each executor, so we pass down the db path
+
+            _executors = new Dictionary<string, ITestExecutor>();
+
+            MgResourceService resSvc = (MgResourceService)_conn.CreateService(MgServiceType.ResourceService);
+            MgFeatureService featSvc = (MgFeatureService)_conn.CreateService(MgServiceType.FeatureService);
+            MgDrawingService drawSvc = (MgDrawingService)_conn.CreateService(MgServiceType.DrawingService);
+
+            var site = _conn.GetSite();
+            var admin = new MgServerAdmin();
+            admin.Open(_userInfo);
+            var wlCreator = new MgWebLayoutCreator(resSvc);
+            var creator = new MgMapCreator(_conn, resSvc);
+            var sessionCreator = new MgSessionCreator(_conn);
+            var sessionApply = new MgApplySession(_userInfo);
+            var session = new MgSession();
+
+            //Resource Service
+            _executors[typeof(Operations.ApplyResourcePackage).Name.ToUpper()] = new Operations.ApplyResourcePackage(resSvc, dbPath);
+            _executors[typeof(Operations.ChangeResourceOwner).Name.ToUpper()] = new Operations.ChangeResourceOwner(resSvc, dbPath);
+            _executors[typeof(Operations.CopyResource).Name.ToUpper()] = new Operations.CopyResource(resSvc, dbPath);
+            _executors[typeof(Operations.DeleteResource).Name.ToUpper()] = new Operations.DeleteResource(resSvc, dbPath);
+            _executors[typeof(Operations.DeleteResourceData).Name.ToUpper()] = new Operations.DeleteResourceData(resSvc, dbPath);
+            _executors[typeof(Operations.EnumerateResourceData).Name.ToUpper()] = new Operations.EnumerateResourceData(resSvc, dbPath);
+            _executors[typeof(Operations.EnumerateResourceReferences).Name.ToUpper()] = new Operations.EnumerateResourceReferences(resSvc, dbPath);
+            _executors[typeof(Operations.EnumerateResources).Name.ToUpper()] = new Operations.EnumerateResources(resSvc, dbPath);
+            _executors[typeof(Operations.GetRepositoryContent).Name.ToUpper()] = new Operations.GetRepositoryContent(resSvc, dbPath);
+            _executors[typeof(Operations.GetRepositoryHeader).Name.ToUpper()] = new Operations.GetRepositoryHeader(resSvc, dbPath);
+            _executors[typeof(Operations.GetResourceContent).Name.ToUpper()] = new Operations.GetResourceContent(resSvc, dbPath);
+            _executors[typeof(Operations.GetResourceData).Name.ToUpper()] = new Operations.GetResourceData(resSvc, dbPath);
+            _executors[typeof(Operations.GetResourceHeader).Name.ToUpper()] = new Operations.GetResourceHeader(resSvc, dbPath);
+            _executors[typeof(Operations.InheritPermissionsFrom).Name.ToUpper()] = new Operations.InheritPermissionsFrom(resSvc, dbPath);
+            _executors[typeof(Operations.MoveResource).Name.ToUpper()] = new Operations.MoveResource(resSvc, dbPath);
+            _executors[typeof(Operations.RenameResourceData).Name.ToUpper()] = new Operations.RenameResourceData(resSvc, dbPath);
+            _executors[typeof(Operations.SetResource).Name.ToUpper()] = new Operations.SetResource(resSvc, dbPath);
+            _executors[typeof(Operations.SetResourceData).Name.ToUpper()] = new Operations.SetResourceData(resSvc, dbPath);
+            _executors[typeof(Operations.UpdateRepository).Name.ToUpper()] = new Operations.UpdateRepository(resSvc, dbPath);
+
+            //Feature Service
+            _executors[typeof(Operations.DescribeFeatureSchema).Name.ToUpper()] = new Operations.DescribeFeatureSchema(featSvc, dbPath);
+            _executors[typeof(Operations.ExecuteSqlQuery).Name.ToUpper()] = new Operations.ExecuteSqlQuery(featSvc, dbPath);
+            _executors[typeof(Operations.GetClasses).Name.ToUpper()] = new Operations.GetClasses(featSvc, dbPath);
+            _executors[typeof(Operations.GetConnectionPropertyValues).Name.ToUpper()] = new Operations.GetConnectionPropertyValues(featSvc, dbPath);
+            _executors[typeof(Operations.GetFeatureProviders).Name.ToUpper()] = new Operations.GetFeatureProviders(featSvc, dbPath);
+            _executors[typeof(Operations.GetLongTransactions).Name.ToUpper()] = new Operations.GetLongTransactions(featSvc, dbPath);
+            _executors[typeof(Operations.GetProviderCapabilities).Name.ToUpper()] = new Operations.GetProviderCapabilities(featSvc, dbPath);
+            _executors[typeof(Operations.GetSchemas).Name.ToUpper()] = new Operations.GetSchemas(featSvc, dbPath);
+            _executors[typeof(Operations.GetSpatialContexts).Name.ToUpper()] = new Operations.GetSpatialContexts(featSvc, dbPath);
+            _executors[typeof(Operations.SelectAggregates).Name.ToUpper()] = new Operations.SelectAggregates(featSvc, dbPath);
+            _executors[typeof(Operations.SelectFeatures).Name.ToUpper()] = new Operations.SelectFeatures(featSvc, dbPath);
+            _executors[typeof(Operations.SetLongTransaction).Name.ToUpper()] = new Operations.SetLongTransaction(featSvc, dbPath, sessionCreator, sessionApply);
+            _executors[typeof(Operations.TestConnection).Name.ToUpper()] = new Operations.TestConnection(featSvc, dbPath);
+
+            //Map and Layers
+            _executors[typeof(Operations.AddLayerGroup).Name.ToUpper()] = new Operations.AddLayerGroup(resSvc, dbPath, creator);
+            _executors[typeof(Operations.AddLayer).Name.ToUpper()] = new Operations.AddLayer(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetCoordinateSystem).Name.ToUpper()] = new Operations.GetCoordinateSystem(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetDataExtent).Name.ToUpper()] = new Operations.GetDataExtent(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetDisplayInLegend).Name.ToUpper()] = new Operations.GetDisplayInLegend(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetLayerFeatureClass).Name.ToUpper()] = new Operations.GetLayerFeatureClass(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetLayerFeatureSource).Name.ToUpper()] = new Operations.GetLayerFeatureSource(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetLayerDefinition).Name.ToUpper()] = new Operations.GetLayerDefinition(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetGroups).Name.ToUpper()] = new Operations.GetGroups(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetLayers).Name.ToUpper()] = new Operations.GetLayers(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetLayerVisibility).Name.ToUpper()] = new Operations.GetLayerVisibility(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetLegendLabel).Name.ToUpper()] = new Operations.GetLegendLabel(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetMapExtent).Name.ToUpper()] = new Operations.GetMapExtent(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetMapName).Name.ToUpper()] = new Operations.GetMapName(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetViewCenter).Name.ToUpper()] = new Operations.GetViewCenter(resSvc, dbPath, creator);
+            _executors[typeof(Operations.GetViewScale).Name.ToUpper()] = new Operations.GetViewScale(resSvc, dbPath, creator);
+            _executors[typeof(Operations.HideGroup).Name.ToUpper()] = new Operations.HideGroup(resSvc, dbPath, creator);
+            _executors[typeof(Operations.IsLayerVisible).Name.ToUpper()] = new Operations.IsLayerVisible(resSvc, dbPath, creator);
+            _executors[typeof(Operations.LayerExists).Name.ToUpper()] = new Operations.LayerExists(resSvc, dbPath, creator);
+            _executors[typeof(Operations.RemoveGroup).Name.ToUpper()] = new Operations.RemoveGroup(resSvc, dbPath, creator);
+            _executors[typeof(Operations.ShowGroup).Name.ToUpper()] = new Operations.ShowGroup(resSvc, dbPath, creator);
+
+            //Drawing Service
+            _executors[typeof(Operations.DescribeDrawing).Name.ToUpper()] = new Operations.DescribeDrawing(drawSvc, dbPath);
+            _executors[typeof(Operations.GetDrawing).Name.ToUpper()] = new Operations.GetDrawing(drawSvc, dbPath);
+            _executors[typeof(Operations.EnumerateDrawingLayers).Name.ToUpper()] = new Operations.EnumerateDrawingLayers(drawSvc, dbPath);
+            _executors[typeof(Operations.GetDrawingLayer).Name.ToUpper()] = new Operations.GetDrawingLayer(drawSvc, dbPath);
+            _executors[typeof(Operations.GetDrawingSection).Name.ToUpper()] = new Operations.GetDrawingSection(drawSvc, dbPath);
+            _executors[typeof(Operations.EnumerateDrawingSections).Name.ToUpper()] = new Operations.EnumerateDrawingSections(drawSvc, dbPath);
+            _executors[typeof(Operations.EnumerateDrawingSectionResources).Name.ToUpper()] = new Operations.EnumerateDrawingSectionResources(drawSvc, dbPath);
+            _executors[typeof(Operations.GetDrawingSectionResource).Name.ToUpper()] = new Operations.GetDrawingSectionResource(drawSvc, dbPath);
+
+            //Mapping Service
+
+            //Rendering Service
+
+            //Server Admin
+            _executors[typeof(Operations.Offline).Name.ToUpper()] = new Operations.Offline(admin, dbPath);
+            _executors[typeof(Operations.Online).Name.ToUpper()] = new Operations.Online(admin, dbPath);
+            _executors[typeof(Operations.GetLog).Name.ToUpper()] = new Operations.GetLog(admin, dbPath);
+            _executors[typeof(Operations.GetLogByDate).Name.ToUpper()] = new Operations.GetLogByDate(admin, dbPath);
+            _executors[typeof(Operations.ClearLog).Name.ToUpper()] = new Operations.ClearLog(admin, dbPath);
+            _executors[typeof(Operations.DeleteLog).Name.ToUpper()] = new Operations.DeleteLog(admin, dbPath);
+            _executors[typeof(Operations.RenameLog).Name.ToUpper()] = new Operations.RenameLog(admin, dbPath);
+            _executors[typeof(Operations.EnumeratePackages).Name.ToUpper()] = new Operations.EnumeratePackages(admin, dbPath);
+            _executors[typeof(Operations.DeletePackage).Name.ToUpper()] = new Operations.DeletePackage(admin, dbPath);
+            _executors[typeof(Operations.LoadPackage).Name.ToUpper()] = new Operations.LoadPackage(admin, dbPath);
+            _executors[typeof(Operations.GetPackageStatus).Name.ToUpper()] = new Operations.GetPackageStatus(admin, dbPath);
+            _executors[typeof(Operations.GetPackageLog).Name.ToUpper()] = new Operations.GetPackageLog(admin, dbPath);
+
+            //Site Service
+            _executors[typeof(Operations.CreateSession).Name.ToUpper()] = new Operations.CreateSession(site, dbPath, session);
+            _executors[typeof(Operations.DestroySession).Name.ToUpper()] = new Operations.DestroySession(site, dbPath);
+            _executors[typeof(Operations.GetUserForSession).Name.ToUpper()] = new Operations.GetUserForSession(site, dbPath, session);
+            _executors[typeof(Operations.EnumerateUsers).Name.ToUpper()] = new Operations.EnumerateUsers(site, dbPath);
+            _executors[typeof(Operations.AddUser).Name.ToUpper()] = new Operations.AddUser(site, dbPath);
+            _executors[typeof(Operations.UpdateUser).Name.ToUpper()] = new Operations.UpdateUser(site, dbPath);
+            _executors[typeof(Operations.DeleteUsers).Name.ToUpper()] = new Operations.DeleteUsers(site, dbPath);
+            _executors[typeof(Operations.GrantRoleMembershipsToUsers).Name.ToUpper()] = new Operations.GrantRoleMembershipsToUsers(site, dbPath);
+            _executors[typeof(Operations.RevokeRoleMembershipsFromUsers).Name.ToUpper()] = new Operations.RevokeRoleMembershipsFromUsers(site, dbPath);
+            _executors[typeof(Operations.GrantGroupMembershipsToUsers).Name.ToUpper()] = new Operations.GrantGroupMembershipsToUsers(site, dbPath);
+            _executors[typeof(Operations.RevokeGroupMembershipsFromUsers).Name.ToUpper()] = new Operations.RevokeGroupMembershipsFromUsers(site, dbPath);
+            _executors[typeof(Operations.EnumerateGroups).Name.ToUpper()] = new Operations.EnumerateGroups(site, dbPath);
+            _executors[typeof(Operations.EnumerateGroups2).Name.ToUpper()] = new Operations.EnumerateGroups2(site, dbPath);
+            _executors[typeof(Operations.EnumerateRoles2).Name.ToUpper()] = new Operations.EnumerateRoles2(site, dbPath);
+            _executors[typeof(Operations.AddGroup).Name.ToUpper()] = new Operations.AddGroup(site, dbPath);
+            _executors[typeof(Operations.UpdateGroup).Name.ToUpper()] = new Operations.UpdateGroup(site, dbPath);
+            _executors[typeof(Operations.DeleteGroups).Name.ToUpper()] = new Operations.DeleteGroups(site, dbPath);
+            _executors[typeof(Operations.GrantRoleMembershipsToGroups).Name.ToUpper()] = new Operations.GrantRoleMembershipsToGroups(site, dbPath);
+            _executors[typeof(Operations.RevokeRoleMembershipsFromGroups).Name.ToUpper()] = new Operations.RevokeRoleMembershipsFromGroups(site, dbPath);
+            _executors[typeof(Operations.EnumerateRoles).Name.ToUpper()] = new Operations.EnumerateRoles(site, dbPath);
+            _executors[typeof(Operations.EnumerateServers).Name.ToUpper()] = new Operations.EnumerateServers(site, dbPath);
+            _executors[typeof(Operations.AddServer).Name.ToUpper()] = new Operations.AddServer(site, dbPath);
+            _executors[typeof(Operations.UpdateServer).Name.ToUpper()] = new Operations.UpdateServer(site, dbPath);
+            _executors[typeof(Operations.RemoveServer).Name.ToUpper()] = new Operations.RemoveServer(site, dbPath);
+
+            //Web Layout
+            _executors[typeof(Operations.WL_GetTitle).Name.ToUpper()] = new Operations.WL_GetTitle(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_GetMapDefinition).Name.ToUpper()] = new Operations.WL_GetMapDefinition(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_GetScale).Name.ToUpper()] = new Operations.WL_GetScale(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_GetCenter).Name.ToUpper()] = new Operations.WL_GetCenter(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_ShowToolbar).Name.ToUpper()] = new Operations.WL_ShowToolbar(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_ShowStatusbar).Name.ToUpper()] = new Operations.WL_ShowStatusbar(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_ShowTaskpane).Name.ToUpper()] = new Operations.WL_ShowTaskpane(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_ShowTaskbar).Name.ToUpper()] = new Operations.WL_ShowTaskbar(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_ShowLegend).Name.ToUpper()] = new Operations.WL_ShowLegend(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_ShowProperties).Name.ToUpper()] = new Operations.WL_ShowProperties(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_GetTaskPaneWidth).Name.ToUpper()] = new Operations.WL_GetTaskPaneWidth(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_GetInformationPaneWidth).Name.ToUpper()] = new Operations.WL_GetInformationPaneWidth(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_GetInitialTaskUrl).Name.ToUpper()] = new Operations.WL_GetInitialTaskUrl(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_ShowContextMenu).Name.ToUpper()] = new Operations.WL_ShowContextMenu(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_TestUiItem).Name.ToUpper()] = new Operations.WL_TestUiItem(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_HomeTooltip).Name.ToUpper()] = new Operations.WL_HomeTooltip(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_HomeDescription).Name.ToUpper()] = new Operations.WL_HomeDescription(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_BackTooltip).Name.ToUpper()] = new Operations.WL_BackTooltip(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_BackDescription).Name.ToUpper()] = new Operations.WL_BackDescription(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_ForwardTooltip).Name.ToUpper()] = new Operations.WL_ForwardTooltip(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_ForwardDescription).Name.ToUpper()] = new Operations.WL_ForwardDescription(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_TasksName).Name.ToUpper()] = new Operations.WL_TasksName(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_TasksTooltip).Name.ToUpper()] = new Operations.WL_TasksTooltip(wlCreator, dbPath);
+            _executors[typeof(Operations.WL_TasksDescription).Name.ToUpper()] = new Operations.WL_TasksDescription(wlCreator, dbPath);
+        }
+
+        class MgWebLayoutCreator : Operations.IWebLayoutCreator
+        {
+            private MgResourceService _resSvc;
+            private MgWebLayout _wl;
+
+            public MgWebLayoutCreator(MgResourceService resSvc)
+            {
+                _resSvc = resSvc;
+            }
+
+            public MgWebLayout CreateWebLayout(MgResourceIdentifier resId)
+            {
+                if (_wl != null)
+                    return _wl;
+
+                _wl = new MgWebLayout(_resSvc, resId);
+                return _wl;
+            }
+        }
+
+        class MgMapCreator : Operations.IMapCreator
+        {
+            private MgSiteConnection _siteConn;
+            private MgMap _map;
+            private MgResourceService _resSvc;
+
+            public MgMapCreator(MgSiteConnection siteConn, MgResourceService resSvc)
+            {
+                _siteConn = siteConn;
+                _resSvc = resSvc;
+            }
+
+            public MgMapBase CreateMap(MgResourceIdentifier resId)
+            {
+                //The test suite relies on the same map and not a new one
+                //re-created on each test of that particular test suite
+                if (_map != null)
+                    return _map;
+
+                _map = new MgMap(_siteConn);
+                _map.Create(resId, resId.GetName());
+                return _map;
+            }
+
+            public MgLayerBase CreateLayer(MgResourceIdentifier resId)
+            {
+                return new MgLayer(resId, _resSvc);
+            }
+        }
+
+        class MgSessionCreator : Operations.ISessionCreator
+        {
+            private MgSiteConnection _siteConn;
+
+            public MgSessionCreator(MgSiteConnection siteConn)
+            {
+                _siteConn = siteConn;
+            }
+
+            public string CreateSession()
+            {
+                var site = _siteConn.GetSite();
+                return site.CreateSession();
+            }
+        }
+
+        class MgApplySession : Operations.IApplySession
+        {
+            private MgUserInformation _userInfo;
+
+            public MgApplySession(MgUserInformation userInfo)
+            {
+                _userInfo = userInfo;
+            }
+
+            public void SetSessionId(string sessionId)
+            {
+                _userInfo.SetMgSessionId(sessionId);
+            }
+        }
+
+        class MgSession : Operations.IMapGuideSession
+        {
+            public string SessionID
+            {
+                get;
+                set;
+            }
+        }
+
+        public override ITestExecutor GetTestExecutor(string opName)
+        {
+            if (_executors.ContainsKey(opName))
+                return _executors[opName];
+            throw new Exception("Executor for " + opName + " not found");
+        }
+
+        public override int ValidateRequest(SqliteDb db, string testName, int paramSetId, string operation, TestResult actualResult, ITestLogger logger)
+        {
+            int exitStatus = 0;
+            string outcome = "pass";
+            SqliteVm vm = new SqliteVm(db, false);
+
+            object expectedResult = null;
+
+            //If we have an exception we need to remove the stack trace because different line numbers will fail the test
+            object resultData = CommonUtility.RemoveStackTraceFromResult(actualResult.ResultData);
+            //Get the mime type based on the content type in the result
+            string mimeType = actualResult.ContentType;
+            //If we have exception message we need to remove any parts that may contain system dependent information
+            //Ex. file paths
+            resultData = CommonUtility.ProcessExceptionMessage(resultData);
+            //Get the file extension that will be used for a dump
+            string actualExtension = CommonUtility.GetExtensionFromMimeType(mimeType);
+
+            //If we have an ALWAYSPASS parameter defined for the operation then skip the whole validation process
+            //This parameter should only be used for clean up operations that are no related with the tests
+            if (vm.Execute("Select ParamValue from Params where ParamName=\"ALWAYSPASS\" and ParamSet={0}", paramSetId) != Sqlite.Row)
+            {
+                //TestName is Test_[ServiceType]
+                string type = testName.Substring(testName.IndexOf("_") + 1);
+                string filePath = CommonUtility.GetPath($"{TestDataRoot.Path}/TestData/{type}/DumpFiles/{type}ApiTest");
+                string fileName = string.Format("{0}_{1}.{2}", filePath, paramSetId, actualExtension);
+
+                if (this.TestExecutionMode == "dump")
+                {
+                    //File.WriteAllText(fileName, resultData);
+                    throw new NotImplementedException("The .net test runner does not support dumping of test results. Please use the PHP test runner for this purpose");
+                }
+                else
+                {
+                    //This section is special case handling for the operations that return different data after each call
+                    resultData = CommonUtility.SpecialDataHandling(operation, resultData, mimeType);
+
+                    if (this.TestExecutionMode == "generate")
+                    {
+                        throw new NotImplementedException("The .net test runner does not support test update/generation. Please use the PHP test runner for this purpose");
+                        /*
+                        //Get the sample result that is stored in the database. If we are using file on disk for validation
+                        //then do not overwrite the filename in the database
+                        //To distinguish between sample data and filename all filenames should be prefixed with "@@"
+                        int status = vm.Execute("Select Result from ApiTestResults where ParamSet={0}", paramSetId);
+                        string sampleResult = vm.GetString("Result");
+
+                        if (!sampleResult.StartsWith("@@"))
+                        {
+                            //Insert the sample data as a BLOB
+                            //Update the row for that param set or create a new row if we do not have it yet
+
+                            string responseBody = "";
+                            if (status == Sqlite.Row)
+                            {
+                                vm.Prepare("update ApiTestResults set Result = :blob where ParamSet={0}", paramSetId);
+                            }
+                            else
+                            {
+                                Console.WriteLine("A new row has been created in ApiTestResults table to store the result for operation {0}", paramSetId);
+                                Console.WriteLine("Please update the description field for that row later");
+                                vm.Prepare("INSERT INTO ApiTestResults(ParamSet, Result) VALUES({0}, :blob)", paramSetId);
+                            }
+
+                            byte[] bytes = Encoding.UTF8.GetBytes(resultData);
+                            vm.BindBlob(":blob", bytes);
+                            vm.Execute();
+
+                            if (mimeType != null)
+                            {
+                                vm.Execute("UPDATE ApiTestResults SET ContentType=\"{0}\" WHERE ParamSet={1}", mimeType, paramSetId);
+                            }
+
+                            File.WriteAllText(fileName, resultData);
+                        }
+                         */
+                    }
+                    else if (this.TestExecutionMode == "validate" || this.TestExecutionMode == "show")
+                    {
+                        string resultContent = "";
+
+                        //Get the sample result and the expected content type from the database
+                        int status = vm.Execute("Select Result, ContentType from ApiTestResults where ParamSet={0}", paramSetId);
+                        string expectedContentType = vm.GetString("ContentType");
+                        string expectedExtension = CommonUtility.GetExtensionFromMimeType(expectedContentType ?? string.Empty);
+                        SqliteGcBlob blob = vm.GetBlob("Result");
+
+                        byte[] b = blob?.Read();
+                        if (b != null)
+                        {
+                            if (expectedExtension == "xml" || expectedExtension == "txt" || expectedExtension == "html")
+                                expectedResult = Encoding.UTF8.GetString(b);
+                            else
+                                expectedResult = b;
+                        }
+                        else
+                        {
+                            if (expectedExtension == "xml" || expectedExtension == "txt" || expectedExtension == "html")
+                                expectedResult = string.Empty;
+                            else
+                                expectedResult = null;
+                        }
+                        string strExpectedResult = expectedResult as string;
+                        //If we are validating from a file then get the contents of that file
+                        //File names should be prefixed with "@@" to distinguish them from BLOB data
+                        if (strExpectedResult != null && strExpectedResult.StartsWith("@@"))
+                        {
+                            string sampleDataFile = strExpectedResult.Substring(2);
+                            sampleDataFile = CommonUtility.GetPath(sampleDataFile);
+                            expectedResult = File.ReadAllText(sampleDataFile);
+                        }
+
+                        if (this.TestExecutionMode == "validate")
+                        {
+                            bool bEqual = false;
+                            byte[] bExpected = expectedResult as byte[];
+                            byte[] bActual = resultData as byte[];
+                            string strResultData = resultData as string;
+
+                            //FIXME: We're not processing DWF content properly to do this check properly. So just
+                            //pass these for now
+                            if (operation == "GETDRAWINGLAYER" || operation == "GETDRAWINGSECTION")
+                            {
+                                bEqual = true;
+                            }
+                            else
+                            {
+                                if (strExpectedResult != null && strResultData != null)
+                                {
+                                    //Normalize line endings on LF before comparsion (in case the SQLite GUI recorded CRLFs)
+                                    string normStrResultData = strResultData.Replace("\r\n", "\n");
+                                    string normStrExpectedResult = strExpectedResult.Replace("\r\n", "\n");
+
+                                    bEqual = normStrResultData.Equals(normStrExpectedResult, StringComparison.OrdinalIgnoreCase);
+                                }
+                                else if (bExpected != null && bActual != null)
+                                {
+                                    bEqual = CommonUtility.ByteArraysEqual(bExpected, bActual, operation, testName);
+                                }
+                                else
+                                {
+                                    System.Diagnostics.Debug.WriteLine(string.Format("[MgTestRunner]: {0} - {1} - Encountered disparate data types between expected and actual results. Expecting test failure :(", testName, operation));
+                                }
+                            }
+                            //If the results are different and special validation fails then the operation failed ->mark it red
+                            if (!bEqual && !CommonUtility.SpecialValidation(operation, resultData, expectedResult))
+                            {
+                                outcome = "fail";
+                                exitStatus = 1;
+
+                                if (expectedExtension != "xml" && expectedExtension != "html" && expectedExtension != "txt")
+                                {
+                                    expectedResult = "Unable to display binary data";
+                                }
+
+                                if (actualExtension != "xml" && actualExtension != "html" && actualExtension != "txt")
+                                {
+                                    resultData = "Unable to display binary data";
+                                }
+                            }
+                        }
+                        else
+                        {
+                            throw new NotImplementedException("The .net test runner does not support the given test execution mode (" + this.TestExecutionMode + "). Please use the PHP test runner for this purpose");
+                            /*
+                            type = testName.Substring(testName.IndexOf("_") + 1);
+                            string showPath = CommonUtility.GetPath(string.Format("../../TestData/{0}/ShowFiles/{0}ApiTest", type));
+                            string showName = string.Format("{0}_{1}.{2}", showPath, paramSetId, actualExtension);
+                            File.WriteAllText(showName, expectedResult);
+                             */
+                        }
+                    }
+                }
+            }
+
+            if (outcome == "fail")
+            {
+                Console.WriteLine("****{0} {1} {2} failed.\n", testName, paramSetId, operation);
+                if (string.Empty.Equals(expectedResult) && actualResult.IsException)
+                {
+                    //If expected result is empty and we got an exception, log the full details instead of exception name so we have a clue where to look
+                    string str = string.Format("\n****ACTUAL RESULT****\n{0}\n****EXPECTED RESULT****\n{1}\n********\n\n\n", actualResult.FullExceptionDetails, expectedResult);
+                    //Console.WriteLine(str);
+                    //Console.WriteLine("<FAIL>");
+                    logger.Write(str);
+                }
+                else
+                {
+                    string str = string.Format("\n****ACTUAL RESULT****\n{0}\n****EXPECTED RESULT****\n{1}\n********\n\n\n", resultData, expectedResult);
+                    //Console.WriteLine(str);
+                    //Console.WriteLine("<FAIL>");
+                    logger.Write(str);
+                }
+            }
+
+            vm.SqlFinalize();
+            vm = null;
+
+            return exitStatus;
+        }
+
+        public override void Cleanup()
+        {
+            foreach (var exec in _executors.Values)
+            {
+                exec.Dispose();
+            }
+            _executors.Clear();
+        }
+
+        public override string ApiType
+        {
+            get { return ApiTypes.Platform; }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MapGuideTests.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MapGuideTests.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MapGuideTests.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,45 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Reflection;
+
+namespace OSGeo.MapGuide.Test
+{
+    //Defines tests outside of the SQLite-based test suite
+
+    public class MapGuideTests
+    {
+        public static int Execute(IPlatformFactory factory, ITestLogger logger, ref int testsRun)
+        {
+            int failures = 0;
+            var types = typeof(MapGuideTests).GetTypeInfo().Assembly.GetTypes();
+            foreach (var type in types)
+            {
+                if (typeof(IExternalTest).IsAssignableFrom(type) && type.GetTypeInfo().IsClass && !type.GetTypeInfo().IsAbstract)
+                {
+                    var test = (IExternalTest)Activator.CreateInstance(type);
+                    try
+                    {
+                        logger.WriteLine("****** Executing MapGuide test: " + type.Name + " *********");
+                        Console.WriteLine("Executing external MapGuide test: " + type.Name);
+                        test.Execute(factory, logger);
+                    }
+                    catch (AssertException ex)
+                    {
+                        logger.WriteLine("Assertion failure: " + ex.Message);
+                        failures++;
+                    }
+                    catch (Exception ex)
+                    {
+                        logger.WriteLine("General failure: " + ex.ToString());
+                        failures++;
+                    }
+                    finally
+                    {
+                        testsRun++;
+                    }
+                }
+            }
+            return failures;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MappingService/MappingServiceOperationExecutor.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MappingService/MappingServiceOperationExecutor.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/MappingService/MappingServiceOperationExecutor.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,24 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    /// <summary>
+    /// The base class of all test executors using the MgMappingService
+    /// </summary>
+    /// <typeparam name="T">The test executor subclass type</typeparam>
+    public abstract class MappingServiceOperationExecutor<T> : PlatformApiTestExecutor
+    {
+        protected MgMappingService _mappingService;
+
+        protected MappingServiceOperationExecutor(MgMappingService mappingSvc, string unitTestVm)
+            : base(typeof(T).Name.ToUpper(), ApiTypes.Platform, unitTestVm)
+        {
+            _mappingService = mappingSvc;
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/Properties/AssemblyInfo.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/Properties/AssemblyInfo.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/Properties/AssemblyInfo.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,23 @@
+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("TestMapGuideApi")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TestMapGuideApi")]
+[assembly: AssemblyCopyright("Copyright ©  2015")]
+[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("b1f5b09f-febf-436f-8951-d315b603bbf4")]

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/RenderingService/RenderingServiceOperationExecutor.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/RenderingService/RenderingServiceOperationExecutor.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/RenderingService/RenderingServiceOperationExecutor.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,24 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    /// <summary>
+    /// The base class of all test executors using the MgRenderingService
+    /// </summary>
+    /// <typeparam name="T">The test executor subclass type</typeparam>
+    public abstract class RenderingServiceOperationExecutor<T> : PlatformApiTestExecutor
+    {
+        protected MgRenderingService _renderingService;
+
+        protected RenderingServiceOperationExecutor(MgRenderingService renderSvc, string unitTestVm)
+            : base(typeof(T).Name.ToUpper(), ApiTypes.Platform, unitTestVm)
+        {
+            _renderingService = renderSvc;
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ServerAdmin/Operations.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ServerAdmin/Operations.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ServerAdmin/Operations.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,357 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    /*
+    public class GetProperties : ServerAdminOperationExecutor<GetProperties>
+    {
+        public GetProperties(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+    */
+    public class Offline : ServerAdminOperationExecutor<Offline>
+    {
+        public Offline(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _serverAdmin.TakeOffline();
+                return new TestResult(CommonUtility.BooleanToString(_serverAdmin.IsOnline()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class Online : ServerAdminOperationExecutor<Online>
+    {
+        public Online(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _serverAdmin.BringOnline();
+                return new TestResult(CommonUtility.BooleanToString(_serverAdmin.IsOnline()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetLog : ServerAdminOperationExecutor<GetLog>
+    {
+        public GetLog(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "LOGTYPE", "NUMENTRIES" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgByteReader reader = null;
+                if (param["NUMENTRIES"] == null)
+                {
+                    reader = _serverAdmin.GetLog(param["LOGTYPE"]);
+                }
+                else
+                {
+                    reader = _serverAdmin.GetLog(param["LOGTYPE"], Convert.ToInt32(param["NUMENTRIES"]));
+                }
+
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetLogByDate : ServerAdminOperationExecutor<GetLogByDate>
+    {
+        public GetLogByDate(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "LOGTYPE", "FROMDATE", "TODATE" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                string[] fromDatePieces = (param["FROMDATE"] ?? "").Split(',');
+                string[] toDatePieces = (param["TODATE"] ?? "").Split(',');
+
+                MgDateTime fromDate = null;
+                MgDateTime toDate = null;
+
+                if (fromDatePieces.Length == 3)
+                {
+                    fromDate = new MgDateTime(Convert.ToInt16(fromDatePieces[0]), Convert.ToInt16(fromDatePieces[1]), Convert.ToInt16(fromDatePieces[2]));
+                }
+                else if (fromDatePieces.Length == 7)
+                {
+                    fromDate = new MgDateTime(Convert.ToInt16(fromDatePieces[0]), Convert.ToInt16(fromDatePieces[1]), Convert.ToInt16(fromDatePieces[2]), Convert.ToInt16(fromDatePieces[3]), Convert.ToInt16(fromDatePieces[4]), Convert.ToInt16(fromDatePieces[5]), Convert.ToInt32(fromDatePieces[6]));
+                }
+
+                if (toDatePieces.Length == 3)
+                {
+                    toDate = new MgDateTime(Convert.ToInt16(toDatePieces[0]), Convert.ToInt16(toDatePieces[1]), Convert.ToInt16(toDatePieces[2]));
+                }
+                else if (toDatePieces.Length == 7)
+                {
+                    toDate = new MgDateTime(Convert.ToInt16(toDatePieces[0]), Convert.ToInt16(toDatePieces[1]), Convert.ToInt16(toDatePieces[2]), Convert.ToInt16(toDatePieces[3]), Convert.ToInt16(toDatePieces[4]), Convert.ToInt16(toDatePieces[5]), Convert.ToInt32(toDatePieces[6]));
+                }
+
+                MgByteReader reader = _serverAdmin.GetLog(param["LOGTYPE"] ?? "", fromDate, toDate);
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class ClearLog : ServerAdminOperationExecutor<ClearLog>
+    {
+        public ClearLog(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "LOGTYPE" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                bool cleared = _serverAdmin.ClearLog(param["LOGTYPE"]);
+                return new TestResult(CommonUtility.BooleanToString(cleared), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    /*
+    public class EnumerateLogs : ServerAdminOperationExecutor<EnumerateLogs>
+    {
+        public EnumerateLogs(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+     */
+
+    public class DeleteLog : ServerAdminOperationExecutor<DeleteLog>
+    {
+        public DeleteLog(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "FILENAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _serverAdmin.DeleteLog(param["FILENAME"]);
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class RenameLog : ServerAdminOperationExecutor<RenameLog>
+    {
+        public RenameLog(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "OLDFILENAME", "NEWFILENAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _serverAdmin.RenameLog(param["OLDFILENAME"], param["NEWFILENAME"]);
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class EnumeratePackages : ServerAdminOperationExecutor<EnumeratePackages>
+    {
+        public EnumeratePackages(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                var packages = _serverAdmin.EnumeratePackages();
+                return new TestResult(CommonUtility.MgStringCollectionToString(packages), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class DeletePackage : ServerAdminOperationExecutor<DeletePackage>
+    {
+        public DeletePackage(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "PACKAGENAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _serverAdmin.DeletePackage(param["PACKAGENAME"]);
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class LoadPackage : ServerAdminOperationExecutor<LoadPackage>
+    {
+        public LoadPackage(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "PACKAGENAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _serverAdmin.LoadPackage(param["PACKAGENAME"]);
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetPackageStatus : ServerAdminOperationExecutor<GetPackageStatus>
+    {
+        public GetPackageStatus(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "PACKAGENAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgPackageStatusInformation status = _serverAdmin.GetPackageStatus(param["PACKAGENAME"]);
+                string code = status.GetStatusCode();
+                return new TestResult(code, "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetPackageLog : ServerAdminOperationExecutor<GetPackageLog>
+    {
+        public GetPackageLog(MgServerAdmin admin, string unitTestVm)
+            : base(admin, unitTestVm)
+        {
+        }
+
+        protected override string[] ParameterNames => new string[] { "PACKAGENAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgByteReader reader = _serverAdmin.GetPackageLog(param["PACKAGENAME"]);
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ServerAdmin/ServerAdminOperationExecutor.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ServerAdmin/ServerAdminOperationExecutor.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/ServerAdmin/ServerAdminOperationExecutor.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,24 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    /// <summary>
+    /// The base class of all test executors using the MgServerAdmin
+    /// </summary>
+    /// <typeparam name="T">The test executor subclass type</typeparam>
+    public abstract class ServerAdminOperationExecutor<T> : PlatformApiTestExecutor
+    {
+        protected MgServerAdmin _serverAdmin;
+
+        protected ServerAdminOperationExecutor(MgServerAdmin serverAdmin, string unitTestVm)
+            : base(typeof(T).Name.ToUpper(), ApiTypes.Platform, unitTestVm)
+        {
+            _serverAdmin = serverAdmin;
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/SiteService/Operations.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/SiteService/Operations.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/SiteService/Operations.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,746 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    public interface IMapGuideSession
+    {
+        string SessionID { get; set; }
+    }
+
+    public class CreateSession : SiteServiceOperationExecutor<CreateSession>
+    {
+        private IMapGuideSession _session;
+
+        public CreateSession(MgSite site, string unitTestVm, IMapGuideSession session)
+            : base(site, unitTestVm)
+        {
+            _session = session;
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                var user = new MgUserInformation();
+                user.SetMgUsernamePassword("Administrator", "admin");
+                user.SetLocale("en");
+
+                var site = new MgSite();
+                site.Open(user);
+
+                string session = site.CreateSession();
+                _session.SessionID = session;
+                site.Close();
+
+                return new TestResult(session, "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class DestroySession : SiteServiceOperationExecutor<DestroySession>
+    {
+        public DestroySession(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                //This is what PHP one is giving us
+                return new TestResult("Not Implemented Yet", "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GetUserForSession : SiteServiceOperationExecutor<GetUserForSession>
+    {
+        private IMapGuideSession _session;
+
+        public GetUserForSession(MgSite site, string unitTestVm, IMapGuideSession session)
+            : base(site, unitTestVm)
+        {
+            _session = session;
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                var site = new MgSite();
+                var user = new MgUserInformation();
+                user.SetMgSessionId(_session.SessionID ?? "");
+                site.Open(user);
+                var userId = site.GetUserForSession();
+                site.Close();
+                return new TestResult(userId, "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    /*
+    public class GetSiteServerAddress : SiteServiceOperationExecutor<GetSiteServerAddress>
+    {
+        public GetSiteServerAddress(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                var result = _site.GetCurrentSiteAddress();
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+     */
+
+    public class EnumerateUsers : SiteServiceOperationExecutor<EnumerateUsers>
+    {
+        public EnumerateUsers(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "GROUP", "ROLE", "INCLUDEGROUPS" };
+        
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgByteReader reader = null;
+                if (param["ROLE"] != null)
+                {
+                    reader = _site.EnumerateUsers(param["GROUP"] ?? "", param["ROLE"] ?? "", (param["INCLUDEGROUPS"] == "1"));
+                }
+                else
+                {
+                    reader = _site.EnumerateUsers(param["GROUP"] ?? "");
+                }
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class AddUser : SiteServiceOperationExecutor<AddUser>
+    {
+        public AddUser(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "USERID", "USERNAME", "PASSWORD", "DESCRIPTION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _site.AddUser(param["USERID"], param["USERNAME"], param["PASSWORD"], param["DESCRIPTION"]);
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class UpdateUser : SiteServiceOperationExecutor<UpdateUser>
+    {
+        public UpdateUser(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "USERID", "NEWUSERID", "NEWUSERNAME", "NEWPASSWORD", "NEWDESCRIPTION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _site.UpdateUser(param["USERID"], param["NEWUSERID"], param["NEWUSERNAME"], param["NEWPASSWORD"], param["NEWDESCRIPTION"]);
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class DeleteUsers : SiteServiceOperationExecutor<DeleteUsers>
+    {
+        public DeleteUsers(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "USERS" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgStringCollection users = CommonUtility.StringToMgStringCollection(param["USERS"]);
+
+                _site.DeleteUsers(users);
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GrantRoleMembershipsToUsers : SiteServiceOperationExecutor<GrantRoleMembershipsToUsers>
+    {
+        public GrantRoleMembershipsToUsers(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "ROLES", "USERS" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgStringCollection roles = CommonUtility.StringToMgStringCollection(param["ROLES"]);
+                MgStringCollection users = CommonUtility.StringToMgStringCollection(param["USERS"]);
+
+                _site.GrantRoleMembershipsToUsers(roles, users);
+
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class RevokeRoleMembershipsFromUsers : SiteServiceOperationExecutor<RevokeRoleMembershipsFromUsers>
+    {
+        public RevokeRoleMembershipsFromUsers(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "ROLES", "USERS" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgStringCollection roles = CommonUtility.StringToMgStringCollection(param["ROLES"]);
+                MgStringCollection users = CommonUtility.StringToMgStringCollection(param["USERS"]);
+
+                _site.RevokeRoleMembershipsFromUsers(roles, users);
+
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GrantGroupMembershipsToUsers : SiteServiceOperationExecutor<GrantGroupMembershipsToUsers>
+    {
+        public GrantGroupMembershipsToUsers(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "GROUPS", "USERS" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgStringCollection groups = CommonUtility.StringToMgStringCollection(param["GROUPS"]);
+                MgStringCollection users = CommonUtility.StringToMgStringCollection(param["USERS"]);
+
+                _site.GrantGroupMembershipsToUsers(groups, users);
+
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class RevokeGroupMembershipsFromUsers : SiteServiceOperationExecutor<RevokeGroupMembershipsFromUsers>
+    {
+        public RevokeGroupMembershipsFromUsers(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "GROUPS", "USERS" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgStringCollection groups = CommonUtility.StringToMgStringCollection(param["GROUPS"]);
+                MgStringCollection users = CommonUtility.StringToMgStringCollection(param["USERS"]);
+
+                _site.RevokeGroupMembershipsFromUsers(groups, users);
+
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class EnumerateGroups : SiteServiceOperationExecutor<EnumerateGroups>
+    {
+        public EnumerateGroups(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "USER", "ROLE" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgByteReader reader = _site.EnumerateGroups(param["USER"] ?? "", param["ROLE"] ?? "");
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class EnumerateGroups2 : SiteServiceOperationExecutor<EnumerateGroups2>
+    {
+        public EnumerateGroups2(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "USER", "LOGIN", "PASSWORD" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                var userInfo = new MgUserInformation();
+                userInfo.SetMgUsernamePassword(param["LOGIN"], param["PASSWORD"]);
+                userInfo.SetLocale("en");
+
+                var site = new MgSite();
+                site.Open(userInfo);
+
+                MgByteReader reader = site.EnumerateGroups(param["USER"]);
+                site.Close();
+
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class EnumerateRoles2 : SiteServiceOperationExecutor<EnumerateRoles2>
+    {
+        public EnumerateRoles2(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "USER", "LOGIN", "PASSWORD" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                var userInfo = new MgUserInformation();
+                userInfo.SetMgUsernamePassword(param["LOGIN"], param["PASSWORD"]);
+                userInfo.SetLocale("en");
+
+                var site = new MgSite();
+                site.Open(userInfo);
+
+                MgStringCollection roles = site.EnumerateRoles(param["USER"]);
+                site.Close();
+
+                return new TestResult(CommonUtility.MgStringCollectionToString(roles), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class AddGroup : SiteServiceOperationExecutor<AddGroup>
+    {
+        public AddGroup(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "GROUP", "DESCRIPTION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _site.AddGroup(param["GROUP"] ?? "", param["DESCRIPTION"] ?? "");
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class UpdateGroup : SiteServiceOperationExecutor<UpdateGroup>
+    {
+        public UpdateGroup(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "GROUP", "NEWGROUP", "NEWDESCRIPTION" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _site.UpdateGroup(param["GROUP"] ?? "", param["NEWGROUP"] ?? "", param["NEWDESCRIPTION"] ?? "");
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class DeleteGroups : SiteServiceOperationExecutor<DeleteGroups>
+    {
+        public DeleteGroups(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "GROUPS" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgStringCollection groups = CommonUtility.StringToMgStringCollection(param["GROUPS"]);
+                _site.DeleteGroups(groups);
+
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class GrantRoleMembershipsToGroups : SiteServiceOperationExecutor<GrantRoleMembershipsToGroups>
+    {
+        public GrantRoleMembershipsToGroups(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "ROLES", "GROUPS" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgStringCollection roles = CommonUtility.StringToMgStringCollection(param["ROLES"]);
+                MgStringCollection groups = CommonUtility.StringToMgStringCollection(param["GROUPS"]);
+
+                _site.GrantRoleMembershipsToGroups(roles, groups);
+
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class RevokeRoleMembershipsFromGroups : SiteServiceOperationExecutor<RevokeRoleMembershipsFromGroups>
+    {
+        public RevokeRoleMembershipsFromGroups(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "ROLES", "GROUPS" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgStringCollection roles = CommonUtility.StringToMgStringCollection(param["ROLES"]);
+                MgStringCollection groups = CommonUtility.StringToMgStringCollection(param["GROUPS"]);
+
+                _site.RevokeRoleMembershipsFromGroups(roles, groups);
+
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class EnumerateRoles : SiteServiceOperationExecutor<EnumerateRoles>
+    {
+        public EnumerateRoles(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "USER", "GROUP" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgStringCollection roles = _site.EnumerateRoles(param["USER"], param["GROUP"]);
+
+                return new TestResult(CommonUtility.MgStringCollectionToString(roles), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class EnumerateServers : SiteServiceOperationExecutor<EnumerateServers>
+    {
+        public EnumerateServers(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                MgByteReader reader = _site.EnumerateServers();
+                return TestResult.FromByteReader(reader);
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class AddServer : SiteServiceOperationExecutor<AddServer>
+    {
+        public AddServer(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "NAME", "DESCRIPTION", "ADDRESS" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _site.AddServer(param["NAME"], param["DESCRIPTION"], param["ADDRESS"]);
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class UpdateServer : SiteServiceOperationExecutor<UpdateServer>
+    {
+        public UpdateServer(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "OLDNAME", "NEWNAME", "NEWDESCRIPTION", "NEWADDRESS" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _site.UpdateServer(param["OLDNAME"], param["NEWNAME"], param["NEWDESCRIPTION"], param["NEWADDRESS"]);
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class RemoveServer : SiteServiceOperationExecutor<RemoveServer>
+    {
+        public RemoveServer(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "NAME" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                _site.RemoveServer(param["NAME"]);
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    /*
+    public class EnumerateServicesOnServer : SiteServiceOperationExecutor<EnumerateServicesOnServer>
+    {
+        public EnumerateServicesOnServer(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class AddServicesToServer : SiteServiceOperationExecutor<AddServicesToServer>
+    {
+        public AddServicesToServer(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+    
+    public class RemoveServicesFromServer : SiteServiceOperationExecutor<RemoveServicesFromServer>
+    {
+        public RemoveServicesFromServer(MgSite site, string unitTestVm)
+            : base(site, unitTestVm)
+        {
+
+        }
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                return new TestResult();
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+     */
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/SiteService/SiteServiceOperationExecutor.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/SiteService/SiteServiceOperationExecutor.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/SiteService/SiteServiceOperationExecutor.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,24 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    /// <summary>
+    /// The base class of all test executors using the MgSite
+    /// </summary>
+    /// <typeparam name="T">The test executor subclass type</typeparam>
+    public abstract class SiteServiceOperationExecutor<T> : PlatformApiTestExecutor
+    {
+        protected MgSite _site;
+
+        protected SiteServiceOperationExecutor(MgSite site, string unitTestVm)
+            : base(typeof(T).Name.ToUpper(), ApiTypes.Platform, unitTestVm)
+        {
+            _site = site;
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/TestMapGuideApi.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/TestMapGuideApi.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/TestMapGuideApi.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,21 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <Description>TestMapGuideApi Class Library</Description>
+    <Authors>OSGeo</Authors>
+    <TargetFrameworks>netstandard2.0</TargetFrameworks>
+    <AssemblyName>TestMapGuideApi</AssemblyName>
+    <PackageId>TestMapGuideApi</PackageId>
+    <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
+    <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
+    <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
+    <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
+    <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
+    <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
+  </PropertyGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\TestCommon\TestCommon.csproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Data.SQLite" Version="2.0.1" />
+  </ItemGroup>
+</Project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/WebLayout/Operations.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/WebLayout/Operations.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/WebLayout/Operations.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,666 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    public class WL_GetTitle : WebLayoutOperationExecutor<WL_GetTitle>
+    {
+        public WL_GetTitle(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                return new TestResult(_wl.GetTitle(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);   
+            }
+        }
+    }
+
+    public class WL_GetMapDefinition : WebLayoutOperationExecutor<WL_GetMapDefinition>
+    {
+        public WL_GetMapDefinition(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                return new TestResult(_wl.GetMapDefinition(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_GetScale : WebLayoutOperationExecutor<WL_GetScale>
+    {
+        public WL_GetScale(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                return new TestResult(_wl.GetScale().ToString(CultureInfo.InvariantCulture), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_GetCenter : WebLayoutOperationExecutor<WL_GetCenter>
+    {
+        public WL_GetCenter(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var center = _wl.GetCenter();
+                if (center == null)
+                {
+                    return new TestResult("", "text/plain");
+                }
+                else
+                {
+                    var coord = center.Coordinate;
+                    return new TestResult(coord.GetX() + "/" + coord.GetY(), "text/plain");
+                }
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_ShowToolbar : WebLayoutOperationExecutor<WL_ShowToolbar>
+    {
+        public WL_ShowToolbar(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tb = _wl.GetToolBar();
+                return new TestResult(CommonUtility.BooleanToString(tb.IsVisible()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_ShowStatusbar : WebLayoutOperationExecutor<WL_ShowStatusbar>
+    {
+        public WL_ShowStatusbar(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var sb = _wl.GetStatusBar();
+                return new TestResult(CommonUtility.BooleanToString(sb.IsVisible()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_ShowTaskpane : WebLayoutOperationExecutor<WL_ShowTaskpane>
+    {
+        public WL_ShowTaskpane(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                return new TestResult(CommonUtility.BooleanToString(tp.IsVisible()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_ShowTaskbar : WebLayoutOperationExecutor<WL_ShowTaskbar>
+    {
+        public WL_ShowTaskbar(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                var tb = tp.GetTaskBar();
+                return new TestResult(CommonUtility.BooleanToString(tb.IsVisible()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_ShowLegend : WebLayoutOperationExecutor<WL_ShowLegend>
+    {
+        public WL_ShowLegend(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var ip = _wl.GetInformationPane();
+                return new TestResult(CommonUtility.BooleanToString(ip.IsLegendBandVisible()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_ShowProperties : WebLayoutOperationExecutor<WL_ShowProperties>
+    {
+        public WL_ShowProperties(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var ip = _wl.GetInformationPane();
+                return new TestResult(CommonUtility.BooleanToString(ip.IsPropertiesBandVisible()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_GetTaskPaneWidth : WebLayoutOperationExecutor<WL_GetTaskPaneWidth>
+    {
+        public WL_GetTaskPaneWidth(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                return new TestResult(tp.GetWidth().ToString(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_GetInformationPaneWidth : WebLayoutOperationExecutor<WL_GetInformationPaneWidth>
+    {
+        public WL_GetInformationPaneWidth(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var ip = _wl.GetInformationPane();
+                return new TestResult(ip.GetWidth().ToString(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_GetInitialTaskUrl : WebLayoutOperationExecutor<WL_GetInitialTaskUrl>
+    {
+        public WL_GetInitialTaskUrl(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                return new TestResult(tp.GetInitialTaskUrl(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_ShowContextMenu : WebLayoutOperationExecutor<WL_ShowContextMenu>
+    {
+        public WL_ShowContextMenu(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var ctx = _wl.GetContextMenu();
+                return new TestResult(CommonUtility.BooleanToString(ctx.IsVisible()), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_TestUiItem : WebLayoutOperationExecutor<WL_TestUiItem>
+    {
+        public WL_TestUiItem(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT", "CONTAINER", "INDEX" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+
+                MgWebWidgetCollection coll = null;
+
+                switch (param["CONTAINER"])
+                {
+                    case "toolbar":
+                        var tb = _wl.GetToolBar();
+                        coll = tb.GetWidgets();
+                        break;
+                    case "tasklist":
+                        var tp = _wl.GetTaskPane();
+                        var tbar = tp.GetTaskBar();
+                        coll = tbar.GetTaskList();
+                        break;
+                    case "contextmenu":
+                        coll = _wl.GetContextMenu();
+                        break;
+                }
+
+                var widget = coll.GetWidget(Convert.ToInt32(param["INDEX"]));
+                if (widget == null)
+                    return new TestResult("Null widget", "text/plain");
+                else
+                    return new TestResult(FormatProperties(widget), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_HomeTooltip : WebLayoutOperationExecutor<WL_HomeTooltip>
+    {
+        public WL_HomeTooltip(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                var tbar = tp.GetTaskBar();
+                var tbuttons = tbar.GetTaskButtons();
+                var home = (MgWebTaskBarWidget)tbuttons.GetWidget(MgWebTaskButtonType.Home);
+                return new TestResult(home.GetTooltip(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_HomeDescription : WebLayoutOperationExecutor<WL_HomeDescription>
+    {
+        public WL_HomeDescription(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                var tbar = tp.GetTaskBar();
+                var tbuttons = tbar.GetTaskButtons();
+                var home = (MgWebTaskBarWidget)tbuttons.GetWidget(MgWebTaskButtonType.Home);
+                return new TestResult(home.GetDescription(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_BackTooltip : WebLayoutOperationExecutor<WL_BackTooltip>
+    {
+        public WL_BackTooltip(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                var tbar = tp.GetTaskBar();
+                var tbuttons = tbar.GetTaskButtons();
+                var back = (MgWebTaskBarWidget)tbuttons.GetWidget(MgWebTaskButtonType.Back);
+                return new TestResult(back.GetTooltip(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_BackDescription : WebLayoutOperationExecutor<WL_BackDescription>
+    {
+        public WL_BackDescription(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                var tbar = tp.GetTaskBar();
+                var tbuttons = tbar.GetTaskButtons();
+                var back = (MgWebTaskBarWidget)tbuttons.GetWidget(MgWebTaskButtonType.Back);
+                return new TestResult(back.GetDescription(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_ForwardTooltip : WebLayoutOperationExecutor<WL_ForwardTooltip>
+    {
+        public WL_ForwardTooltip(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                var tbar = tp.GetTaskBar();
+                var tbuttons = tbar.GetTaskButtons();
+                var fwd = (MgWebTaskBarWidget)tbuttons.GetWidget(MgWebTaskButtonType.Forward);
+                return new TestResult(fwd.GetTooltip(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_ForwardDescription : WebLayoutOperationExecutor<WL_ForwardDescription>
+    {
+        public WL_ForwardDescription(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                var tbar = tp.GetTaskBar();
+                var tbuttons = tbar.GetTaskButtons();
+                var fwd = (MgWebTaskBarWidget)tbuttons.GetWidget(MgWebTaskButtonType.Forward);
+                return new TestResult(fwd.GetDescription(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_TasksName : WebLayoutOperationExecutor<WL_TasksName>
+    {
+        public WL_TasksName(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                var tbar = tp.GetTaskBar();
+                var tbuttons = tbar.GetTaskButtons();
+                var tasks = (MgWebTaskBarWidget)tbuttons.GetWidget(MgWebTaskButtonType.Tasks);
+                return new TestResult(tasks.GetName(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_TasksTooltip : WebLayoutOperationExecutor<WL_TasksTooltip>
+    {
+        public WL_TasksTooltip(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                var tbar = tp.GetTaskBar();
+                var tbuttons = tbar.GetTaskButtons();
+                var tasks = (MgWebTaskBarWidget)tbuttons.GetWidget(MgWebTaskButtonType.Tasks);
+                return new TestResult(tasks.GetTooltip(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+
+    public class WL_TasksDescription : WebLayoutOperationExecutor<WL_TasksDescription>
+    {
+        public WL_TasksDescription(IWebLayoutCreator layout, string unitTestVm)
+            : base(layout, unitTestVm)
+        {
+
+        }
+
+        protected override string[] ParameterNames => new string[] { "WEBLAYOUT" };
+
+        protected override TestResult ExecuteInternal(NameValueCollection param)
+        {
+            try
+            {
+                CreateWebLayoutFromResource(param);
+                var tp = _wl.GetTaskPane();
+                var tbar = tp.GetTaskBar();
+                var tbuttons = tbar.GetTaskButtons();
+                var tasks = (MgWebTaskBarWidget)tbuttons.GetWidget(MgWebTaskButtonType.Tasks);
+                return new TestResult(tasks.GetDescription(), "text/plain");
+            }
+            catch (MgException ex)
+            {
+                return TestResult.FromMgException(ex, param);
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/WebLayout/WebLayoutOperationExecutor.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/WebLayout/WebLayoutOperationExecutor.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApi/WebLayout/WebLayoutOperationExecutor.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,102 @@
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSGeo.MapGuide.Test.Operations
+{
+    public interface IWebLayoutCreator
+    {
+        MgWebLayout CreateWebLayout(MgResourceIdentifier resId);
+    }
+
+    /// <summary>
+    /// The base class of all test executors using the MgWebLayout
+    /// </summary>
+    /// <typeparam name="T">The test executor subclass type</typeparam>
+    public abstract class WebLayoutOperationExecutor<T> : PlatformApiTestExecutor
+    {
+        protected MgWebLayout _wl;
+        protected IWebLayoutCreator _creator;
+
+        protected WebLayoutOperationExecutor(IWebLayoutCreator wl, string unitTestVm)
+            : base(typeof(T).Name.ToUpper(), ApiTypes.Platform, unitTestVm)
+        {
+            _creator = wl;
+        }
+
+        /*
+        protected void CreateWebLayoutFromResource(int paramSetId)
+        {
+            if (_wl != null)
+                return;
+
+            _unitTestVm.Execute("Select ParamValue from Params WHERE ParamSet={0} AND ParamName=\"WEBLAYOUT\"", paramSetId);
+            string wlId = _unitTestVm.GetString("ParamValue");
+            if (string.IsNullOrEmpty(wlId))
+            {
+                wlId = "Library://UnitTest/layouts/Test.WebLayout";
+            }
+            MgResourceIdentifier resId = new MgResourceIdentifier(wlId);
+            _wl = _creator.CreateWebLayout(resId);
+        }
+        */
+
+        protected void CreateWebLayoutFromResource(NameValueCollection param)
+        {
+            string wlId = param["WEBLAYOUT"] ?? "Library://UnitTest/layouts/Test.WebLayout";
+            MgResourceIdentifier resId = new MgResourceIdentifier(wlId);
+            _wl = _creator.CreateWebLayout(resId);
+        }
+
+        protected string FormatProperties(MgWebWidget it)
+        {
+            string name = "";
+            switch (it.GetType())
+            { 
+                case MgWebWidgetType.Separator:
+                    name = "";
+                    break;
+                case MgWebWidgetType.Command:
+                    var cmd = ((MgWebCommandWidget)it).GetCommand();
+                    name = cmd.GetLabel();
+                    break;
+                case MgWebWidgetType.Flyout:
+                    name = ((MgWebFlyoutWidget)it).GetLabel();
+                    break;
+            }
+
+            string str = "[" + name + "/" + it.GetType();
+            if (it.GetType() == MgWebWidgetType.Separator)
+            {
+                return str + "]";
+            }
+            else if (it.GetType() == MgWebWidgetType.Command)
+            {
+                var cmd = ((MgWebCommandWidget)it).GetCommand();
+                return str + "/" + cmd.GetName() + "]";
+            }
+            else if (it.GetType() == MgWebWidgetType.Flyout)
+            {
+                var fly = ((MgWebFlyoutWidget)it);
+                str = str + "/" + fly.GetIconUrl();
+                var coll = fly.GetSubItems();
+                for (int i = 0; i < coll.GetCount(); i++)
+                {
+                    var widget = coll.GetWidget(i);
+                    str = str + "/" + FormatProperties(widget);
+                }
+                str = str + "]";
+            }
+            else
+            {
+                return "[** error **]";
+            }
+
+            return str;
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApiFull/Properties/AssemblyInfo.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApiFull/Properties/AssemblyInfo.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApiFull/Properties/AssemblyInfo.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -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("TestMapGuideApiFull")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TestMapGuideApiFull")]
+[assembly: AssemblyCopyright("Copyright ©  2018")]
+[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("6584c02f-080f-425d-be53-9f72f7be2897")]
+
+// 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/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApiFull/TestMapGuideApiFull.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApiFull/TestMapGuideApiFull.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMapGuideApiFull/TestMapGuideApiFull.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,35 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <Description>TestMapGuideApi Class Library (Full Framework)</Description>
+    <Authors>OSGeo</Authors>
+    <TargetFramework>net461</TargetFramework>
+    <AssemblyName>TestMapGuideApi</AssemblyName>
+    <PackageId>TestMapGuideApi</PackageId>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="..\TestMapGuideApi\DrawingService\DrawingServiceOperationExecutor.cs" Link="DrawingService\DrawingServiceOperationExecutor.cs" />
+    <Compile Include="..\TestMapGuideApi\DrawingService\Operations.cs" Link="DrawingService\Operations.cs" />
+    <Compile Include="..\TestMapGuideApi\ExternalTests\CollectionTests.cs" Link="ExternalTests\CollectionTests.cs" />
+    <Compile Include="..\TestMapGuideApi\ExternalTests\MapTests.cs" Link="ExternalTests\MapTests.cs" />
+    <Compile Include="..\TestMapGuideApi\ExternalTests\RenderingServiceTests.cs" Link="ExternalTests\RenderingServiceTests.cs" />
+    <Compile Include="..\TestMapGuideApi\ExternalTests\ResourceServiceTests.cs" Link="ExternalTests\ResourceServiceTests.cs" />
+    <Compile Include="..\TestMapGuideApi\MapGuideResources.cs" Link="MapGuideResources.cs" />
+    <Compile Include="..\TestMapGuideApi\MapGuideTestExecutorCollection.cs" Link="MapGuideTestExecutorCollection.cs" />
+    <Compile Include="..\TestMapGuideApi\MapGuideTests.cs" Link="MapGuideTests.cs" />
+    <Compile Include="..\TestMapGuideApi\MappingService\MappingServiceOperationExecutor.cs" Link="MappingService\MappingServiceOperationExecutor.cs" />
+    <Compile Include="..\TestMapGuideApi\RenderingService\RenderingServiceOperationExecutor.cs" Link="RenderingService\RenderingServiceOperationExecutor.cs" />
+    <Compile Include="..\TestMapGuideApi\ServerAdmin\Operations.cs" Link="ServerAdmin\Operations.cs" />
+    <Compile Include="..\TestMapGuideApi\ServerAdmin\ServerAdminOperationExecutor.cs" Link="ServerAdmin\ServerAdminOperationExecutor.cs" />
+    <Compile Include="..\TestMapGuideApi\SiteService\Operations.cs" Link="SiteService\Operations.cs" />
+    <Compile Include="..\TestMapGuideApi\SiteService\SiteServiceOperationExecutor.cs" Link="SiteService\SiteServiceOperationExecutor.cs" />
+    <Compile Include="..\TestMapGuideApi\WebLayout\Operations.cs" Link="WebLayout\Operations.cs" />
+    <Compile Include="..\TestMapGuideApi\WebLayout\WebLayoutOperationExecutor.cs" Link="WebLayout\WebLayoutOperationExecutor.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\TestCommonFull\TestCommonFull.csproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Data.SQLite" Version="2.0.1" />
+  </ItemGroup>
+</Project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/Program.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/Program.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/Program.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,127 @@
+using System;
+using System.Runtime.InteropServices;
+using OSGeo.MapGuide;
+
+namespace TestMisc
+{
+    class Program
+    {
+        static void TestBody(string webConfigPath)
+        {
+            MapGuideApi.MgInitializeWebTier(webConfigPath);
+            Console.WriteLine("[dotnet]: Initialized");
+            var user = new MgUserInformation("Anonymous", "");
+            var conn = new MgSiteConnection();
+            conn.Open(user);
+            // Create a session repository
+            var site = conn.GetSite();
+            var sessionID = site.CreateSession();
+            Console.WriteLine($"[dotnet]: Created session: {sessionID}");
+            user.SetMgSessionId(sessionID);
+            // Get an instance of the required services.
+            var resourceService = (MgResourceService)conn.CreateService(MgServiceType.ResourceService);
+            Console.WriteLine("[dotnet]: Created Resource Service");
+            var mappingService = (MgMappingService)conn.CreateService(MgServiceType.MappingService);
+            Console.WriteLine("[dotnet]: Created Mapping Service");
+            var resId = new MgResourceIdentifier("Library://UnitTest/");
+            Console.WriteLine("[dotnet]: Enumeratin'");
+            var resources = resourceService.EnumerateResources(resId, -1, "");
+            Console.WriteLine(resources.ToString());
+            Console.WriteLine("[dotnet]: Coordinate System");
+            var csFactory = new MgCoordinateSystemFactory();
+            Console.WriteLine("[dotnet]: CS Catalog");
+            var catalog = csFactory.GetCatalog();
+            Console.WriteLine("[dotnet]: Category Dictionary");
+            var catDict = catalog.GetCategoryDictionary();
+            Console.WriteLine("[dotnet]: CS Dictionary");
+            var csDict = catalog.GetCoordinateSystemDictionary();
+            Console.WriteLine("[dotnet]: Datum Dictionary");
+            var datumDict = catalog.GetDatumDictionary();
+            Console.WriteLine("[dotnet]: Coordinate System - LL84");
+            var cs1 = csFactory.CreateFromCode("LL84");
+            Console.WriteLine("[dotnet]: Coordinate System - WGS84.PseudoMercator");
+            var cs2 = csFactory.CreateFromCode("WGS84.PseudoMercator");
+            Console.WriteLine("[dotnet]: Make xform");
+            var xform = csFactory.GetTransform(cs1, cs2);
+            Console.WriteLine("[dotnet]: WKT reader");
+            var wktRw = new MgWktReaderWriter();
+            Console.WriteLine("[dotnet]: WKT Point");
+            var pt = (MgPoint)wktRw.Read("POINT (1 2)");
+            var coord = pt.GetCoordinate();
+            Console.WriteLine($"[dotnet]: X: {coord.X}, Y: {coord.Y}");
+            site.DestroySession(sessionID);
+            Console.WriteLine($"[dotnet]: Destroyed session {sessionID}");
+            Console.WriteLine("[dotnet]: Test byte reader");
+            var bytes = System.Text.Encoding.UTF8.GetBytes("abcd1234");
+            var bs = new MgByteSource(bytes, bytes.Length);
+            var content = "";
+            var br = bs.GetReader();
+            byte[] buffer = new byte[2];
+            int read = br.Read(buffer, 2);
+            while (read > 0)
+            {
+                var sbuf = System.Text.Encoding.UTF8.GetString(buffer);
+                Console.WriteLine("Buffer: " + sbuf);
+                content += sbuf;
+                read = br.Read(buffer, 2);
+            }
+            Console.WriteLine("[dotnet]: Test byte reader 2");
+            var bs2 = new MgByteSource(bytes, bytes.Length);
+            content = "";
+            var br2 = bs2.GetReader();
+            buffer = new byte[3];
+            read = br2.Read(buffer, 3);
+            while (read > 0)
+            {
+                var sbuf = System.Text.Encoding.UTF8.GetString(buffer, 0, read);
+                Console.WriteLine("Buffer: " + sbuf);
+                content += sbuf;
+                read = br2.Read(buffer, 3);
+            }
+            var agfRw = new MgAgfReaderWriter();
+            Console.WriteLine("[dotnet]: Trigger an exception");
+            try
+            {
+                agfRw.Read(null);
+            }
+            catch (MgException ex)
+            {
+                Console.WriteLine("[dotnet]: MgException caught");
+                Console.WriteLine($"[dotnet]: MgException - Message: {ex.GetExceptionMessage()}");
+                Console.WriteLine($"[dotnet]: MgException - Details: {ex.GetDetails()}");
+                Console.WriteLine($"[dotnet]: MgException - Stack: {ex.GetStackTrace()}");
+            }
+            Console.WriteLine("[dotnet]: Trigger another exception");
+            try
+            {
+                var r = new MgResourceIdentifier("");
+            }
+            catch (MgException ex)
+            {
+                Console.WriteLine("[dotnet]: MgException caught");
+                Console.WriteLine($"[dotnet]: MgException - Message: {ex.GetExceptionMessage()}");
+                Console.WriteLine($"[dotnet]: MgException - Details: {ex.GetDetails()}");
+                Console.WriteLine($"[dotnet]: MgException - Stack: {ex.GetStackTrace()}");
+            }
+        }
+
+        static void Main(string[] args)
+        {
+            string path = args[0];
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                Console.WriteLine($"Running on Windows: {path}");
+            }
+            else
+            {
+                Console.WriteLine($"Running on Linux: {path}");
+            }
+            TestBody(path);
+            //If you have built the .net SWIG glue wrapper with REFCOUNTING_DIAGNOSTICS, then
+            //you should be seeing a whole bunch of refcounting chatter, which is verification
+            //that we are actually releasing our unmanaged resources 
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/TestMisc.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/TestMisc.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/TestMisc.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,10 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
+  </PropertyGroup>
+  <ItemGroup>
+  </ItemGroup>
+
+</Project>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMiscFull/Properties/AssemblyInfo.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMiscFull/Properties/AssemblyInfo.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMiscFull/Properties/AssemblyInfo.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -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("TestMiscFull")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TestMiscFull")]
+[assembly: AssemblyCopyright("Copyright ©  2018")]
+[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("27285bb5-37b4-42dd-ab2c-ccd91824d044")]
+
+// 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/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMiscFull/TestMiscFull.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMiscFull/TestMiscFull.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMiscFull/TestMiscFull.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net461</TargetFramework>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+    <Platforms>x64;x86</Platforms>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="..\TestMisc\Program.cs" Link="Program.cs" />
+  </ItemGroup>
+</Project>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Program.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Program.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Program.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,163 @@
+using CommandLine;
+using OSGeo.MapGuide;
+using OSGeo.MapGuide.Test;
+using OSGeo.MapGuide.Test.Common;
+using System;
+using System.IO;
+
+namespace TestRunner
+{
+    //TODO: This test runner does not support generation/update mode yet. Please use the existing PHP test runner for doing this.
+
+    class Options
+    {
+        [Option("web-config-path", Required = true, HelpText = "Path to webconfig.ini")]
+        public string WebConfigPath { get; set; }
+
+        [Option("dictionary-path", Required = true, HelpText = "CS-Map Dictionary Path")]
+        public string DictionaryPath { get; set; }
+
+        [Option("test-data-root", Required = true, HelpText = "Root path of test data files")]
+        public string TestDataRoot { get; set; }
+
+        [Option("log-path", Required = false, HelpText = "Custom log path. If not set, will default to UnitTests.log on the current directory")]
+        public string LogPath { get; set; }
+    }
+
+
+    public class Program
+    {
+        static MgUserInformation userInfo;
+        static MgSiteConnection siteConn;
+
+        class PlatformFactory : IPlatformFactory
+        {
+            private MgSiteConnection _siteConn;
+
+            public PlatformFactory(MgSiteConnection siteConn)
+            {
+                _siteConn = siteConn;
+            }
+
+            public MgService CreateService(int serviceType)
+            {
+                return _siteConn.CreateService(serviceType);
+            }
+
+            public MgMapBase CreateMap(MgResourceIdentifier mapDefinition)
+            {
+                var map = new MgMap(_siteConn);
+                map.Create(mapDefinition, mapDefinition.Name);
+                return map;
+            }
+
+            public MgMapBase CreateMap(string coordSys, MgEnvelope env, string name)
+            {
+                var map = new MgMap(_siteConn);
+                map.Create(coordSys, env, name);
+                return map;
+            }
+
+            public MgLayerBase CreateLayer(MgResourceIdentifier resId)
+            {
+                MgResourceService resSvc = (MgResourceService)_siteConn.CreateService(MgServiceType.ResourceService);
+                return new MgLayer(resId, resSvc);
+            }
+        }
+
+        //Usage: MgTestRunner.exe <webconfig.ini path> <MENTOR_DICTIONARY_PATH> [test log path]
+        static int Main(string[] args)
+        {
+            int exitCode = 0;
+            Parser.Default.ParseArguments<Options>(args)
+                .WithParsed(opts =>
+                {
+                    exitCode = Run(opts);
+                });
+
+            return exitCode;
+        }
+
+        static int Run(Options options)
+        {
+            if (!File.Exists(options.WebConfigPath))
+            {
+                Console.WriteLine("webconfig.ini not found");
+                return 1;
+            }
+            if (!Directory.Exists(options.DictionaryPath))
+            {
+                Console.WriteLine("CS-Map Dictionary Path not found");
+                return 1;
+            }
+            if (!File.Exists(Path.Combine(options.TestDataRoot, $"ResourceService{Path.DirectorySeparatorChar}ResourceServiceTest.dump")))
+            {
+                Console.WriteLine("Bad test data root path");
+                return 1;
+            }
+
+            string webconfig = options.WebConfigPath;
+            string logFile = options.LogPath;
+            if (string.IsNullOrEmpty(logFile))
+                logFile = "UnitTests.log";
+
+            TestDataRoot.Path = options.TestDataRoot;
+
+            int failures = 0;
+            using (var logger = new TestLoggerFile(logFile, false))
+            {
+
+                logger.Write("Run started: {0}\n\n", DateTime.Now.ToString());
+
+                Environment.SetEnvironmentVariable("MENTOR_DICTIONARY_PATH", options.DictionaryPath, EnvironmentVariableTarget.Process);
+
+                MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
+                Console.WriteLine($"Using CS Library: {csFactory.GetBaseLibrary()}");
+
+                MapGuideApi.MgInitializeWebTier(options.WebConfigPath);
+                userInfo = new MgUserInformation("Administrator", "admin");
+                siteConn = new MgSiteConnection();
+                siteConn.Open(userInfo);
+
+                var factory = new PlatformFactory(siteConn);
+
+                int testsRun = 0;
+                bool isEnterprise = false;
+                failures += ExecuteTest(ApiTypes.Platform, $"{TestDataRoot.Path}/ResourceService/ResourceServiceTest.dump", ref testsRun, logger, isEnterprise);
+                failures += ExecuteTest(ApiTypes.Platform, $"{TestDataRoot.Path}/DrawingService/DrawingServiceTest.dump", ref testsRun, logger, isEnterprise);
+                failures += ExecuteTest(ApiTypes.Platform, $"{TestDataRoot.Path}/FeatureService/FeatureServiceTest.dump", ref testsRun, logger, isEnterprise);
+                failures += ExecuteTest(ApiTypes.Platform, $"{TestDataRoot.Path}/SiteService/SiteServiceTest.dump", ref testsRun, logger, isEnterprise);
+                failures += ExecuteTest(ApiTypes.Platform, $"{TestDataRoot.Path}/MappingService/MappingServiceTest.dump", ref testsRun, logger, isEnterprise);
+                failures += ExecuteTest(ApiTypes.Platform, $"{TestDataRoot.Path}/ServerAdmin/ServerAdminTest.dump", ref testsRun, logger, isEnterprise);
+                failures += ExecuteTest(ApiTypes.Platform, $"{TestDataRoot.Path}/MapLayer/MapLayerTest.dump", ref testsRun, logger, isEnterprise);
+                failures += ExecuteTest(ApiTypes.Platform, $"{TestDataRoot.Path}/WebLayout/WebLayoutTest.dump", ref testsRun, logger, isEnterprise);
+                failures += ExecuteTest(ApiTypes.Platform, $"{TestDataRoot.Path}/Unicode/UnicodeTest.dump", ref testsRun, logger, isEnterprise);
+                //Run auxillary tests not part of the SQLite-defined suite
+                failures += CommonTests.Execute(factory, logger, ref testsRun);
+                failures += MapGuideTests.Execute(factory, logger, ref testsRun);
+                logger.Write("\n\nTests failed/run: {0}/{1}\n", failures, testsRun);
+                Console.Write("\n\nTests failed/run: {0}/{1}\n", failures, testsRun);
+                logger.Write("Run ended: {0}\n\n", DateTime.Now.ToString());
+            }
+            //Environment.ExitCode = failures;
+            return failures;
+        }
+
+        private static int ExecuteTest(string apiType, string dumpFile, ref int testsRun, TestLoggerFile logger, bool isEnterprise)
+        {
+            ITestExecutorCollection exec = null;
+            if (apiType == ApiTypes.Platform)
+                exec = new MapGuideTestExecutorCollection(userInfo, siteConn);
+
+            int ret = 0;
+            if (exec != null)
+            {
+                //"validate" is currently the only test execution mode supported
+                exec.Initialize("validate", dumpFile);
+                ret += exec.Execute(ref testsRun, logger, isEnterprise);
+                exec.Cleanup();
+            }
+            return ret;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Properties/AssemblyInfo.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Properties/AssemblyInfo.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Properties/AssemblyInfo.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,23 @@
+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("TestRunner")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TestRunner")]
+[assembly: AssemblyCopyright("Copyright ©  2015")]
+[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("4dcfcfdb-3698-45b6-908e-19da1cff56fd")]

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Properties/launchSettings.json
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Properties/launchSettings.json	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/Properties/launchSettings.json	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,9 @@
+{
+  "profiles": {
+    "TestRunner": {
+      "commandName": "Project",
+      "commandLineArgs": "--web-config-path \"C:\\Program Files\\OSGeo\\MapGuide\\Web\\www\\webconfig.ini\" --dictionary-path \"C:\\Program Files\\OSGeo\\MapGuide\\CS-Map\\Dictionaries\" --test-data-root \"D:\\workspace\\mapguide-api-bindings\\src\\TestData\"",
+      "workingDirectory": "D:\\Workspace\\mapguide-api-bindings\\src\\Test\\DotNet\\src\\TestRunner"
+    }
+  }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/TestRunner.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/TestRunner.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/TestRunner.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,23 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <Description>TestRunner Console Application</Description>
+    <Authors>OSGeo</Authors>
+    <TargetFrameworks>netcoreapp2.1</TargetFrameworks>
+    <AssemblyName>TestRunner</AssemblyName>
+    <OutputType>Exe</OutputType>
+    <PackageId>TestRunner</PackageId>
+    <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
+    <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
+    <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
+    <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
+    <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
+    <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
+  </PropertyGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\TestCommon\TestCommon.csproj" />
+    <ProjectReference Include="..\TestMapGuideApi\TestMapGuideApi.csproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="CommandLineParser" Version="2.2.1" />
+  </ItemGroup>
+</Project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/App.config
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/App.config	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/App.config	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
+    </startup>
+</configuration>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/Properties/AssemblyInfo.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/Properties/AssemblyInfo.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/Properties/AssemblyInfo.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -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("TestRunnerFull")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TestRunnerFull")]
+[assembly: AssemblyCopyright("Copyright ©  2018")]
+[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("757ba8eb-065f-47a7-834d-025c1559e2a5")]
+
+// 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/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/TestRunnerFull.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/TestRunnerFull.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunnerFull/TestRunnerFull.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <Description>TestRunner Console Application (Full Framework)</Description>
+    <Authors>OSGeo</Authors>
+    <TargetFramework>net461</TargetFramework>
+    <AssemblyName>TestRunnerFull</AssemblyName>
+    <OutputType>Exe</OutputType>
+    <PackageId>TestRunnerFull</PackageId>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+    <Platforms>x64;x86</Platforms>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="..\TestRunner\Program.cs" Link="Program.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\TestCommonFull\TestCommonFull.csproj" />
+    <ProjectReference Include="..\TestMapGuideApiFull\TestMapGuideApiFull.csproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="CommandLineParser" Version="2.2.1" />
+  </ItemGroup>
+</Project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/README.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/README.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/README.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,23 @@
+Java Unit Test README
+=====================
+
+This is the test suite for the Java bindings to the MapGuide API.
+
+This test suite exercises the enhanced Java wrapper (MapGuideApiEx.jar) and not the original (MapGuideApi.jar)
+
+Requirements
+============
+
+ - You must have already built the Oem/Server/Web components of MapGuide
+ - Apache Ant
+ - ant-contrib 1.0b2 or higher
+
+Running the tests
+=================
+
+ 1. Start the MapGuide Server process
+ 2. Run the following ant command: ant check
+
+Unit tests results will be logged to unittest.log
+
+The default ant build.xml properties is configured for 64-bit release configuration of MapGuide. Where applicable, you should override these properties to match your specific build configuration.
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/build.xml
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/build.xml	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/build.xml	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<project name="MgUnitTest" default="dist" basedir=".">
+  <description>MgUnitTest project file</description>
+  <!-- Must install ant-contrib 1.0b2 or higher -->
+  <taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
+  <condition property="unix">
+    <os family="unix" />
+  </condition>
+  <condition property="windows">
+    <os family="windows" />
+  </condition>
+  <!-- set global properties for this build -->
+  <property name="src" location="src" />
+  <property name="build" location="build" />
+  <property name="dist"  location="../java_dist" />
+  <property name="classpath" location="lib" />
+  <property name="extlib" location="extlib" />
+  <property name="build.target" location="64" />
+  <property name="binsrc.web" location="../../../Web/bin/release64" />
+  <property name="jarsrc.web" location="../../../Web/src/WEB-INF/lib" />
+  <property name="mapguide.res.src" location="../../../Common/MapGuideCommon/Resources/mapguide_en.res" />
+  <property name="mapguide.config.src" location="../../../Web/src/webconfig.ini" />
+  <property name="mapguide.res" location="${dist}/Resources/mapguide_en.res" />
+  <property name="mapguide.config" location="${dist}/webconfig.ini" />
+  <property name="mapguide.dictpath" location="../../../Oem/CsMap/Dictionaries" />
+  <property name="unittest.logpath" location="${dist}/unittest.log" />
+  <!-- Linux-specific -->
+  <property name="mapguide.ldpath" location="/usr/local/mapguideopensource-3.0.0/lib:/usr/local/mapguideopensource-3.0.0/webserverextensions/lib" />
+  <path id="master-classpath">
+    <fileset dir="${classpath}">
+      <include name="*.jar" />
+    </fileset>
+  </path>
+  <target name="initwin" if="windows">
+    <echo message="Init build environment for Windows" />
+    <!-- Create the time stamp -->
+    <tstamp/>
+    <!-- Create the build directory structure used by compile -->
+    <mkdir dir="${build}"/>
+    <!-- Copy required jars and dlls -->
+    <copy todir="${classpath}">
+      <fileset dir="${jarsrc.web}">
+        <include name="**/MapGuideApi.jar" />
+      </fileset>
+    </copy>
+    <copy todir="${classpath}">
+      <fileset dir="${extlib}">
+        <include name="**/sqlite4java.jar" />
+      </fileset>
+    </copy>
+    <condition property="binsrc.web" value="../../../Web/bin/release">
+      <equals arg1="${build.target}" arg2="32" />
+    </condition>
+    <condition property="binsrc.web" value="../../../Web/bin/release64">
+      <equals arg1="${build.target}" arg2="64" />
+    </condition>
+    <echo message="Copying dlls from ${binsrc.web}" />
+    <copy todir="${classpath}">
+      <fileset dir="${binsrc.web}">
+        <include name="**/Mg*.dll" />
+        <include name="**/ACE.dll" />
+        <include name="**/GEOS.dll" />
+        <include name="**/lib_json.dll" />
+        <include name="**/MapGuideJavaApi.dll" />
+        <include name="**/xerces-c_3_1mg.dll" />
+      </fileset>
+    </copy>
+  </target>
+  <target name="initunix" if="unix">
+    <echo message="Init build environment for Unix" />
+    <!-- Create the time stamp -->
+    <tstamp/>
+    <!-- Create the build directory structure used by compile -->
+    <mkdir dir="${build}"/>
+    <!-- Copy required jars and dlls -->
+    <copy todir="${classpath}">
+      <fileset dir="${jarsrc.web}">
+        <include name="**/MapGuideApi.jar" />
+      </fileset>
+    </copy>
+    <copy todir="${classpath}">
+      <fileset dir="${extlib}">
+        <include name="**/sqlite4java.jar" />
+      </fileset>
+    </copy>
+  </target>
+  <target name="compile" depends="initwin,initunix"
+        description="compile the source " >
+    <!-- Compile the java code from ${src} into ${build} referencing our MapGuide API jar -->
+    <javac srcdir="${src}" destdir="${build}" includeantruntime="false" debug="true" debuglevel="lines,vars,source">
+      <classpath>
+        <pathelement path="${classpath}" />
+        <fileset dir="${classpath}">
+          <include name="**/MapGuideApi.jar" />
+          <include name="**/sqlite4java.jar" />
+        </fileset>
+      </classpath>
+    </javac>
+  </target>
+  <target name="dist" depends="compile"
+        description="generate the distribution" >
+    <!-- Create the distribution directory -->
+    <mkdir dir="${dist}"/>
+    <!-- Make the unit test jar ensuring the MapGuide API jar reference is intact -->
+    <jar jarfile="${dist}/MgUnitTest.jar">
+      <fileset dir="${build}" />
+      <manifest>
+        <attribute name="Main-Class" value="org.osgeo.mapguide.test.Program" />
+        <attribute name="Class-Path" value="MapGuideApi.jar sqlite4java.jar" />
+      </manifest>
+    </jar>
+    <copy todir="${dist}" verbose="true">
+      <fileset dir="${classpath}">
+        <include name="**/*" />
+      </fileset>
+    </copy>
+    <copy todir="${dist}" verbose="true">
+      <fileset dir="${extlib}">
+        <include name="**/*.dll" />
+        <include name="**/*.so" />
+      </fileset>
+    </copy>
+    <copy file="${mapguide.res.src}" tofile="${mapguide.res}" verbose="true" />
+    <copy file="${mapguide.config.src}" tofile="${mapguide.config}" verbose="true" overwrite="true" />
+  </target>
+  <target name="updatewebconfig">
+    <echo message="Updating ${mapguide.config} with default values" />
+    <!-- write webconfig.ini -->
+    <inifile dest="${mapguide.config}">
+      <set section="GeneralProperties" property="DefaultMessageLocale" value="en" />
+      <set section="GeneralProperties" property="LogsPath" value="${dist}/Logs" />
+      <set section="GeneralProperties" property="MentorDictionaryPath" value="${mapguide.dictpath}" />
+      <set section="GeneralProperties" property="ResourcesPath" value="${dist}/Resources" />
+      <set section="GeneralProperties" property="TcpIpMtu" value="1460" />
+      <set section="GeneralProperties" property="TempPath" value="${dist}/Temp" />
+      <set section="GeneralProperties" property="FailoverRetryTime" value="1" />
+      <set section="AdministrativeConnectionProperties" property="MaxConnections" value="2" />
+      <set section="AdministrativeConnectionProperties" property="Port" value="2810" />
+      <set section="ClientConnectionProperties" property="MaxConnections" value="12" />
+      <set section="ClientConnectionProperties" property="Port" value="2811" />
+      <set section="SiteConnectionProperties" property="IpAddress" value="127.0.0.1" />
+      <set section="SiteConnectionProperties" property="MaxConnections" value="6" />
+      <set section="SiteConnectionProperties" property="Port" value="2812" />
+      <set section="AgentProperties" property="DebugPause" value="0" />
+      <set section="AgentProperties" property="DisableAuthoring" value="0" />
+      <set section="AgentProperties" property="DisableWfs" value="0" />
+      <set section="AgentProperties" property="DisableWms" value="0" />
+      <set section="AgentProperties" property="ErrorLogEnabled" value="0" />
+      <set section="AgentProperties" property="ErrorLogFilename" value="Error.log" />
+      <set section="AgentProperties" property="RequestLogEnabled" value="0" />
+      <set section="AgentProperties" property="RequestLogFilename" value="Request.log" />
+      <set section="OgcProperties" property="WfsPassword" value="wfs" />
+      <set section="OgcProperties" property="WmsPassword" value="wms" />
+      <set section="OgcProperties" property="CITEWfsEnabled" value="0" />
+      <set section="OgcProperties" property="CITEWmsEnabled" value="0" />
+    </inifile>
+  </target>
+  <target name="checkwin_external" depends="dist">
+    <echo message="Run Java tests for Windows" />
+    <!-- Remove any log files and dumped binary files from previous run -->
+    <delete>
+      <fileset dir="${dist}" includes="**/*.bin"/>
+      <fileset dir="${dist}" includes="**/*.log"/>
+    </delete>
+    <!--
+    <exec executable="java" dir="${dist}">
+      <arg line="-jar MgUnitTest.jar ${mapguide.config} ${unittest.logpath}" />
+      <env key="MENTOR_DICTIONARY_PATH" value="${mapguide.dictpath}" />
+    </exec>
+    -->
+    <java jar="${dist}/MgUnitTest.jar" fork="true" dir="${dist}">
+      <arg value="${mapguide.config}" />
+      <arg value="${unittest.logpath}" />
+      <env key="MENTOR_DICTIONARY_PATH" value="${mapguide.dictpath}" />
+    </java>
+  </target>
+  <target name="checkwin" depends="dist,updatewebconfig">
+    <echo message="Run Java tests for Windows" />
+    <!-- Remove any log files and dumped binary files from previous run -->
+    <delete>
+      <fileset dir="${dist}" includes="**/*.bin"/>
+      <fileset dir="${dist}" includes="**/*.log"/>
+    </delete>
+    <!--
+    <exec executable="java" dir="${dist}">
+      <arg line="-jar MgUnitTest.jar ${mapguide.config} ${unittest.logpath}" />
+      <env key="MENTOR_DICTIONARY_PATH" value="${mapguide.dictpath}" />
+    </exec>
+    -->
+    <java jar="${dist}/MgUnitTest.jar" fork="true" dir="${dist}">
+      <arg value="${mapguide.config}" />
+      <arg value="${unittest.logpath}" />
+      <env key="MENTOR_DICTIONARY_PATH" value="${mapguide.dictpath}" />
+    </java>
+  </target>
+  <target name="checkunix" depends="dist,updatewebconfig">
+    <echo message="Run Java tests for Unix" />
+    <!-- Remove any log files and dumped binary files from previous run -->
+    <delete>
+      <fileset dir="${dist}" includes="**/*.bin"/>
+      <fileset dir="${dist}" includes="**/*.log"/>
+    </delete>
+    <echo message="MENTOR_DICTIONARY_PATH is: ${mapguide.dictpath}" />
+    <echo message="java.library.path is: ${mapguide.ldpath}" />
+    <java jar="${dist}/MgUnitTest.jar" fork="true" dir="${dist}">
+      <arg value="${mapguide.config}" />
+      <arg value="${unittest.logpath}" />
+      <env key="MENTOR_DICTIONARY_PATH" value="${mapguide.dictpath}" />
+      <sysproperty key="java.library.path" value="${mapguide.ldpath}" />
+    </java>
+    <!--
+    <exec executable="java" dir="${dist}">
+      <arg value="-Djava.library.path=${mapguide.ldpath}" />
+      <arg value="-jar MgUnitTest.jar" />   
+      <arg value="${mapguide.config}" />
+      <arg value="${unittest.logpath}" />
+      <env key="LD_LIBRARY_PATH" value="${mapguide.ldpath}" />
+      <env key="MENTOR_DICTIONARY_PATH" value="${mapguide.dictpath}" />
+    </exec>
+    -->
+  </target>
+  <target name="clean"
+        description="clean up" >
+    <!-- Delete the ${build} and ${dist} directory trees -->
+    <delete dir="${build}"/>
+    <delete dir="${dist}"/>
+    <delete dir="${classpath}"/>
+  </target>
+</project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java-win32-x64.dll
===================================================================
(Binary files differ)

Index: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java-win32-x64.dll
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java-win32-x64.dll	2020-11-06 15:49:38 UTC (rev 9756)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java-win32-x64.dll	2020-11-06 16:04:14 UTC (rev 9757)

Property changes on: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java-win32-x64.dll
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java-win32-x86.dll
===================================================================
(Binary files differ)

Index: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java-win32-x86.dll
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java-win32-x86.dll	2020-11-06 15:49:38 UTC (rev 9756)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java-win32-x86.dll	2020-11-06 16:04:14 UTC (rev 9757)

Property changes on: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java-win32-x86.dll
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java.jar
===================================================================
(Binary files differ)

Index: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java.jar
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java.jar	2020-11-06 15:49:38 UTC (rev 9756)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java.jar	2020-11-06 16:04:14 UTC (rev 9757)

Property changes on: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/extlib/sqlite4java.jar
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/ApiTypes.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/ApiTypes.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/ApiTypes.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,7 @@
+package org.osgeo.mapguide.test;
+
+public class ApiTypes
+{
+    public static final String Platform = "Api";
+    public static final String Http = "Http";
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/CommonTests.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/CommonTests.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/CommonTests.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,68 @@
+package org.osgeo.mapguide.test;
+
+import java.util.*;
+
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class CommonTests
+{
+    public static int Execute(IPlatformFactory factory, ITestLogger logger, MutableInteger testsRun) {
+        int failures = 0;
+        Vector<IExternalTest> tests = new Vector<IExternalTest>();
+
+        //--------------- BEGIN Test Registration -----------------//
+        tests.add(new ByteReaderTest());
+        tests.add(new ByteReaderMemoryConstructorTest());
+        tests.add(new ByteReaderStringConstructorTest());
+        tests.add(new BatchPropertyCollectionTest());
+        tests.add(new ClassDefinitionCollectionTest());
+        tests.add(new CoordinateCollectionTest());
+        tests.add(new CurvePolygonCollectionTest());
+        tests.add(new CurveRingCollectionTest());
+        tests.add(new CurveSegmentCollectionTest());
+        tests.add(new CurveStringCollectionTest());
+        tests.add(new FeatureCommandCollectionTest());
+        tests.add(new FeatureSchemaCollectionTest());
+        tests.add(new GeometryCollectionTest());
+        tests.add(new IntCollectionTest());
+        tests.add(new LayerCollectionTest());
+        tests.add(new LayerGroupCollectionTest());
+        tests.add(new LinearRingCollectionTest());
+        tests.add(new LineStringCollectionTest());
+        tests.add(new MapCollectionTest());
+        tests.add(new PropertyCollectionTest());
+        tests.add(new PropertyDefinitionCollectionTest());
+        tests.add(new StringCollectionTest());
+        tests.add(new StringPropertyCollectionTest());
+        //--------------- END Test Registration -------------------//
+
+        for (IExternalTest test : tests)
+        {
+            try
+            {
+                String clsName = CommonUtility.GetTypeName(test);
+                logger.WriteLine("****** Executing platform test: " + clsName + " *********");
+                Console.WriteLine("Executing external platform test: " + clsName);
+                test.Execute(factory, logger);
+            }
+            catch (AssertException ex)
+            {
+                logger.WriteLine("Assertion failure: " + ex.getMessage());
+                Console.WriteLine("Assertion failure: " + ex.getMessage());
+                failures++;
+            }
+            catch (Exception ex)
+            {
+                logger.WriteLine("General failure: " + CommonUtility.ExceptionToString(ex));
+                Console.WriteLine("General failure: " + CommonUtility.ExceptionToString(ex));
+                failures++;
+            }
+            finally
+            {
+                testsRun.increment();
+            }
+        }
+        return failures;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/Console.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/Console.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/Console.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,25 @@
+package org.osgeo.mapguide.test;
+
+// .net console shim
+public class Console
+{
+    public static void Write(String message)
+    {
+        System.out.print(message);
+    }
+
+    public static void Write(String format, Object ... args)
+    {
+        Write(String.format(format, args));
+    }
+
+    public static void WriteLine(String message)
+    {
+        System.out.println(message);
+    }
+
+    public static void WriteLine(String format, Object ... args)
+    {
+        WriteLine(String.format(format, args));
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/DebugSettings.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/DebugSettings.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/DebugSettings.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,6 @@
+package org.osgeo.mapguide.test;
+
+public class DebugSettings
+{
+    public static boolean Enabled = false;
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/MapGuideTestExecutorCollection.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/MapGuideTestExecutorCollection.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/MapGuideTestExecutorCollection.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,777 @@
+package org.osgeo.mapguide.test;
+
+import java.io.*;
+import java.util.*;
+import java.nio.charset.StandardCharsets;
+
+import com.almworks.sqlite4java.*;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.*;
+import javax.xml.transform.stream.*;
+import javax.xml.xpath.*;
+import org.w3c.dom.*;
+
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.operations.feature.*;
+import org.osgeo.mapguide.test.platform.operations.resource.*;
+import org.osgeo.mapguide.test.platform.operations.maplayer.*;
+import org.osgeo.mapguide.test.mapguide.operations.drawing.*;
+import org.osgeo.mapguide.test.mapguide.operations.admin.*;
+import org.osgeo.mapguide.test.mapguide.operations.site.*;
+import org.osgeo.mapguide.test.mapguide.operations.weblayout.*;
+
+public class MapGuideTestExecutorCollection extends TestExecutorCollectionBase
+{
+    private HashMap<String, ITestExecutor> _executors;
+
+    private MgUserInformation _userInfo;
+    private MgSiteConnection _conn;
+
+    public MapGuideTestExecutorCollection(MgUserInformation userInfo, MgSiteConnection conn)
+    {
+        _userInfo = userInfo;
+        _conn = conn;
+        _executors = new HashMap<String, ITestExecutor>();
+    }
+
+    protected void SetupExecutors(String dbPath)
+    {
+        //NOTE: We can't share the SqliteVm instance among our executor Objects as this messes up query results
+        //we must be able to re-create a new SqliteVm for each executor, so we pass down the db path
+
+        MgResourceService resSvc = (MgResourceService)_conn.createService(MgServiceType.ResourceService);
+        MgFeatureService featSvc = (MgFeatureService)_conn.createService(MgServiceType.FeatureService);
+        MgDrawingService drawSvc = (MgDrawingService)_conn.createService(MgServiceType.DrawingService);
+
+        
+        MgSite site = _conn.getSite();
+        MgServerAdmin admin = new MgServerAdmin();
+        admin.open(_userInfo);
+        MgWebLayoutCreator wlCreator = new MgWebLayoutCreator(resSvc);
+        MgMapCreator creator = new MgMapCreator(_conn, resSvc);
+        MgSessionCreator sessionCreator = new MgSessionCreator(_conn);
+        MgApplySession sessionApply = new MgApplySession(_userInfo);
+        MgSession session = new MgSession();
+
+        //Resource Service
+        _executors.put("ApplyResourcePackage".toUpperCase(), new ApplyResourcePackage(resSvc, dbPath));
+        _executors.put("ChangeResourceOwner".toUpperCase(), new ChangeResourceOwner(resSvc, dbPath));
+        _executors.put("CopyResource".toUpperCase(), new CopyResource(resSvc, dbPath));
+        _executors.put("DeleteResource".toUpperCase(), new DeleteResource(resSvc, dbPath));
+        _executors.put("DeleteResourceData".toUpperCase(), new DeleteResourceData(resSvc, dbPath));
+        _executors.put("EnumerateResourceData".toUpperCase(), new EnumerateResourceData(resSvc, dbPath));
+        _executors.put("EnumerateResourceReferences".toUpperCase(), new EnumerateResourceReferences(resSvc, dbPath));
+        _executors.put("EnumerateResources".toUpperCase(), new EnumerateResources(resSvc, dbPath));
+        _executors.put("GetRepositoryContent".toUpperCase(), new GetRepositoryContent(resSvc, dbPath));
+        _executors.put("GetRepositoryHeader".toUpperCase(), new GetRepositoryHeader(resSvc, dbPath));
+        _executors.put("GetResourceContent".toUpperCase(), new GetResourceContent(resSvc, dbPath));
+        _executors.put("GetResourceData".toUpperCase(), new GetResourceData(resSvc, dbPath));
+        _executors.put("GetResourceHeader".toUpperCase(), new GetResourceHeader(resSvc, dbPath));
+        _executors.put("InheritPermissionsFrom".toUpperCase(), new InheritPermissionsFrom(resSvc, dbPath));
+        _executors.put("MoveResource".toUpperCase(), new MoveResource(resSvc, dbPath));
+        _executors.put("RenameResourceData".toUpperCase(), new RenameResourceData(resSvc, dbPath));
+        _executors.put("SetResource".toUpperCase(), new SetResource(resSvc, dbPath));
+        _executors.put("SetResourceData".toUpperCase(), new SetResourceData(resSvc, dbPath));
+        _executors.put("UpdateRepository".toUpperCase(), new UpdateRepository(resSvc, dbPath));
+        
+        //Feature Service
+        _executors.put("DescribeFeatureSchema".toUpperCase(), new DescribeFeatureSchema(featSvc, dbPath));
+        _executors.put("ExecuteSqlQuery".toUpperCase(), new ExecuteSqlQuery(featSvc, dbPath));
+        _executors.put("GetClasses".toUpperCase(), new GetClasses(featSvc, dbPath));
+        _executors.put("GetConnectionPropertyValues".toUpperCase(), new GetConnectionPropertyValues(featSvc, dbPath));
+        _executors.put("GetFeatureProviders".toUpperCase(), new GetFeatureProviders(featSvc, dbPath));
+        _executors.put("GetLongTransactions".toUpperCase(), new GetLongTransactions(featSvc, dbPath));
+        _executors.put("GetProviderCapabilities".toUpperCase(), new GetProviderCapabilities(featSvc, dbPath));
+        _executors.put("GetSchemas".toUpperCase(), new GetSchemas(featSvc, dbPath));
+        _executors.put("GetSpatialContexts".toUpperCase(), new GetSpatialContexts(featSvc, dbPath));
+        _executors.put("SelectAggregates".toUpperCase(), new SelectAggregates(featSvc, dbPath));
+        _executors.put("SelectFeatures".toUpperCase(), new SelectFeatures(featSvc, dbPath));
+        _executors.put("SetLongTransaction".toUpperCase(), new SetLongTransaction(featSvc, dbPath, sessionCreator, sessionApply));
+        _executors.put("TestConnection".toUpperCase(), new TestConnection(featSvc, dbPath));
+
+        //Map and Layers
+        _executors.put("AddLayerGroup".toUpperCase(), new AddLayerGroup(resSvc, dbPath, creator));
+        _executors.put("AddLayer".toUpperCase(), new AddLayer(resSvc, dbPath, creator));
+        _executors.put("GetCoordinateSystem".toUpperCase(), new GetCoordinateSystem(resSvc, dbPath, creator));
+        _executors.put("GetDataExtent".toUpperCase(), new GetDataExtent(resSvc, dbPath, creator));
+        _executors.put("GetDisplayInLegend".toUpperCase(), new GetDisplayInLegend(resSvc, dbPath, creator));
+        _executors.put("GetLayerFeatureClass".toUpperCase(), new GetLayerFeatureClass(resSvc, dbPath, creator));
+        _executors.put("GetLayerFeatureSource".toUpperCase(), new GetLayerFeatureSource(resSvc, dbPath, creator));
+        _executors.put("GetLayerDefinition".toUpperCase(), new GetLayerDefinition(resSvc, dbPath, creator));
+        _executors.put("GetGroups".toUpperCase(), new GetGroups(resSvc, dbPath, creator));
+        _executors.put("GetLayers".toUpperCase(), new GetLayers(resSvc, dbPath, creator));
+        _executors.put("GetLayerVisibility".toUpperCase(), new GetLayerVisibility(resSvc, dbPath, creator));
+        _executors.put("GetLegendLabel".toUpperCase(), new GetLegendLabel(resSvc, dbPath, creator));
+        _executors.put("GetMapExtent".toUpperCase(), new GetMapExtent(resSvc, dbPath, creator));
+        _executors.put("GetMapName".toUpperCase(), new GetMapName(resSvc, dbPath, creator));
+        _executors.put("GetViewCenter".toUpperCase(), new GetViewCenter(resSvc, dbPath, creator));
+        _executors.put("GetViewScale".toUpperCase(), new GetViewScale(resSvc, dbPath, creator));
+        _executors.put("HideGroup".toUpperCase(), new HideGroup(resSvc, dbPath, creator));
+        _executors.put("IsLayerVisible".toUpperCase(), new IsLayerVisible(resSvc, dbPath, creator));
+        _executors.put("LayerExists".toUpperCase(), new LayerExists(resSvc, dbPath, creator));
+        _executors.put("RemoveGroup".toUpperCase(), new RemoveGroup(resSvc, dbPath, creator));
+        _executors.put("ShowGroup".toUpperCase(), new ShowGroup(resSvc, dbPath, creator));
+
+        //Drawing Service
+        _executors.put("DescribeDrawing".toUpperCase(), new DescribeDrawing(drawSvc, dbPath));
+        _executors.put("GetDrawing".toUpperCase(), new GetDrawing(drawSvc, dbPath));
+        _executors.put("EnumerateDrawingLayers".toUpperCase(), new EnumerateDrawingLayers(drawSvc, dbPath));
+        _executors.put("GetDrawingLayer".toUpperCase(), new GetDrawingLayer(drawSvc, dbPath));
+        _executors.put("GetDrawingSection".toUpperCase(), new GetDrawingSection(drawSvc, dbPath));
+        _executors.put("EnumerateDrawingSections".toUpperCase(), new EnumerateDrawingSections(drawSvc, dbPath));
+        _executors.put("EnumerateDrawingSectionResources".toUpperCase(), new EnumerateDrawingSectionResources(drawSvc, dbPath));
+        _executors.put("GetDrawingSectionResource".toUpperCase(), new GetDrawingSectionResource(drawSvc, dbPath));
+
+        //Mapping Service
+
+        //Rendering Service
+
+        //Server Admin
+        _executors.put("Offline".toUpperCase(), new Offline(admin, dbPath));
+        _executors.put("Online".toUpperCase(), new Online(admin, dbPath));
+        _executors.put("GetLog".toUpperCase(), new GetLog(admin, dbPath));
+        _executors.put("GetLogByDate".toUpperCase(), new GetLogByDate(admin, dbPath));
+        _executors.put("ClearLog".toUpperCase(), new ClearLog(admin, dbPath));
+        _executors.put("DeleteLog".toUpperCase(), new DeleteLog(admin, dbPath));
+        _executors.put("RenameLog".toUpperCase(), new RenameLog(admin, dbPath));
+        _executors.put("EnumeratePackages".toUpperCase(), new EnumeratePackages(admin, dbPath));
+        _executors.put("DeletePackage".toUpperCase(), new DeletePackage(admin, dbPath));
+        _executors.put("LoadPackage".toUpperCase(), new LoadPackage(admin, dbPath));
+        _executors.put("GetPackageStatus".toUpperCase(), new GetPackageStatus(admin, dbPath));
+        _executors.put("GetPackageLog".toUpperCase(), new GetPackageLog(admin, dbPath));
+
+        //Site Service
+        _executors.put("CreateSession".toUpperCase(), new CreateSession(site, dbPath, session));
+        _executors.put("DestroySession".toUpperCase(), new DestroySession(site, dbPath));
+        _executors.put("GetUserForSession".toUpperCase(), new GetUserForSession(site, dbPath, session));
+        _executors.put("EnumerateUsers".toUpperCase(), new EnumerateUsers(site, dbPath));
+        _executors.put("AddUser".toUpperCase(), new AddUser(site, dbPath));
+        _executors.put("UpdateUser".toUpperCase(), new UpdateUser(site, dbPath));
+        _executors.put("DeleteUsers".toUpperCase(), new DeleteUsers(site, dbPath));
+        _executors.put("GrantRoleMembershipsToUsers".toUpperCase(), new GrantRoleMembershipsToUsers(site, dbPath));
+        _executors.put("RevokeRoleMembershipsFromUsers".toUpperCase(), new RevokeRoleMembershipsFromUsers(site, dbPath));
+        _executors.put("GrantGroupMembershipsToUsers".toUpperCase(), new GrantGroupMembershipsToUsers(site, dbPath));
+        _executors.put("RevokeGroupMembershipsFromUsers".toUpperCase(), new RevokeGroupMembershipsFromUsers(site, dbPath));
+        _executors.put("EnumerateGroups".toUpperCase(), new EnumerateGroups(site, dbPath));
+        _executors.put("EnumerateGroups2".toUpperCase(), new EnumerateGroups2(site, dbPath));
+        _executors.put("EnumerateRoles2".toUpperCase(), new EnumerateRoles2(site, dbPath));
+        _executors.put("AddGroup".toUpperCase(), new AddGroup(site, dbPath));
+        _executors.put("UpdateGroup".toUpperCase(), new UpdateGroup(site, dbPath));
+        _executors.put("DeleteGroups".toUpperCase(), new DeleteGroups(site, dbPath));
+        _executors.put("GrantRoleMembershipsToGroups".toUpperCase(), new GrantRoleMembershipsToGroups(site, dbPath));
+        _executors.put("RevokeRoleMembershipsFromGroups".toUpperCase(), new RevokeRoleMembershipsFromGroups(site, dbPath));
+        _executors.put("EnumerateRoles".toUpperCase(), new EnumerateRoles(site, dbPath));
+        _executors.put("EnumerateServers".toUpperCase(), new EnumerateServers(site, dbPath));
+        _executors.put("AddServer".toUpperCase(), new AddServer(site, dbPath));
+        _executors.put("UpdateServer".toUpperCase(), new UpdateServer(site, dbPath));
+        _executors.put("RemoveServer".toUpperCase(), new RemoveServer(site, dbPath));
+
+        //Web Layout
+        _executors.put("WL_GetTitle".toUpperCase(), new WL_GetTitle(wlCreator, dbPath));
+        _executors.put("WL_GetMapDefinition".toUpperCase(), new WL_GetMapDefinition(wlCreator, dbPath));
+        _executors.put("WL_GetScale".toUpperCase(), new WL_GetScale(wlCreator, dbPath));
+        _executors.put("WL_GetCenter".toUpperCase(), new WL_GetCenter(wlCreator, dbPath));
+        _executors.put("WL_ShowToolbar".toUpperCase(), new WL_ShowToolbar(wlCreator, dbPath));
+        _executors.put("WL_ShowStatusbar".toUpperCase(), new WL_ShowStatusbar(wlCreator, dbPath));
+        _executors.put("WL_ShowTaskpane".toUpperCase(), new WL_ShowTaskpane(wlCreator, dbPath));
+        _executors.put("WL_ShowTaskbar".toUpperCase(), new WL_ShowTaskbar(wlCreator, dbPath));
+        _executors.put("WL_ShowLegend".toUpperCase(), new WL_ShowLegend(wlCreator, dbPath));
+        _executors.put("WL_ShowProperties".toUpperCase(), new WL_ShowProperties(wlCreator, dbPath));
+        _executors.put("WL_GetTaskPaneWidth".toUpperCase(), new WL_GetTaskPaneWidth(wlCreator, dbPath));
+        _executors.put("WL_GetInformationPaneWidth".toUpperCase(), new WL_GetInformationPaneWidth(wlCreator, dbPath));
+        _executors.put("WL_GetInitialTaskUrl".toUpperCase(), new WL_GetInitialTaskUrl(wlCreator, dbPath));
+        _executors.put("WL_ShowContextMenu".toUpperCase(), new WL_ShowContextMenu(wlCreator, dbPath));
+        _executors.put("WL_TestUiItem".toUpperCase(), new WL_TestUiItem(wlCreator, dbPath));
+        _executors.put("WL_HomeTooltip".toUpperCase(), new WL_HomeTooltip(wlCreator, dbPath));
+        _executors.put("WL_HomeDescription".toUpperCase(), new WL_HomeDescription(wlCreator, dbPath));
+        _executors.put("WL_BackTooltip".toUpperCase(), new WL_BackTooltip(wlCreator, dbPath));
+        _executors.put("WL_BackDescription".toUpperCase(), new WL_BackDescription(wlCreator, dbPath));
+        _executors.put("WL_ForwardTooltip".toUpperCase(), new WL_ForwardTooltip(wlCreator, dbPath));
+        _executors.put("WL_ForwardDescription".toUpperCase(), new WL_ForwardDescription(wlCreator, dbPath));
+        _executors.put("WL_TasksName".toUpperCase(), new WL_TasksName(wlCreator, dbPath));
+        _executors.put("WL_TasksTooltip".toUpperCase(), new WL_TasksTooltip(wlCreator, dbPath));
+        _executors.put("WL_TasksDescription".toUpperCase(), new WL_TasksDescription(wlCreator, dbPath));
+    }
+
+    class MgWebLayoutCreator implements IWebLayoutCreator
+    {
+        private MgResourceService _resSvc;
+        private MgWebLayout _wl;
+
+        public MgWebLayoutCreator(MgResourceService resSvc)
+        {
+            _resSvc = resSvc;
+        }
+
+        public MgWebLayout CreateWebLayout(MgResourceIdentifier resId)
+        {
+            if (_wl != null)
+                return _wl;
+
+            _wl = new MgWebLayout(_resSvc, resId);
+            return _wl;
+        }
+    }
+
+    class MgMapCreator implements IMapCreator
+    {
+        private MgSiteConnection _siteConn;
+        private MgMap _map;
+        private MgResourceService _resSvc;
+
+        public MgMapCreator(MgSiteConnection siteConn, MgResourceService resSvc)
+        {
+            _siteConn = siteConn;
+            _resSvc = resSvc;
+        }
+
+        public MgMapBase CreateMap(MgResourceIdentifier resId)
+        {
+            //The test suite relies on the same map and not a new one
+            //re-created on each test of that particular test suite
+            if (_map != null)
+              return _map;
+
+            _map = new MgMap(_siteConn);
+            _map.create(resId, resId.getName());
+            return _map;
+        }
+
+        public MgLayerBase CreateLayer(MgResourceIdentifier resId)
+        {
+            return new MgLayer(resId, _resSvc);
+        }
+    }
+
+    class MgSessionCreator implements ISessionCreator
+    {
+        private MgSiteConnection _siteConn;
+
+        public MgSessionCreator(MgSiteConnection siteConn)
+        {
+            _siteConn = siteConn;
+        }
+
+        public String CreateSession()
+        {
+            MgSite site = _siteConn.getSite();
+            return site.createSession();
+        }
+    }
+
+    class MgApplySession implements IApplySession
+    {
+        private MgUserInformation _userInfo;
+
+        public MgApplySession(MgUserInformation userInfo)
+        {
+            _userInfo = userInfo;
+        }
+
+        public void SetSessionId(String sessionId)
+        {
+            _userInfo.setMgSessionId(sessionId);
+        }
+    }
+
+    class MgSession implements IMapGuideSession
+    {
+        private String _value;
+
+        public String getSessionID() { return _value; }
+        public void setSessionID(String value) { _value = value; }
+    }
+
+    public ITestExecutor GetTestExecutor(String opName)
+    {
+        if (_executors.containsKey(opName))
+            return _executors.get(opName);
+        throw new MissingTestExecutorException(opName);
+    }
+
+    public int ValidateRequest(SQLiteConnection db, String testName, int paramSetId, String operation, TestResult actualResult, ITestLogger logger)
+    {
+        int exitStatus = 0;
+
+        String outcome = "pass";
+
+        Object expectedResult = null;
+
+        //If we have an exception we need to remove the stack trace because different line numbers will fail the test
+        Object resultData = CommonUtility.RemoveStackTraceFromResult(actualResult.ResultData);
+        try {    
+            //Get the mime type based on the content type in the result
+            String mimeType = actualResult.ContentType;
+            //If we have exception message we need to remove any parts that may contain system dependent information
+            //Ex. file paths
+            resultData = CommonUtility.ProcessExceptionMessage(resultData);
+            //Get the file extension that will be used for a dump
+            String actualExtension = CommonUtility.GetExtensionFromMimeType(mimeType);
+
+            SQLiteStatement stmt = db.prepare(String.format("Select ParamValue from Params where ParamName=\"ALWAYSPASS\" and ParamSet=%d", paramSetId));
+
+            if (DebugSettings.Enabled) {
+                Console.WriteLine("\t\tTest Result");
+                Console.WriteLine("\t\t\thas return value: " + actualResult.HasReturnValue);
+                Console.WriteLine("\t\t\tis exception: " + actualResult.IsException);
+            }
+
+            //If we have an ALWAYSPASS parameter defined for the operation then skip the whole validation process
+            //This parameter should only be used for clean up operations that are no related with the tests
+            if (!stmt.step())
+            {
+                //TestName is Test_[ServiceType]
+                String type = testName.substring(testName.indexOf("_") + 1);
+                String filePath = CommonUtility.GetPath(String.format("../../TestData/{0}/DumpFiles/{0}ApiTest", type));
+                String fileName = String.format("{0}_{1}.{2}", filePath, paramSetId, actualExtension);
+
+                if (this.TestExecutionMode == "dump")
+                {
+                    //File.WriteAllText(fileName, resultData);
+                    throw new RuntimeException("The Java test runner does not support dumping of test results. Please use the PHP test runner for this purpose");
+                }
+                else
+                {
+                    //This section is special case handling for the operations that return different data after each call
+                    resultData = CommonUtility.SpecialDataHandling(operation, resultData, mimeType);
+
+                    if (this.TestExecutionMode == "validate" || this.TestExecutionMode == "show")
+                    {   
+                        String resultContent = "";
+
+                        //Get the sample result and the expected content type from the database
+                        SQLiteStatement rstmt = db.prepare(String.format("Select Result, ContentType from ApiTestResults where ParamSet=%d", paramSetId));
+                        if (rstmt.step())
+                        {
+                            String expectedContentType = CommonUtility.GetStringFromStatement(rstmt, "ContentType");
+                            String expectedExtension = CommonUtility.GetExtensionFromMimeType(expectedContentType);
+                            byte[] b = CommonUtility.GetByteArrayFromStatement(rstmt, "Result");
+                            if (b != null)
+                            {
+                                if (expectedExtension == "xml" || expectedExtension == "txt" || expectedExtension == "html")
+                                    expectedResult = new String(b, StandardCharsets.UTF_8);
+                                else
+                                    expectedResult = b;
+                            }
+                            else
+                            {
+                                if (expectedExtension == "xml" || expectedExtension == "txt" || expectedExtension == "html")
+                                    expectedResult = "";
+                                else
+                                    expectedResult = null;
+                            }
+                            String strExpectedResult = null;
+                            if (expectedResult instanceof String)
+                                strExpectedResult = (String)expectedResult;
+                            //If we are validating from a file then get the contents of that file
+                            //File names should be prefixed with "@@" to distinguish them from BLOB data
+                            if (strExpectedResult != null && strExpectedResult.startsWith("@@"))
+                            {
+                                String sampleDataFile = strExpectedResult.substring(2);
+                                sampleDataFile = CommonUtility.GetPath(sampleDataFile);
+                                expectedResult = CommonUtility.ReadAllText(sampleDataFile);
+                            }
+
+                            if (this.TestExecutionMode == "validate")
+                            {
+                                boolean bEqual = false;
+                                byte[] bExpected = null;
+                                if (expectedResult instanceof byte[])
+                                    bExpected = (byte[])expectedResult;
+                                byte[] bActual = null;
+                                if (resultData instanceof byte[])
+                                    bActual = (byte[])resultData;
+                                String strResultData = null;
+                                if (resultData instanceof String)
+                                    strResultData = (String)resultData;
+
+                                //FIXME: We're not processing DWF content properly to do this check properly. So just
+                                //pass these for now
+                                //
+                                //DESCRIBEFEATURESCHEMA is auto-pass because I haven't figured out how to parse XML with qualified
+                                //elements with Java's woeful XML APIs.
+                                if (operation.equals("GETDRAWINGLAYER") || operation.equals("GETDRAWINGSECTION") || operation.equals("DESCRIBEFEATURESCHEMA"))
+                                {
+                                    bEqual = true;
+                                }
+                                else
+                                {
+                                    if (strExpectedResult != null && strResultData != null)
+                                    {
+                                        if (DebugSettings.Enabled)
+                                            Console.WriteLine("[DEBUG] - Validating string result");
+                                        //Normalize line endings on LF before comparsion (in case the SQLite GUI recorded CRLFs)
+                                        String normStrResultData = strResultData.replace("\r\n", "\n");
+                                        String normStrExpectedResult = strExpectedResult.replace("\r\n", "\n");
+
+                                        bEqual = normStrResultData.equalsIgnoreCase(normStrExpectedResult);
+
+                                        if (!bEqual) {
+                                            XmlCompareResult result = TryXmlCompare(normStrExpectedResult, normStrResultData);
+                                            bEqual = result.Result;
+                                            if (!bEqual && result.HasXml()) {
+                                                //Update expected/actual with normalized versions for display
+                                                expectedResult = result.Expected;
+                                                resultData = result.Actual;
+                                                if (DebugSettings.Enabled)
+                                                    Console.WriteLine("Expected: <<<\n" + result.Expected + "\nActual: <<<\n" + result.Actual + "\n<<<<<<<<<<<<\n");
+                                            }
+                                        }
+                                    }
+                                    else if (bExpected != null && bActual != null)
+                                    {
+                                        if (DebugSettings.Enabled)
+                                            Console.WriteLine("[DEBUG] - Validating byte array results");
+                                        bEqual = CommonUtility.ByteArraysEqual(bExpected, bActual, operation, testName);
+                                    }
+                                    else
+                                    {
+                                        if (DebugSettings.Enabled)
+                                            Console.WriteLine(String.format("[MgTestRunner]: %s - %s - Encountered disparate data types between expected and actual results. Expecting test failure :(", testName, operation));
+                                    }
+                                }
+                                //If the results are different and special validation fails then the operation failed ->mark it red
+                                if (!bEqual && !CommonUtility.SpecialValidation(operation, resultData, expectedResult))
+                                {
+                                    //But only if this is not a void operation
+                                    if (actualResult.HasReturnValue)
+                                    {
+                                        outcome = "fail";
+                                        exitStatus = 1;
+
+                                        if (!expectedExtension.equals("xml") && !expectedExtension.equals("html") && !expectedExtension.equals("txt"))
+                                        {
+                                            expectedResult = "Unable to display binary data";
+                                        }
+
+                                        if (!actualExtension.equals("xml") && !actualExtension.equals("html") && !actualExtension.equals("txt"))
+                                        {
+                                            resultData = "Unable to display binary data";
+                                        }
+                                    }
+                                }
+                            }
+                            else
+                            {
+                                throw new RuntimeException("The Java test runner does not support the given test execution mode (" + this.TestExecutionMode + "). Please use the PHP test runner for this purpose");
+                            }
+                        }
+                        else
+                        {
+                            if (DebugSettings.Enabled)
+                                Console.WriteLine("\t\tNo result to compare against");
+                        }
+                    }
+                }
+            }
+            else
+            {
+                if (DebugSettings.Enabled) {
+                    String pval = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+                    Console.WriteLine("\t\tALWAYSPASS: " + pval);
+                }
+            }
+        } catch (Exception ex) {
+            Console.WriteLine("Exception validating response: %s", CommonUtility.ExceptionToString(ex));
+            outcome = "fail";
+            exitStatus = 1;
+        }
+
+        if (outcome.equals("fail"))
+        {
+            Console.WriteLine("****%s %d %s failed.\n", testName, paramSetId, operation);
+            String str = String.format("\n****ACTUAL RESULT****\n%s\n****EXPECTED RESULT****\n%s\n********\n\n\n", resultData, expectedResult);
+            logger.Write(str);
+            Console.WriteLine(str);
+        }
+
+        return exitStatus;
+    }
+
+    private static String SortElement(Node elem, String preText)
+    {
+        Vector<String> elemArray = new Vector<String>();
+        String elemString = "";
+        NodeList childNodes = elem.getChildNodes();
+        if (childNodes.getLength() > 0)
+        {
+            int elCount = 0;
+            int txtCount = 0;
+            for (int i = 0; i < childNodes.getLength(); i++)
+            {
+                Node child = childNodes.item(i);
+                if (child.getNodeType() == Node.ELEMENT_NODE)
+                {
+                    String elemValue = SortElement(child, preText + "  ");
+                    if (!CommonUtility.IsNullOrEmpty(elemValue))
+                    {
+                        elemArray.add(elemValue);
+                        elCount++;
+                    }
+                }
+                else if (child.getNodeType() == Node.TEXT_NODE)
+                {
+                    String content = child.getTextContent().trim();
+                    if (!CommonUtility.IsNullOrEmpty(content))
+                    {
+                        elemArray.add(content);
+                        txtCount++;
+                    }
+                }
+            }
+
+            Collections.sort(elemArray);
+            for (String str : elemArray)
+            {
+                elemString += str;
+            }
+        }
+
+        String endTag = "";
+        if (elemArray.size() > 1 && elemString.length() > 0)
+        {
+            endTag = "\n" + preText;
+        }
+        String tagName = "";
+        if (!(elem instanceof Document))
+        {
+            tagName = elem.getNodeName();
+        }
+        endTag += "</" + tagName + ">";
+
+        if ("" != tagName)
+        {
+            elemString = "\n" + preText + "<" + tagName + ">" + elemString + endTag;
+        }
+
+        return elemString;
+    }
+
+    private static String toString(Document doc) {
+        try {
+            StringWriter sw = new StringWriter();
+            Source xmlInput = new DOMSource(doc);
+            Result xmlOutput = new StreamResult(sw);
+            TransformerFactory tf = TransformerFactory.newInstance();
+            //See: http://stackoverflow.com/questions/161462/java-writing-a-dom-to-an-xml-file-formatting-issues
+            tf.setAttribute("indent-number", 4);
+            Transformer transformer = tf.newTransformer();
+            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+
+            transformer.transform(xmlInput, xmlOutput);
+            return sw.toString();
+        } catch (Exception ex) {
+            throw new RuntimeException("Error converting to String", ex);
+        }
+    }
+
+    private Node RecursiveNormalizeNode(Node elem) {
+        //Strip whitespace nodes
+        try {
+            XPathFactory xpathFactory = XPathFactory.newInstance();
+            // XPath to find empty text nodes.
+            XPathExpression xpathExp = xpathFactory.newXPath().compile(
+                    "//text()[normalize-space(.) = '']");  
+            NodeList emptyTextNodes = (NodeList) 
+                    xpathExp.evaluate(elem, XPathConstants.NODESET);
+
+            // Remove each empty text node from document.
+            for (int i = 0; i < emptyTextNodes.getLength(); i++) {
+                Node emptyTextNode = emptyTextNodes.item(i);
+                emptyTextNode.getParentNode().removeChild(emptyTextNode);
+            }
+        } catch (Exception ex) {
+            
+        }
+
+        //Strip attributes
+        if (elem instanceof Element) {
+            Element e = (Element)elem;
+            e.removeAttribute("xsi:noNamespaceSchemaLocation");
+            e.removeAttribute("noNamespaceSchemaLocation");
+            e.removeAttribute("xmlns:xsi");
+            e.removeAttribute("xsi");
+            e.removeAttribute("version");
+        }
+
+        NodeList childNodes = elem.getChildNodes();
+        for (int i = 0; i < childNodes.getLength(); i++) {
+            Node n = childNodes.item(i);
+            RecursiveNormalizeNode(n);
+        }
+        /*
+        NodeList childNodes = elem.getChildNodes();
+        Vector<Node> nodes = new Vector<Node>();
+        for (int i = 0; i < childNodes.getLength(); i++) {
+            Node n = childNodes.item(i);
+            nodes.add(n);
+            elem.removeChild(n);
+        }
+        Collections.sort(nodes, new NodeNormalizeComparator());
+        for (Node child : nodes) {
+            child.normalize();
+            elem.appendChild(child);
+        }
+        */
+        return elem;
+    }
+
+    private Document NormalizeDocument(Document doc) {
+        //Strip known attributes on this node
+        Element e = doc.getDocumentElement();
+        e.removeAttribute("xsi:noNamespaceSchemaLocation");
+        e.removeAttribute("noNamespaceSchemaLocation");
+        e.removeAttribute("xmlns:xsi");
+        e.removeAttribute("xsi");
+        e.removeAttribute("version");
+
+        RecursiveNormalizeNode(doc);
+        //Strip whitespace nodes
+        try {
+            XPathFactory xpathFactory = XPathFactory.newInstance();
+            // XPath to find empty text nodes.
+            XPathExpression xpathExp = xpathFactory.newXPath().compile(
+                    "//text()[normalize-space(.) = '']");  
+            NodeList emptyTextNodes = (NodeList) 
+                    xpathExp.evaluate(doc, XPathConstants.NODESET);
+
+            // Remove each empty text node from document.
+            for (int i = 0; i < emptyTextNodes.getLength(); i++) {
+                Node emptyTextNode = emptyTextNodes.item(i);
+                emptyTextNode.getParentNode().removeChild(emptyTextNode);
+            }
+        } catch (Exception ex) {
+            
+        }
+        doc.normalizeDocument();
+
+        //Sort the top-level child nodes
+        NodeList childNodes = e.getChildNodes();
+        if (DebugSettings.Enabled) {
+            Console.WriteLine(">>> Pre-sort node list");
+            for (int i = 0; i < childNodes.getLength(); i++) {
+                Node n = childNodes.item(i);
+                Console.WriteLine("\t" + n.getNodeName());
+            }
+            Console.WriteLine("<<<");
+        }
+
+        Vector<Node> nodes = new Vector<Node>();
+        for (int i = 0; i < childNodes.getLength(); i++) {
+            Node n = childNodes.item(i);
+            nodes.add(n);
+            e.removeChild(n);
+        }
+        Comparator<Node> nodeComparator = new Comparator<Node>() {
+            public int compare(Node n1, Node n2) {
+                String s1 = n1.getNodeName();
+                String s2 = n2.getNodeName();
+                int result = s1.compareTo(s2);
+                if (DebugSettings.Enabled)
+                    Console.WriteLine("'%s'.compareTo('%s') - %d", s1, s2, result);
+                return result;
+            }
+        };
+        Collections.sort(nodes, nodeComparator); //, new NodeNormalizeComparator());
+        for (Node child : nodes) {
+            child.normalize();
+            e.appendChild(child);
+        }
+
+        if (DebugSettings.Enabled) {
+            childNodes = e.getChildNodes();
+            Console.WriteLine(">>> Post-sort node list");
+            for (int i = 0; i < childNodes.getLength(); i++) {
+                Node n = childNodes.item(i);
+                Console.WriteLine("\t" + n.getNodeName());
+            }
+            Console.WriteLine("<<<");
+        }
+        return doc;
+    }
+
+    private XmlCompareResult TryXmlCompare(String expected, String actual) {
+        /*
+        XmlCompareResult result = null;
+        try {
+            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+            dbf.setNamespaceAware(true);
+            dbf.setCoalescing(true);
+            dbf.setIgnoringElementContentWhitespace(true);
+            dbf.setIgnoringComments(true);
+            DocumentBuilder db = dbf.newDocumentBuilder();
+
+            Document doc1 = db.parse(new ByteArrayInputStream(expected.trim().getBytes()));
+            doc1 = NormalizeDocument(doc1);
+
+            Document doc2 = db.parse(new ByteArrayInputStream(actual.trim().getBytes()));
+            doc2 = NormalizeDocument(doc2);
+
+            result = new XmlCompareResult();
+            result.Result = doc1.isEqualNode(doc2);
+            result.Expected = toString(doc1);
+            result.Actual = toString(doc2);
+        } catch (Exception ex) {
+            result = new XmlCompareResult();
+            result.Result = false;
+        }
+        Console.WriteLine("TryXmlCompare returned " + result.Result);
+        return result;
+        */
+       
+        XmlCompareResult result = null;
+        try {
+            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+            //dbf.setNamespaceAware(true);
+            dbf.setCoalescing(true);
+            dbf.setIgnoringElementContentWhitespace(true);
+            dbf.setIgnoringComments(true);
+            DocumentBuilder db = dbf.newDocumentBuilder();
+
+            Document doc1 = db.parse(new ByteArrayInputStream(expected.trim().getBytes()));
+            doc1 = NormalizeDocument(doc1);
+
+            Document doc2 = db.parse(new ByteArrayInputStream(actual.trim().getBytes()));
+            doc2 = NormalizeDocument(doc2);
+
+            result = new XmlCompareResult();
+            result.Expected = SortElement(doc1, "");
+            result.Actual = SortElement(doc2, "");
+            result.Result = (result.Expected.equals(result.Actual));
+        } catch (Exception ex) {
+            //Console.WriteLine("Exception doing XML compare: %s", CommonUtility.ExceptionToString(ex));
+            result = new XmlCompareResult();
+            result.Result = false;
+        }
+        if (DebugSettings.Enabled)
+            Console.WriteLine("\t\tTryXmlCompare returned " + result.Result);
+        return result;
+    }
+
+    public void Cleanup()
+    {
+        for (ITestExecutor exec : _executors.values())
+        {
+            try {
+                exec.close();
+            } catch (IOException ex) {
+
+            }
+        }
+        _executors.clear();
+    }
+
+    public String getApiType()
+    {
+      return ApiTypes.Platform;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/MapGuideTests.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/MapGuideTests.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/MapGuideTests.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,48 @@
+package org.osgeo.mapguide.test;
+
+import java.util.*;
+
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.mapguide.*;
+
+public class MapGuideTests
+{
+    public static int Execute(IPlatformFactory factory, ITestLogger logger, MutableInteger testsRun) {
+        int failures = 0;
+        Vector<IExternalTest> tests = new Vector<IExternalTest>();
+        
+        //--------------- BEGIN Test Registration -----------------//
+        tests.add(new ReadOnlyLayerCollectionTest());
+        tests.add(new RenderingServiceTest());
+        //--------------- END Test Registration -------------------//
+
+        for (IExternalTest test : tests)
+        {
+            try
+            {
+                String clsName = CommonUtility.GetTypeName(test);
+                logger.WriteLine("****** Executing MapGuide test: " + clsName + " *********");
+                Console.WriteLine("Executing external MapGuide test: " + clsName);
+                test.Execute(factory, logger);
+            }
+            catch (AssertException ex)
+            {
+                logger.WriteLine("Assertion failure: " + ex.getMessage());
+                Console.WriteLine("Assertion failure: " + ex.getMessage());
+                failures++;
+            }
+            catch (Exception ex)
+            {
+                logger.WriteLine("General failure: " + CommonUtility.ExceptionToString(ex));
+                Console.WriteLine("General failure: " + CommonUtility.ExceptionToString(ex));
+                failures++;
+            }
+            finally
+            {
+                testsRun.increment();
+            }
+        }
+        return failures;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/PlatformFactory.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/PlatformFactory.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/PlatformFactory.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test;
+
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class PlatformFactory implements IPlatformFactory
+{
+    private MgSiteConnection _siteConn;
+
+    public PlatformFactory(MgSiteConnection siteConn)
+    {
+        _siteConn = siteConn;
+    }
+
+    public MgService createService(int serviceType)
+    {
+        return _siteConn.createService(serviceType);
+    }
+
+    public MgMapBase createMap()
+    {
+        return new MgMap(_siteConn);
+    }
+
+    public MgLayerBase createLayer(MgResourceIdentifier resId)
+    {
+        MgResourceService resSvc = (MgResourceService)_siteConn.createService(MgServiceType.ResourceService);
+        return new MgLayer(resId, resSvc);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/Program.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/Program.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/Program.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,91 @@
+package org.osgeo.mapguide.test;
+
+import java.io.*;
+import java.util.*;
+import java.util.logging.Logger;
+import java.util.logging.LogManager;
+import java.util.logging.Level;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class Program
+{
+    static MgUserInformation userInfo;
+    static MgSiteConnection siteConn;
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("Working Directory is: " + System.getProperty("user.dir"));
+            LogManager.getLogManager().reset(); //Needed for Linux
+            Logger.getLogger("com.almworks.sqlite4java").setLevel(Level.OFF);
+            if (args.length >= 1 && args.length <= 2) {
+                String webConfigPath = args[0];
+                String logFile = "UnitTests.log";
+                if (args.length == 2)
+                    logFile = args[1];
+                
+                int failures = 0;
+                try (ITestLogger logger = new TestLoggerFile(logFile)) {
+                    logger.WriteLine(String.format("Run started: %s", new Date().toString()));
+                    MapGuideJavaApi.mgInitializeWebTier(webConfigPath);
+
+                    userInfo = new MgUserInformation("Administrator", "admin");
+                    siteConn = new MgSiteConnection();
+                    siteConn.open(userInfo);
+
+                    //Load the Sheboygan sample data set required by some tests
+                    Console.WriteLine("Loading Sheboygan sample data package");
+                    MgByteSource bs = new MgByteSource("../../TestData/Samples/Sheboygan/Sheboygan.mgp");
+                    MgByteReader rdr = bs.getReader();
+                    MgResourceService resSvc = (MgResourceService)siteConn.createService(MgServiceType.ResourceService);
+                    resSvc.applyResourcePackage(rdr);
+
+                    Console.WriteLine("MapGuide Initialized. Running tests");
+
+                    IPlatformFactory factory = new PlatformFactory(siteConn);
+
+                    MutableInteger testsRun = new MutableInteger(0);
+                    boolean isEnterprise = false;
+                    failures += ExecuteTest(ApiTypes.Platform, "../../TestData/ResourceService/ResourceServiceTest.dump", testsRun, logger, isEnterprise);
+                    failures += ExecuteTest(ApiTypes.Platform, "../../TestData/DrawingService/DrawingServiceTest.dump", testsRun, logger, isEnterprise);
+                    failures += ExecuteTest(ApiTypes.Platform, "../../TestData/FeatureService/FeatureServiceTest.dump", testsRun, logger, isEnterprise);
+                    failures += ExecuteTest(ApiTypes.Platform, "../../TestData/SiteService/SiteServiceTest.dump", testsRun, logger, isEnterprise);
+                    failures += ExecuteTest(ApiTypes.Platform, "../../TestData/MappingService/MappingServiceTest.dump", testsRun, logger, isEnterprise);
+                    failures += ExecuteTest(ApiTypes.Platform, "../../TestData/ServerAdmin/ServerAdminTest.dump", testsRun, logger, isEnterprise);
+                    failures += ExecuteTest(ApiTypes.Platform, "../../TestData/MapLayer/MapLayerTest.dump", testsRun, logger, isEnterprise);
+                    failures += ExecuteTest(ApiTypes.Platform, "../../TestData/WebLayout/WebLayoutTest.dump", testsRun, logger, isEnterprise);
+                    failures += ExecuteTest(ApiTypes.Platform, "../../TestData/Unicode/UnicodeTest.dump", testsRun, logger, isEnterprise);
+                    //Run auxillary tests not part of the SQLite-defined suite
+                    failures += CommonTests.Execute(factory, logger, testsRun);
+                    failures += MapGuideTests.Execute(factory, logger, testsRun);
+                    logger.WriteLine("\n\nTests failed/run: %d/%d", failures, testsRun.getValue());
+                    Console.Write("\n\nTests failed/run: %d/%d\n", failures, testsRun.getValue());
+                    logger.WriteLine(String.format("Run ended: %s", new Date().toString()));
+                }
+                System.exit(failures);
+            } else {
+                System.out.println("Usage: java -jar MgUnitTest.jar <webconfig.ini path> [test log path]");
+                System.exit(1);
+            }
+        } catch (Exception ex) {
+            System.err.println(CommonUtility.ExceptionToString(ex));
+            System.exit(1);
+        }
+    }
+
+    private static int ExecuteTest(String apiType, String dumpFile, MutableInteger testsRun, ITestLogger logger, boolean isEnterprise) {
+        ITestExecutorCollection exec = null;
+        if (apiType.equals(ApiTypes.Platform))
+            exec = new MapGuideTestExecutorCollection(userInfo, siteConn);
+
+        int ret = 0;
+        if (exec != null)
+        {
+            //"validate" is currently the only test execution mode supported
+            exec.Initialize("validate", dumpFile);
+            ret += exec.Execute(testsRun, logger, isEnterprise);
+            exec.Cleanup();
+        }
+        return ret;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/Assert.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/Assert.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/Assert.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,91 @@
+package org.osgeo.mapguide.test.common;
+
+public class Assert
+{
+    public static void AreBytesEqual(byte[] expected, byte[] actual)
+    {
+        byte[] a = expected;
+        byte[] b = actual;
+        if (a.length != b.length)
+            throw new AssertException("Expected byte array size of " + a.length + ", got size of: " + b.length);
+
+        for (int i = 0; i < a.length; i++)
+        {
+            if (a[i] != b[i])
+                throw new AssertException("Byte number " + (i + 1) + " in expected byte array does not match byte in actual byte array");
+        }
+    }
+
+    public static void AreEqual(long expected, long actual) {
+        if (expected != actual)
+        {
+            throw new AssertException("Expected: " + expected + ", got: " + actual);
+        }
+    }
+    
+    public static void AreEqual(float expected, float actual) {
+        if (expected != actual)
+        {
+            throw new AssertException("Expected: " + expected + ", got: " + actual);
+        }
+    }
+
+    public static void AreEqual(double expected, double actual) {
+        if (expected != actual)
+        {
+            throw new AssertException("Expected: " + expected + ", got: " + actual);
+        }
+    }
+
+    public static void AreEqual(Object expected, Object actual)
+    {
+        if (expected instanceof byte[] && actual instanceof byte[])
+        {
+            AreBytesEqual((byte[])expected, (byte[])actual);
+        }
+        else if (!expected.equals(actual))
+        {
+            throw new AssertException("Expected: " + expected + ", got: " + actual);
+        }
+    }
+
+    public static void Fail(String message) {
+        throw new AssertException(message);
+    }
+
+    public static void Greater(Comparable value, Comparable against)
+    {
+        if (value.compareTo(against) <= 0)
+            throw new AssertException(value + " is not greater than " + against);
+    }
+
+    public static void IsNull(Object obj)
+    {
+        if (obj != null)
+            throw new AssertException("Reference is not null");
+    }
+
+    public static void IsTrue(boolean condition)
+    {
+        if (!condition)
+            throw new AssertException("Condition evaluated to false. Expected: true");
+    }
+
+    public static void IsTrue(boolean condition, String message)
+    {
+        if (!condition)
+            throw new AssertException(message);
+    }
+
+    public static void IsFalse(boolean condition)
+    {
+        if (condition)
+            throw new AssertException("Condition evaluated to true. Expected: false");
+    }
+
+    public static void IsNaN(double value)
+    {
+        if (!Double.isNaN(value))
+            throw new AssertException("Double is a number. Expected: NaN");
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/AssertException.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/AssertException.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/AssertException.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+package org.osgeo.mapguide.test.common;
+
+public class AssertException extends RuntimeException
+{
+    public AssertException(String message) {
+        super(message);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/CommonUtility.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/CommonUtility.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/CommonUtility.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,756 @@
+package org.osgeo.mapguide.test.common;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.ByteArrayInputStream;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Vector;
+import java.util.HashMap;
+import java.nio.file.*;
+import java.nio.charset.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+public class CommonUtility
+{
+    public static boolean IsWindows()
+    {
+        return System.getProperty("os.name").toLowerCase().contains("win");
+    }
+
+    public static String DoubleToString(double d)
+    {
+        if(d == (long) d)
+            return String.format("%d",(long)d);
+        else
+            return String.format("%s",d);
+    }
+
+    public static MgStringCollection StringToMgStringCollection(String str)
+    {
+        try
+        {
+            MgStringCollection coll = new MgStringCollection();
+            String[] tokens = str.split(",");
+            for (String token : tokens)
+            {
+                coll.add(token);
+            }
+            return coll;
+        }
+        catch (MgException ex)
+        {
+            throw new UnitTestException(String.format("Exception from MapGuide:\n%s", ex.getDetails()));
+        }
+    }
+
+    public static String JoinStringArray(String glue, String[] s)
+    {
+        int k = s.length;
+        if ( k == 0 )
+        {
+            return null;
+        }
+        StringBuilder out = new StringBuilder();
+        out.append( s[0] );
+        for ( int x=1; x < k; ++x )
+        {
+            out.append(glue).append(s[x]);
+        }
+        return out.toString();
+    }
+
+    public static String MgPointToString(MgPoint pt)
+    {
+        try
+        {
+            MgCoordinate coord = pt.getCoordinate();
+            return String.format("(%d:%d)",
+                (int)coord.getX(),
+                (int)coord.getY());
+        }
+        catch (MgException ex)
+        {
+            throw new UnitTestException(String.format("Exception from MapGuide:\n%s", ex.getDetails()));
+        }
+    }
+
+    public static String MgEnvelopeToString(MgEnvelope env)
+    {
+        try
+        {
+            MgCoordinate ll = env.getLowerLeftCoordinate();
+            MgCoordinate ur = env.getUpperRightCoordinate();
+            return String.format("(%d:%d)-(%d:%d)", 
+                (int)ll.getX(), 
+                (int)ll.getY(), 
+                (int)ur.getX(), 
+                (int)ur.getY());
+        }
+        catch (MgException ex)
+        {
+            throw new UnitTestException(String.format("Exception from MapGuide:\n%s", ex.getDetails()));
+        }
+    }
+
+    public static String GetExtension(String name)
+    {
+        if (name.lastIndexOf(".") >= 0)
+        {
+            return name.substring(name.lastIndexOf(".") + 1);
+        }
+        else
+        {
+            if (name.equals("MG_USER_CREDENTIALS"))
+                return "txt";
+            else
+                return "bin";
+        }
+    }
+
+    public static String GetMimeType(String extension)
+    {
+        switch (extension)
+        {
+            case "agf":
+                return "application/agf";
+            case "bin":
+                return "application/octet-stream";
+            case "dwf":
+                return "model/vnd.dwf";
+            case "jpg":
+            case "jpeg":
+                return "image/jpeg";
+            case "png":
+                return "image/png";
+            case "tif":
+            case "tiff":
+                return "image/tiff";
+            case "html":
+                return "text/html";
+            case "txt":
+                return "text/plain";
+            case "xml":
+                return "text/xml";
+            default:
+                return "application/octet-stream";
+        }
+    }
+
+    public static MgByteReader GetByteReaderFromPath(String path)
+    {
+        return GetByteReaderFromPath(path, true);
+    }
+
+    public static MgByteReader GetByteReaderFromPath(String path, boolean bCheck)
+    {
+        if (path != null)
+        {
+            String normPath = GetPath(path);
+            if (bCheck)
+            {
+                if (FileExists(normPath))
+                {
+                    MgByteSource source = new MgByteSource(normPath);
+                    MgByteReader reader = source.getReader();
+                    return reader;
+                }
+                return null;
+            }
+            else
+            {
+                MgByteSource source = new MgByteSource(normPath);
+                MgByteReader reader = source.getReader();
+                return reader;
+            }
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+    public static String BooleanToString(boolean b)
+    {
+        return b ? "True" : "False";
+    }
+
+    public static String GetTypeName(Object o) {
+        Class cls = o.getClass();
+        String name = cls.getName();
+        if (name.lastIndexOf('.') > 0) {
+            name = name.substring(name.lastIndexOf('.') + 1); // Map$Entry
+            name = name.replace('$', '.');      // Map.Entry
+        }
+        return name;
+    }
+
+    public static void DeleteFile(String path) {
+        try {
+            Files.deleteIfExists(Paths.get(path));
+        } catch (IOException ex) {
+
+        }
+    }
+
+    public static boolean FileExists(String path) {
+        File f = new File(path);
+        return (f.exists() && !f.isDirectory());
+    }
+
+    public static byte[] ReadAllBytes(String path) {
+        try {
+            return Files.readAllBytes(Paths.get(path));
+        } catch (IOException ex) {
+            return null;
+        }
+    }
+
+    public static String ReadAllText(String path) throws IOException {
+        return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
+    }
+
+    public static String GetTempFileName() {
+        try {
+            Path p = Files.createTempFile(null, null);
+            return p.toString();
+        } catch (IOException ex) {
+            return null;
+        }
+    }
+
+    public static boolean IsNullOrEmpty(String str) {
+        return str == null || str.length() == 0;
+    }
+
+    public static String ExceptionToString(Exception ex) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(ex.toString() + "\n");
+        sb.append(ex.getMessage() + "\n");
+        StackTraceElement[] items = ex.getStackTrace();
+        for (StackTraceElement item : items) {
+            sb.append("\t" + item.toString() + "\n");
+        }
+        return sb.toString();
+    }
+
+    public static void GenerateDatabase(String dumpFileName, String dbName) throws SQLiteException, IOException {
+        SQLiteConnection db = new SQLiteConnection(new File(dbName));
+        try {
+            db.open(true);
+
+            String sql = new String(Files.readAllBytes(Paths.get(dumpFileName)), StandardCharsets.UTF_8);
+            db.exec(sql);
+        }
+        finally
+        {
+            db.dispose();       
+        }
+    }
+
+    public static String GetStringFromStatement(SQLiteStatement stmt, String colName) {
+        try {
+            for (int i = 0; i < stmt.columnCount(); i++) {
+                String cn = stmt.getColumnName(i);
+                if (cn.equals(colName)) {
+                    if (stmt.columnNull(i)) {
+                        return null;
+                    } else {
+                        return stmt.columnString(i);
+                    }
+                }
+            }
+            return null;
+        } catch (Exception ex) {
+            Console.WriteLine(ex.getMessage());
+            return null;
+        }
+    }
+
+    public static byte[] GetByteArrayFromStatement(SQLiteStatement stmt, String colName) {
+        try {
+            for (int i = 0; i < stmt.columnCount(); i++) {
+                String cn = stmt.getColumnName(i);
+                if (cn.equals(colName)) {
+                    if (stmt.columnNull(i)) {
+                        return null;
+                    } else {
+                        return stmt.columnBlob(i);
+                    }
+                }
+            }
+            return null;
+        } catch (Exception ex) {
+            Console.WriteLine(ex.getMessage());
+            return null;
+        }
+    }
+
+    public static String GetDbPath(String dumpFileName) throws SQLiteException, IOException
+    {
+        String dbPath = dumpFileName.replace(".dump", ".db");
+        String dbName = CommonUtility.GetPath(dbPath);
+
+        if (!FileExists(dumpFileName) && !FileExists(dbName))
+        {
+            throw new UnitTestException(String.format("Error: Dump file %s not found. Unable to create database file", dumpFileName));
+        }
+        else if (!FileExists(dbName))
+        {
+            GenerateDatabase(dumpFileName, dbName);
+        }
+        else
+        {
+            try
+            {
+                DeleteFile(dbName);
+                GenerateDatabase(dumpFileName, dbName);
+            }
+            catch (Exception ex)
+            {
+                throw new UnitTestException(String.format("Unable to delete database file %s. The file is either in use or is read-only. The database has not been updated", dbName));
+            }
+        }
+
+        return dbPath;
+    }
+
+    public static String GetPath(String dbPath)
+    {
+        try {
+            if (IsNullOrEmpty(dbPath)) {
+                return new File(".").getPath();
+            } else {
+                File f = new File(dbPath.replace("\\", "/"));
+                if (f.isAbsolute())
+                    return f.getPath();
+                else
+                    return new File(System.getProperty("user.dir"), dbPath.replace("\\","/")).getCanonicalPath();
+            }
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public static Object SpecialDataHandling(String operation, Object resultData, String mimeType)
+    {
+        Object res = resultData;
+        switch (operation)
+        {
+            case "ENUMERATERESOURCES":
+                res = RemoveTimeStamp(resultData.toString());
+                break;
+            case "GETDRAWINGLAYER":
+                res = RemoveDwfSectionName(resultData);
+                break;
+            case "GETDRAWINGSECTION":
+                res = RemoveDwfSectionName(resultData);
+                break;
+            case "GETLOG":
+                res = RemoveLogEntryTimeStamp(resultData.toString());
+                break;
+            case "GETMAP":
+                res = GetMapHeader(resultData.toString());
+                break;
+            case "GETLONGTRANSACTIONS":
+                res = RemoveCreationDate(resultData.toString());
+                break;
+        }
+
+        String strRes = null;
+        if (res instanceof String)
+            strRes = (String)res;
+        if (strRes != null && mimeType.equals("text/xml"))
+        {
+            try {
+                /*
+                DocumentBuilderFactory documentBuildFactory = DocumentBuilderFactory.newInstance();
+                DocumentBuilder documentBuilder = documentBuildFactory.newDocumentBuilder();
+                Document doc = documentBuilder.parse(new ByteArrayInputStream(strRes.getBytes()));
+
+                res = SortElement(doc, "");
+                */
+                res = strRes;
+            }
+            catch (Exception ex) {
+
+            }
+        }
+        return res;
+    }
+
+    public static Object RemoveStackTraceFromResult(Object result)
+    {
+        String strResult = null;
+        if (result instanceof String)
+            strResult = (String)result;
+        //TODO: Clean out stack trace
+        return result;
+    }
+
+    private static String RemoveTimeStamp(String resultData)
+    {
+        String result = resultData;
+        String newResult = result;
+        while (result.indexOf("<CreatedDate>") >= 0)
+        {
+            newResult = result.substring(0, result.indexOf("<CreatedDate>"));
+            newResult += result.substring(result.indexOf("</ModifiedDate>") + "</ModifiedDate>".length());
+            result = newResult;
+        }
+        return newResult;
+    }
+
+    private static Object RemoveDwfSectionName(Object resultData)
+    {
+        boolean bFromByteArray = false;
+        byte[] bResultData = null;
+        if (resultData instanceof byte[])
+            bResultData = (byte[])resultData;
+        String strResultData = null;
+        if (resultData instanceof String)
+            strResultData = (String)resultData;
+        /*
+        if (strResultData == null)
+        {
+            if (bResultData != null)
+            {
+                strResultData = enc.GetString(bResultData);
+                bFromByteArray = true;
+            }
+        }*/
+
+        if (strResultData != null)
+        {
+            //Console.WriteLine("RemoveDwfSectionName: length = {0}", strResultData.Length);
+            int idx = strResultData.indexOf(".w2d");
+            //Console.WriteLine("RemoveDwfSectionName: widx = {0}", idx);
+            if (idx >= 0)
+            {
+                String newResult = strResultData.substring(idx);
+                int eidx = newResult.indexOf("EndOfDWF");
+                //Console.WriteLine("RemoveDwfSectionName: eidx = {0}", eidx);
+                if (0 != eidx)
+                {
+                    newResult = newResult.substring(0, eidx);
+                    //Console.WriteLine("RemoveDwfSectionName: newlength = {0}", newResult.Length);
+                }
+                if (bFromByteArray)
+                    return newResult.getBytes(StandardCharsets.UTF_8);
+                else
+                    return newResult;
+            }
+        }
+        else if (bResultData != null)
+        {
+            byte[] bW2d = ".w2d".getBytes(StandardCharsets.UTF_8);
+            byte[] bEOF = "EndOfDWF".getBytes(StandardCharsets.UTF_8);
+
+            int widx = -1;
+            int eidx = -1;
+
+            int wMatches = 0;
+            int eMatches = 0;
+
+            int i = 0;
+            while(i < bResultData.length)
+            {
+                //Haven't found .w2d sequence
+                if (widx < 0 && wMatches == 0) 
+                {
+                    //We've found a "."
+                    if (bResultData[i] == bW2d[0])
+                    {
+                        wMatches++;
+                        i++;
+
+                        //Now try to follow through this sequence to see if it is ".w2d"
+                        while (wMatches < bW2d.length)
+                        {
+                            //End of array. Abort
+                            if (i >= bResultData.length)
+                                break;
+
+                            //Next byte in sequence matches. Advance
+                            if (bResultData[i] == bW2d[wMatches])
+                            {
+                                //Increment matches
+                                wMatches++;
+                                
+                                //Check if full sequence matches
+                                if (wMatches == bW2d.length)
+                                {
+                                    //Match. Record index which is current position minus the # of consecutive matches
+                                    widx = i - wMatches;
+                                    break;
+                                }
+                            }
+                            else //Incomplete sequence. Break this loop
+                            {
+                                wMatches = 0; //Reset
+                                break;
+                            }
+                        }
+                    }
+                }
+                //Haven't found EndOfDWF sequence
+                else if (eidx < 0 && eMatches == 0)
+                {
+                    //We've found a "E"
+                    if (bResultData[i] == bEOF[0])
+                    {
+                        eMatches++;
+                        i++;
+
+                        //Now try to follow through this sequence to see if it is "EndOfDWF"
+                        while (eMatches < bEOF.length)
+                        {
+                            //End of array. Abort
+                            if (i >= bResultData.length)
+                                break;
+
+                            //Next byte in sequence matches. Advance
+                            if (bResultData[i] == bEOF[eMatches])
+                            {
+                                //Increment matches
+                                eMatches++;
+
+                                //Check if full sequence matches
+                                if (eMatches == bEOF.length)
+                                {
+                                    //Match. Record index which is current position minus the # of consecutive matches
+                                    eidx = i - eMatches;
+                                    break;
+                                }
+                            }
+                            else //Incomplete sequence. Break this loop
+                            {
+                                eMatches = 0; //Reset
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                //Found both offsets. We're done
+                if (widx > 0 && eidx > widx)
+                    break;
+
+                i++;
+            }
+
+            if (widx > 0 && eidx > widx)
+            {
+                byte[] newResult = new byte[eidx - widx];
+                int off = 0;
+                for (int j = widx; j <= eidx; j++)
+                {
+                    newResult[off] = bResultData[j];
+                }
+            }
+        }
+        return resultData;
+    }
+
+    private static String RemoveLogEntryTimeStamp(String resultData)
+    {
+        String result = resultData;
+        String newResult = result;
+        while (result.indexOf("<") >= 0)
+        {
+            newResult = result.substring(0, result.indexOf("<"));
+            newResult += result.substring(result.indexOf(">") + 1);
+            result = newResult;
+        }
+        return newResult;
+    }
+
+    private static String GetMapHeader(String resultData)
+    {
+        if (resultData.indexOf("(DWF V06.01)") >= 0)
+            resultData = "(DWF V06.01)";
+        return resultData;
+    }
+
+    private static String RemoveCreationDate(String resultData)
+    {
+        String newResult = resultData;
+        while (resultData.indexOf("<CreationDate>") >= 0)
+        {
+            newResult = resultData.substring(0, resultData.indexOf("<CreationDate>"));
+            newResult += resultData.substring(resultData.indexOf("</CreationDate>") + "</CreationDate>".length());
+            resultData = newResult;
+        }
+        return newResult;
+    }
+
+    public static Object ProcessExceptionMessage(Object resultData)
+    {
+        String strResultData = null;
+        if (resultData instanceof String)
+            strResultData = (String)resultData;
+        if (strResultData != null)
+        {
+            String text = "exception occurred";
+            if (strResultData.contains(text))
+            {
+                strResultData = strResultData.substring(0, strResultData.indexOf(text) + text.length());
+            }
+            return strResultData;
+        }
+        return resultData;
+    }
+
+    public static boolean SpecialValidation(String operation, Object resultData, Object expectedResult)
+    {
+        if (operation.equals("GETFEATUREPROVIDERS"))
+        {
+            //We expect both to be strings here
+            return GetFeatureProvidersValidation(resultData.toString(), expectedResult.toString());
+        }
+        return false;
+    }
+
+    private static boolean GetFeatureProvidersValidation(String resultData, String expectedResult)
+    {
+        throw new RuntimeException("Not implemented");
+    }
+
+    public static boolean ByteArraysEqual(byte[] bExpected, byte[] bActual, String operation, String testName)
+    {
+        if (bExpected == null && bActual != null)
+            return false;
+
+        if (bExpected != null && bActual == null)
+            return false;
+
+        boolean bRet = true;
+        for (int i = 0; i < bExpected.length; i++)
+        {
+            if (i >= bExpected.length ||
+                i >= bActual.length)
+            {
+                break;
+            }
+
+            byte b1 = bExpected[i];
+            byte b2 = bActual[i];
+
+            if (b1 != b2)
+            {
+                return false;
+            }
+        }
+
+        //System.Diagnostics.Debug.WriteLine(string.Format("[MgTestRunner]: {0} - {1} - COMPARE: {2} with {3} = {4}", testName, operation, bExpected.Length, bActual.Length, (bRet ? 0 : 1)));
+        return bRet;
+    }
+
+    public static String GetExtensionFromMimeType(String mimeType)
+    {
+        String extension = "xml";
+        if (mimeType.contains("ePlot"))
+            return "dwf";
+        if (mimeType.contains("text/plain"))
+            return "txt";
+        if (mimeType.contains("text/html"))
+            return "html";
+
+        switch (mimeType)
+        { 
+            case "application/agf":
+                return "agf";
+            case "application/octet-stream":
+                return "bin";
+            case "model/vnd.dwf":
+                return "dwf";
+            case "image/jpeg":
+                return "jpg";
+            case "image/png":
+                return "png";
+            case "image/tiff":
+                return "tiff";
+            case "application/x-w2d":
+                return "dwf";
+        }
+
+        return extension;
+    }
+
+    public static boolean ReadParameterValue(SQLiteConnection conn, int paramSetId, String paramName, HashMap<String, String> result)
+    {
+        return ReadParameterValue(conn, paramSetId, paramName, result, false);
+    }
+
+    public static boolean ReadParameterValue(SQLiteConnection conn, int paramSetId, String paramName, HashMap<String, String> result, boolean bIsPath)
+    {
+        try {
+            SQLiteStatement stmt = conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"%s\"", paramSetId, paramName));
+            if (stmt.step())
+            {
+                String str = GetStringFromStatement(stmt, "ParamValue");
+                if (bIsPath)
+                {
+                    if (DebugSettings.Enabled)
+                        Console.WriteLine("Read path from current directory");
+                    str = CommonUtility.GetPath(str);
+                }
+                result.put(paramName, str);
+                return true;
+            }
+            return false;
+        } catch(Exception ex) {
+            return false;
+        }
+    }
+
+    public static boolean ReadParameterValue(SQLiteConnection conn, String paramName, HashMap<String, String> result)
+    {
+        try {
+            SQLiteStatement stmt = conn.prepare(String.format("Select ParamValue from Params WHERE ParamName=\"%s\"", paramName));
+            if (stmt.step())
+            {
+                result.put(paramName, GetStringFromStatement(stmt, "ParamValue"));
+                return true;
+            }
+            return false;
+        } catch (Exception ex) {
+            return false;
+        }
+    }
+
+    public static String MgStringCollectionToString(MgStringCollection coll)
+    {
+        try
+        {
+            //Sigh, we're too smart for our own good. Yes, the trailing comma
+            //should be there!
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < coll.getCount(); i++)
+            {
+                sb.append(coll.getItem(i));
+                sb.append(",");
+            }
+            return sb.toString();
+
+            /*
+            List<string> items = new List<string>();
+            for (int i = 0; i < coll.GetCount(); i++)
+            {
+                items.Add(coll.GetItem(i));
+            }
+            return string.Join(",", items.ToArray());
+             */
+        }
+        catch (MgException ex)
+        {
+            throw new UnitTestException(String.format("Exception from MapGuide:\n%s", ex.getDetails()));
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/IExternalTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/IExternalTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/IExternalTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,6 @@
+package org.osgeo.mapguide.test.common;
+
+public interface IExternalTest
+{
+    void Execute(IPlatformFactory factory, ITestLogger logger);
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/IPlatformFactory.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/IPlatformFactory.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/IPlatformFactory.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,10 @@
+package org.osgeo.mapguide.test.common;
+
+import org.osgeo.mapguide.*;
+
+public interface IPlatformFactory
+{
+    MgService createService(int serviceType);
+    MgMapBase createMap();
+    MgLayerBase createLayer(MgResourceIdentifier resId);
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/ITestExecutor.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/ITestExecutor.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/ITestExecutor.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,10 @@
+package org.osgeo.mapguide.test.common;
+
+public interface ITestExecutor extends java.io.Closeable
+{
+    String getApi();
+
+    String getOperationName();
+
+    TestResult Execute(int paramSetId);
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/ITestExecutorCollection.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/ITestExecutorCollection.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/ITestExecutorCollection.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,56 @@
+package org.osgeo.mapguide.test.common;
+
+import com.almworks.sqlite4java.*;
+
+public interface ITestExecutorCollection
+{
+    /// <summary>
+    /// Initializes this collection of test executors
+    /// </summary>
+    /// <param name="testExecutionMode"></param>
+    /// <param name="dumpFile"></param>
+    void Initialize(String testExecutionMode, String dumpFile);
+
+    /// <summary>
+    /// Gets the dump file path
+    /// </summary>
+    String getDumpFile();
+
+    /// <summary>
+    /// Gets the test execution mode
+    /// </summary>
+    String getTestExecutionMode();
+
+    /// <summary>
+    /// Gets the test executor of the specified operation name
+    /// </summary>
+    /// <param name="opName"></param>
+    /// <returns></returns>
+    ITestExecutor GetTestExecutor(String opName);
+
+    /// <summary>
+    /// Executes the test suite as defined in the dump file
+    /// </summary>
+    /// <param name="testsRun"></param>
+    /// <param name="logger"></param>
+    /// <param name="isEnterprise"></param>
+    /// <returns></returns>
+    int Execute(MutableInteger testsRun, ITestLogger logger, boolean isEnterprise);
+
+    /// <summary>
+    /// Validates a test execution result
+    /// </summary>
+    /// <param name="db"></param>
+    /// <param name="testName"></param>
+    /// <param name="paramSetId"></param>
+    /// <param name="operation"></param>
+    /// <param name="actualResult"></param>
+    /// <param name="logger"></param>
+    /// <returns></returns>
+    int ValidateRequest(SQLiteConnection db, String testName, int paramSetId, String operation, TestResult actualResult, ITestLogger logger);
+
+    /// <summary>
+    /// Performs necessary cleanup
+    /// </summary>
+    void Cleanup();
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/ITestLogger.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/ITestLogger.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/ITestLogger.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,9 @@
+package org.osgeo.mapguide.test.common;
+
+import java.io.*;
+
+public interface ITestLogger extends Closeable
+{
+    void Write(String format, Object ... args);
+    void WriteLine(String format, Object ... args);
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/MissingTestExecutorException.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/MissingTestExecutorException.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/MissingTestExecutorException.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+package org.osgeo.mapguide.test.common;
+
+public class MissingTestExecutorException extends RuntimeException
+{
+    public MissingTestExecutorException(String message) {
+        super(message);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/MutableInteger.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/MutableInteger.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/MutableInteger.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,22 @@
+package org.osgeo.mapguide.test.common;
+
+public class MutableInteger
+{
+    private int _value;
+
+    public MutableInteger(int value) {
+        _value = value;
+    }
+
+    public int increment() {
+        _value++;
+        return _value;
+    }
+
+    public int decrement() {
+        _value--;
+        return _value;
+    }
+
+    public int getValue() { return _value; }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/PlatformApiTestExecutor.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/PlatformApiTestExecutor.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/PlatformApiTestExecutor.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,50 @@
+package org.osgeo.mapguide.test.common;
+
+import java.io.File;
+import java.util.HashMap;
+import com.almworks.sqlite4java.*;
+
+public abstract class PlatformApiTestExecutor extends TestExecutorBase
+{
+    private String _opName;
+    private String _apiType;
+    protected SQLiteConnection _conn;
+
+    protected PlatformApiTestExecutor(String opName, String apiType, String dbPath)
+    {
+        _opName = opName;
+        _apiType = apiType;
+        try {
+            _conn = new SQLiteConnection(new File(dbPath));
+            _conn.open(false);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    protected void ReadParameterValue(int paramSetId, String paramName, HashMap<String, String> paramValues) {
+        CommonUtility.ReadParameterValue(_conn, paramSetId, paramName, paramValues);
+    }
+
+    protected void ReadParameterValue(int paramSetId, String paramName, HashMap<String, String> paramValues, boolean isPath) {
+        CommonUtility.ReadParameterValue(_conn, paramSetId, paramName, paramValues, isPath);
+    }
+
+    public void close()
+    {
+        if (_conn != null) {
+            _conn.dispose();
+            _conn = null;
+        }
+    }
+
+    public String getApi()
+    {
+        return _apiType;
+    }
+
+    public String getOperationName()
+    {
+        return _opName;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestExecutionRun.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestExecutionRun.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestExecutionRun.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,119 @@
+package org.osgeo.mapguide.test.common;
+
+import java.io.File;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+
+public class TestExecutionRun implements java.io.Closeable
+{
+    private ITestExecutorCollection _executors;
+    private SQLiteConnection _unitTestDb;
+
+    public TestExecutionRun(String dbPath, ITestExecutorCollection exec) throws SQLiteException
+    {
+        _executors = exec;
+        _unitTestDb = new SQLiteConnection(new File(dbPath));
+        _unitTestDb.open(false);
+    }
+
+    public void close()
+    {
+        try
+        {
+            _unitTestDb.dispose();
+        }
+        catch (Exception ex) {
+
+        }
+        _unitTestDb = null;
+    }
+
+    private int[] ParamSetStrToArray(String pSet) {
+        if (CommonUtility.IsNullOrEmpty(pSet))
+            return new int[0];
+
+        String[] parts = pSet.split(",");
+        int[] result = new int[parts.length];
+        int i = 0;
+        for (String s : parts) {
+            result[i] = Integer.parseInt(s.trim());
+            i++;
+        }
+        return result;
+    }
+
+    public int RunTests(String testName, ITestLogger logger, MutableInteger testsRun) throws SQLiteException
+    {
+        TestResult actualResult = null;
+        int exitStatus = 0;
+        try
+        {
+            //Add the test in the log file
+            String msg = "\n\n************************************************************\n" +
+                         String.format("Executing %s\n", testName) +
+                         "************************************************************\n\n";
+
+            logger.Write(msg);
+
+            //Get the list of operations to be executed for this test. This list should comma separated, no spaces.
+            SQLiteStatement stmt = _unitTestDb.prepare(String.format("Select ParamSets from TestCase where TestName=\"%s\"", testName));
+            while (stmt.step()) {
+                String sets = CommonUtility.GetStringFromStatement(stmt, "ParamSets");
+
+                //Extract the paramset ids
+                int[] paramSet = ParamSetStrToArray(sets);
+                for (int paramSetId : paramSet)
+                {
+                    SQLiteStatement pstmt = _unitTestDb.prepare(String.format("Select ParamValue from Params where ParamSet=%d AND ParamName=\"OPERATION\"", paramSetId));
+                    while (pstmt.step()) {
+                        String paramValue = CommonUtility.GetStringFromStatement(pstmt, "ParamValue");
+
+                        //Add the operation to the log file
+                        AddLogFileEntry(logger, paramValue, paramSetId);
+
+                        try {
+                            ITestExecutor exec = _executors.GetTestExecutor(paramValue);
+                            if (DebugSettings.Enabled)
+                                Console.WriteLine("\tExecuting operation: %s", exec.getOperationName());
+                            actualResult = exec.Execute(paramSetId);
+                            testsRun.increment();
+                            exitStatus += _executors.ValidateRequest(_unitTestDb, testName, paramSetId, paramValue, actualResult, logger);
+                        } catch (MissingTestExecutorException ex) {
+                            if (DebugSettings.Enabled)
+                                Console.WriteLine("No test executor for %s in test: %s. Skipping test.", paramValue, testName);
+                        }
+                    }
+                }
+            }
+            return exitStatus;
+        }
+        catch (MgException ex)
+        {
+            throw new UnitTestException(String.format("Exception from MapGuide:\n%s", ex.getDetails()));
+        }
+    }
+
+    private void AddLogFileEntry(ITestLogger logger, String operation, int paramSetId) throws SQLiteException
+    {
+        logger.Write("\nParamSet: %s\n", paramSetId);
+        SQLiteStatement stmt = _unitTestDb.prepare(String.format("Select * from Params where ParamSet=%d", paramSetId));
+
+        while (stmt.step())
+        {
+            String paramName = CommonUtility.GetStringFromStatement(stmt, "ParamName");
+            String paramValue = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+
+            logger.Write("%s: %s\n", paramName, paramValue);
+        }
+    }
+
+    public void Cleanup()
+    {
+        if (_executors != null)
+        {
+            _executors.Cleanup();
+            _executors = null;
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestExecutorBase.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestExecutorBase.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestExecutorBase.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,12 @@
+package org.osgeo.mapguide.test.common;
+
+public abstract class TestExecutorBase implements ITestExecutor
+{
+    public abstract String getApi();
+
+    public abstract String getOperationName();
+
+    public abstract TestResult Execute(int paramSetId);
+
+    public abstract void close();
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestExecutorCollectionBase.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestExecutorCollectionBase.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestExecutorCollectionBase.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,81 @@
+package org.osgeo.mapguide.test.common;
+
+import java.io.File;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.test.*;
+
+public abstract class TestExecutorCollectionBase implements ITestExecutorCollection
+{
+    public String TestExecutionMode;
+    public String DumpFile;
+
+    public String getTestExecutionMode() { return this.TestExecutionMode; }
+    public String getDumpFile() { return this.DumpFile; }
+
+    public abstract String getApiType();
+
+    public void Initialize(String testExecutionMode, String dumpFile)
+    {
+        this.TestExecutionMode = testExecutionMode;
+        this.DumpFile = dumpFile;
+    }
+
+    protected abstract void SetupExecutors(String dbPath);
+
+    public int Execute(MutableInteger testsRun, ITestLogger logger, boolean isEnterprise)
+    {
+        int exitStatus = 0;
+        
+        try {
+
+            String dbPath = CommonUtility.GetDbPath(this.DumpFile);
+            String dbName = CommonUtility.GetPath(dbPath);
+
+            if (CommonUtility.FileExists(dbName))
+            {
+                SQLiteConnection db = new SQLiteConnection(new File(dbName));
+                try {
+                    db.open(false);
+
+                    SQLiteStatement stmt = db.prepare(String.format("Select TestName, TestType from TestCase where TestType=\"%s\" order by ExecuteSequence", this.getApiType()));
+
+                    //NOTE: We can't share the SqliteVm instance among our executor objects as this messes up query results
+                    //we must be able to re-create a new SqliteVm for each executor, so we pass down the db path
+                    SetupExecutors(dbName);
+
+                    while (stmt.step()) {
+                        String testName = CommonUtility.GetStringFromStatement(stmt, "TestName");
+                        String testType = CommonUtility.GetStringFromStatement(stmt, "TestType");
+                        Console.WriteLine("Executing %s test: %s", testType, testName);
+                        try (TestExecutionRun run = new TestExecutionRun(dbPath, this))
+                        {
+                            try
+                            {
+                                exitStatus += run.RunTests(testName, logger, testsRun);
+                            }
+                            catch (Exception ex)
+                            {
+                                Console.WriteLine(CommonUtility.ExceptionToString(ex));
+                                exitStatus += 1;
+                            }
+                        }
+                    }
+                } finally {
+                    db.dispose();
+                }
+            }
+
+        } catch (Exception ex) {
+            logger.WriteLine("Exception occurred during test. %s", CommonUtility.ExceptionToString(ex));
+            exitStatus += 1;
+        } 
+
+        return exitStatus;
+    }
+
+    public abstract ITestExecutor GetTestExecutor(String opName);
+
+    public abstract int ValidateRequest(SQLiteConnection db, String testName, int paramSetId, String operation, TestResult actualResult, ITestLogger logger);
+    
+    public abstract void Cleanup();
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestLoggerFile.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestLoggerFile.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestLoggerFile.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,54 @@
+package org.osgeo.mapguide.test.common;
+
+import java.io.*;
+
+public class TestLoggerFile implements ITestLogger
+{
+    private Writer sw;
+
+    public TestLoggerFile(String file)
+    {
+        try {
+            sw = new PrintWriter(file, "UTF-8");
+        } catch (IOException ex) {
+
+        }
+    }
+
+    public void Write(String format, Object ... args)
+    {
+        try {
+            if (args.length > 0)
+                sw.write(String.format(format, args));
+            else
+                sw.write(format);
+        } catch (IOException ex) {
+
+        }
+    }
+
+    public void WriteLine(String format, Object ... args)
+    {
+        try {
+            if (args.length > 0)
+                sw.write(String.format(format, args) + "\n");
+            else
+                sw.write(format + "\n");
+        } catch (IOException ex) {
+
+        }
+    }
+
+    public void close()
+    {
+        if (sw != null)
+        {
+            try {
+                sw.close();
+            } catch (IOException ex) {
+
+            }
+            sw = null;
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestResult.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestResult.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/TestResult.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,162 @@
+package org.osgeo.mapguide.test.common;
+
+import java.util.UUID;
+import java.text.DecimalFormat;
+
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+
+public class TestResult
+{
+    public Object ResultData;
+
+    public String ContentType;
+
+    public String HttpStatusCode;
+
+    public boolean HasReturnValue;
+
+    public boolean IsException;
+
+    private TestResult()
+    {
+        this.ResultData = "";
+        this.ContentType = "";
+        this.HttpStatusCode = "";
+        this.HasReturnValue = false;
+        this.IsException = false;
+    }
+
+    private TestResult(String resultData)
+    {
+        this.ResultData = resultData;
+        this.ContentType = "";
+        this.HttpStatusCode = "";
+        this.HasReturnValue = true;
+        this.IsException = false;
+    }
+
+    private TestResult(String resultData, String contentType)
+    {
+        this.ResultData = resultData;
+        this.ContentType = contentType;
+        this.HttpStatusCode = "";
+        this.HasReturnValue = true;
+        this.IsException = false;
+    }
+
+    private TestResult(String resultData, String contentType, String statusCode)
+    {
+        this.ResultData = resultData;
+        this.ContentType = contentType;
+        this.HttpStatusCode = statusCode;
+        this.HasReturnValue = true;
+        this.IsException = false;
+    }
+
+    public static TestResult Integer(int value) {
+        return new TestResult(value + "", "text/plain");
+    }
+
+    public static TestResult StringCollection(MgStringCollection value) {
+        return new TestResult(CommonUtility.MgStringCollectionToString(value), "text/plain");
+    }
+
+    public static TestResult XmlString(String value) {
+        return new TestResult(value, "text/xml");
+    }
+
+    public static TestResult Point(MgPoint value) {
+        return new TestResult(CommonUtility.MgPointToString(value), "text/plain");
+    }
+
+    public static TestResult Envelope(MgEnvelope value) {
+        return new TestResult(CommonUtility.MgEnvelopeToString(value), "text/plain");
+    }
+
+    public static TestResult Double(double value) {
+        /*
+        DecimalFormat format = new DecimalFormat("0.#");
+        if (value < 0.0)
+            return new TestResult("-" + format.format(Math.abs(value)), "text/plain");
+        else
+            return new TestResult(format.format(value), "text/plain");
+        */
+       return new TestResult(String.format("%g", value), "text/plain");
+    }
+
+    public static TestResult Boolean(boolean value) {
+        return new TestResult(CommonUtility.BooleanToString(value), "text/plain");
+    }
+
+    public static TestResult String(String value) {
+        return new TestResult(value, "text/plain");
+    }
+
+    public static TestResult Ok() {
+        return new TestResult();
+    }
+
+    public static TestResult FromByteReader(MgByteReader byteReader)
+    {
+        return FromByteReader(byteReader, "");
+    }
+
+    public static TestResult FromByteReader(MgByteReader byteReader, String operation)
+    {
+        try
+        {
+            TestResult res = new TestResult();
+            if (byteReader != null)
+            {
+                res.ContentType = byteReader.getMimeType();
+                if (res.ContentType.equals(MgMimeType.Html) ||
+                    res.ContentType.equals(MgMimeType.Json) ||
+                    res.ContentType.equals(MgMimeType.Kml) ||
+                    res.ContentType.equals(MgMimeType.Text) ||
+                    res.ContentType.equals(MgMimeType.Xml))
+                {
+                    res.ResultData = byteReader.toString();
+                }
+                else
+                {
+                    MgByteSink sink = new MgByteSink(byteReader);
+                    String path = operation + UUID.randomUUID().toString() + "Result.bin";
+                    if (CommonUtility.IsNullOrEmpty(operation))
+                        path = CommonUtility.GetTempFileName();
+                    sink.toFile(path);
+                    res.ResultData = CommonUtility.ReadAllBytes(path);
+                    if (CommonUtility.IsNullOrEmpty(operation))
+                        CommonUtility.DeleteFile(path);
+                    else
+                        System.out.println(String.format("[MgUnitTest]: Check out %s if binary comparison results are strange", path));
+                    /*
+                    byte[] bytes = new byte[byteReader.GetLength()];
+                    byteReader.Read(bytes, bytes.Length);
+                    res.ResultData = bytes;
+                    */
+                }
+            }
+            return res;
+        }
+        catch (MgException ex)
+        {
+            return FromMgException(ex);
+        }
+    }
+
+    public static TestResult FromMgException(MgException ex)
+    {
+        //Need to be lowercase to satisfy a PHP-ism. Ugh!
+        TestResult result = new TestResult(CommonUtility.GetTypeName(ex).toLowerCase(), "text/plain");
+        result.IsException = true;
+        return result;
+    }
+
+    public static TestResult FromException(Exception ex)
+    {
+        TestResult result = new TestResult(ex.getMessage(), "text/plain");
+        result.IsException = true;
+        return result;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/UnitTestException.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/UnitTestException.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/UnitTestException.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+package org.osgeo.mapguide.test.common;
+
+public class UnitTestException extends RuntimeException
+{
+    public UnitTestException(String message) {
+        super(message);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/XmlCompareResult.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/XmlCompareResult.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/common/XmlCompareResult.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,10 @@
+package org.osgeo.mapguide.test.common;
+
+public class XmlCompareResult
+{
+    public boolean Result;
+    public String Expected;
+    public String Actual;
+
+    public boolean HasXml() { return this.Expected != null && this.Actual != null; }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/ReadOnlyLayerCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/ReadOnlyLayerCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/ReadOnlyLayerCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,78 @@
+package org.osgeo.mapguide.test.mapguide;
+
+import java.io.*;
+import java.util.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class ReadOnlyLayerCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgFeatureQueryOptions query1 = new MgFeatureQueryOptions();
+        query1.setFilter("Autogenerated_SDF_ID < 4");
+
+        MgFeatureQueryOptions query2 = new MgFeatureQueryOptions();
+        query2.setFilter("Autogenerated_SDF_ID < 4");
+
+        MgMap map = (MgMap)factory.createMap();
+        MgResourceService resSvc = (MgResourceService)factory.createService(MgServiceType.ResourceService);
+        MgResourceIdentifier mdfId = new MgResourceIdentifier("Library://Samples/Sheboygan/Maps/Sheboygan.MapDefinition");
+        map.create(mdfId, "Test");
+        map.save();
+
+        MgLayerCollection layers = map.getLayers();
+        MgLayerBase item1 = layers.getItem("Parcels");
+        MgLayerBase item2 = layers.getItem("Buildings");
+
+        MgFeatureReader fr1 = item1.selectFeatures(query1);
+        MgFeatureReader fr2 = item2.selectFeatures(query2);
+
+        MgSelection sel = new MgSelection(map);
+        try {
+            sel.fromXml(""); //Clear
+            sel.addFeatures(item1, fr1, 0);
+            sel.addFeatures(item2, fr2, 0);
+        } finally{
+            fr1.close();
+            fr2.close();
+        }
+
+        //Now we can test the collection
+        MgReadOnlyLayerCollection items = sel.getLayers();
+
+        Assert.IsTrue(items.getCount() == 2, "Expected 2 items");
+
+        //NOTE: Possible bug in ordering of MgReadOnlyLayerCollection here (I had to reverse the order of layers
+        //being tested) Nevertheless, were testing that the ordering (whether correct or not) is the same when iterated
+        //in both the classical and iterative methods
+
+        int i = 0;
+        //Looping the classic way
+        for (i = 0; i < items.getCount(); i++) {
+            switch (i){
+                case 0:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Buildings"), "Expected item #" + (i+1) + " to be Buildings");
+                    break;
+                case 1:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Parcels"), "Expected item #" + (i+1) + " to be Parcels");
+                    break;
+            }
+        }
+
+        //Looping the iterative way
+        i = 0;
+        for (MgLayerBase item : items) {
+            String itemName = item.getName();
+            switch (i){
+                case 0:
+                    Assert.IsTrue(itemName.equals("Buildings"), "Expected item #" + (i+1) + " to be Buildings");
+                    break;
+                case 1:
+                    Assert.IsTrue(itemName.equals("Parcels"), "Expected item #" + (i+1) + " to be Parcels");
+                    break;
+            }
+            i++;
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/RenderingServiceTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/RenderingServiceTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/RenderingServiceTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,86 @@
+package org.osgeo.mapguide.test.mapguide;
+
+import java.io.*;
+import java.nio.file.*;
+import java.util.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class RenderingServiceTest implements IExternalTest
+{
+    private static void LoadResource(MgResourceService resSvc, String resIdStr, String path)
+    {
+        MgResourceIdentifier resId = new MgResourceIdentifier(resIdStr);
+        MgByteSource bs = new MgByteSource(path);
+        MgByteReader br = bs.getReader();
+        resSvc.setResource(resId, br, null);
+    }
+
+    private static void LoadResourceData(MgResourceService resSvc, String resIdStr, String dataName, String dataType, String path)
+    {
+        MgResourceIdentifier resId = new MgResourceIdentifier(resIdStr);
+        MgByteSource bs = new MgByteSource(path);
+        MgByteReader br = bs.getReader();
+        resSvc.setResourceData(resId, dataName, dataType, br);
+    }
+
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgResourceService resSvc = (MgResourceService)factory.createService(MgServiceType.ResourceService);
+        MgRenderingService renderSvc = (MgRenderingService)factory.createService(MgServiceType.RenderingService);
+        String root = "../../TestData/TileService/";
+
+        LoadResource(resSvc, "Library://UnitTests/Data/RoadCenterLines.FeatureSource", root + "UT_RoadCenterLines.fs");
+        LoadResourceData(resSvc, "Library://UnitTests/Data/RoadCenterLines.FeatureSource", "UT_RoadCenterLines.sdf", MgResourceDataType.File, root + "UT_RoadCenterLines.sdf");
+        LoadResource(resSvc, "Library://UnitTests/Layers/RoadCenterLines.LayerDefinition", root + "UT_RoadCenterLines.ldf");
+
+        LoadResource(resSvc, "Library://UnitTests/Data/VotingDistricts.FeatureSource", root + "UT_VotingDistricts.fs");
+        LoadResourceData(resSvc, "Library://UnitTests/Data/VotingDistricts.FeatureSource", "UT_VotingDistricts.sdf", MgResourceDataType.File, root + "UT_VotingDistricts.sdf");
+        LoadResource(resSvc, "Library://UnitTests/Layers/VotingDistricts.LayerDefinition", root + "UT_VotingDistricts.ldf");
+
+        LoadResource(resSvc, "Library://UnitTests/Data/Parcels.FeatureSource", root + "UT_Parcels.fs");
+        LoadResourceData(resSvc, "Library://UnitTests/Data/Parcels.FeatureSource", "UT_Parcels.sdf", MgResourceDataType.File, root + "UT_Parcels.sdf");
+        LoadResource(resSvc, "Library://UnitTests/Layers/Parcels.LayerDefinition", root + "UT_Parcels.ldf");
+
+        LoadResource(resSvc, "Library://UnitTests/Maps/BaseMap.MapDefinition", root + "UT_BaseMap.mdf");
+
+        MgMap map = (MgMap)factory.createMap();
+        map.create(new MgResourceIdentifier("Library://UnitTests/Maps/BaseMap.MapDefinition"), "BaseMap");
+        MgByteReader img = renderSvc.renderTile(map, "BaseLayers", 0, 0);
+        CheckValidImage(img);
+        img = renderSvc.renderTile(map, "BaseLayers", 0, 0, 256, 256, 96, MgImageFormats.Png);
+        CheckValidImage(img); //, 256, 256, MgImageFormats.Png);
+        img = renderSvc.renderTile(map, "BaseLayers", 0, 0, 256, 256, 96, MgImageFormats.Gif);
+        CheckValidImage(img); //, 256, 256, MgImageFormats.Gif);
+        img = renderSvc.renderTile(map, "BaseLayers", 0, 0, 256, 256, 96, MgImageFormats.Jpeg);
+        CheckValidImage(img); //, 256, 256, MgImageFormats.Jpeg);
+        img = renderSvc.renderTile(map, "BaseLayers", 0, 0, 256, 256, 96, MgImageFormats.Png8);
+        CheckValidImage(img); //, 256, 256, MgImageFormats.Png8);
+        img = renderSvc.renderTileXYZ(map, "BaseLayers", 2099, 2985, 13);
+        CheckValidImage(img); //, 256, 256);
+        img = renderSvc.renderTileXYZ(map, "BaseLayers", 2099, 2985, 13, 96, MgImageFormats.Png);
+        CheckValidImage(img); //, 256, 256, MgImageFormats.Png);
+        img = renderSvc.renderTileXYZ(map, "BaseLayers", 2099, 2985, 13, 96, MgImageFormats.Png8);
+        CheckValidImage(img); //, 256, 256, MgImageFormats.Png8);
+        img = renderSvc.renderTileXYZ(map, "BaseLayers", 2099, 2985, 13, 96, MgImageFormats.Gif);
+        CheckValidImage(img); //, 256, 256, MgImageFormats.Gif);
+        img = renderSvc.renderTileXYZ(map, "BaseLayers", 2099, 2985, 13, 96, MgImageFormats.Jpeg);
+        CheckValidImage(img); //, 256, 256, MgImageFormats.Jpeg);
+    }
+
+    private void CheckValidImage(MgByteReader img)
+    {
+        try
+        {
+            MgByteSink sink = new MgByteSink(img);
+            sink.toFile("tile.img");
+            File f = new File("tile.img");
+            Assert.IsTrue(f.exists());
+            Assert.IsTrue(f.length() > 0);
+        }
+        catch (Exception ex)
+        {
+            throw new AssertException("Invalid image found: " + ex.getMessage());
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/ClearLog.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/ClearLog.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/ClearLog.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,31 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class ClearLog extends ServerAdminOperationExecutor
+{
+    public ClearLog(MgServerAdmin admin, String unitTestVm)
+    {
+        super("ClearLog", admin, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "LOGTYPE", param);
+
+            boolean cleared = _serverAdmin.clearLog(param.get("LOGTYPE"));
+            return TestResult.Boolean(cleared);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/DeleteLog.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/DeleteLog.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/DeleteLog.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,31 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class DeleteLog extends ServerAdminOperationExecutor
+{
+    public DeleteLog(MgServerAdmin admin, String unitTestVm)
+    {
+        super("DeleteLog", admin, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "FILENAME", param);
+
+            _serverAdmin.deleteLog(param.get("FILENAME"));
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/DeletePackage.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/DeletePackage.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/DeletePackage.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,31 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class DeletePackage extends ServerAdminOperationExecutor
+{
+    public DeletePackage(MgServerAdmin admin, String unitTestVm)
+    {
+        super("DeletePackage", admin, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "PACKAGENAME", param);
+
+            _serverAdmin.deletePackage(param.get("PACKAGENAME"));
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/EnumeratePackages.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/EnumeratePackages.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/EnumeratePackages.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,28 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class EnumeratePackages extends ServerAdminOperationExecutor
+{
+    public EnumeratePackages(MgServerAdmin admin, String unitTestVm)
+    {
+        super("EnumeratePackages", admin, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            MgStringCollection packages = _serverAdmin.enumeratePackages();
+            return TestResult.StringCollection(packages);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetLog.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetLog.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetLog.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,41 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GetLog extends ServerAdminOperationExecutor
+{
+    public GetLog(MgServerAdmin admin, String unitTestVm)
+    {
+        super("GetLog", admin, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "LOGTYPE", param);
+            ReadParameterValue(paramSetId, "NUMENTRIES", param);
+
+            MgByteReader reader = null;
+            if (param.get("NUMENTRIES") == null)
+            {
+                reader = _serverAdmin.getLog(param.get("LOGTYPE"));
+            }
+            else
+            {
+                reader = _serverAdmin.getLog(param.get("LOGTYPE"), Integer.parseInt(param.get("NUMENTRIES")));
+            }
+
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetLogByDate.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetLogByDate.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetLogByDate.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,60 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GetLogByDate extends ServerAdminOperationExecutor
+{
+    public GetLogByDate(MgServerAdmin admin, String unitTestVm)
+    {
+        super("GetLogByDate", admin, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "LOGTYPE", param);
+            ReadParameterValue(paramSetId, "FROMDATE", param);
+            ReadParameterValue(paramSetId, "TODATE", param);
+
+            String[] fromDatePieces = (param.get("FROMDATE") == null ? "" : param.get("FROMDATE")).split(",");
+            String[] toDatePieces = (param.get("TODATE") == null ? "" : param.get("TODATE")).split(",");
+
+            MgDateTime fromDate = null;
+            MgDateTime toDate = null;
+
+            if (fromDatePieces.length == 3)
+            {
+                fromDate = new MgDateTime(Short.parseShort(fromDatePieces[0]), Short.parseShort(fromDatePieces[1]), Short.parseShort(fromDatePieces[2]));
+            }
+            else if (fromDatePieces.length == 7)
+            {
+                fromDate = new MgDateTime(Short.parseShort(fromDatePieces[0]), Short.parseShort(fromDatePieces[1]), Short.parseShort(fromDatePieces[2]), Short.parseShort(fromDatePieces[3]), Short.parseShort(fromDatePieces[4]), Short.parseShort(fromDatePieces[5]), Integer.parseInt(fromDatePieces[6]));
+            }
+
+            if (toDatePieces.length == 3)
+            {
+                toDate = new MgDateTime(Short.parseShort(toDatePieces[0]), Short.parseShort(toDatePieces[1]), Short.parseShort(toDatePieces[2]));
+            }
+            else if (toDatePieces.length == 7)
+            {
+                toDate = new MgDateTime(Short.parseShort(toDatePieces[0]), Short.parseShort(toDatePieces[1]), Short.parseShort(toDatePieces[2]), Short.parseShort(toDatePieces[3]), Short.parseShort(toDatePieces[4]), Short.parseShort(toDatePieces[5]), Integer.parseInt(toDatePieces[6]));
+            }
+
+            String logType = "";
+            if (param.get("LOGTYPE") != null)
+                logType = param.get("LOGTYPE");
+            MgByteReader reader = _serverAdmin.getLog(logType, fromDate, toDate);
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetPackageLog.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetPackageLog.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetPackageLog.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,31 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GetPackageLog extends ServerAdminOperationExecutor
+{
+    public GetPackageLog(MgServerAdmin admin, String unitTestVm)
+    {
+        super("GetPackageLog", admin, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "PACKAGENAME", param);
+
+            MgByteReader reader = _serverAdmin.getPackageLog(param.get("PACKAGENAME"));
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetPackageStatus.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetPackageStatus.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/GetPackageStatus.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,32 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GetPackageStatus extends ServerAdminOperationExecutor
+{
+    public GetPackageStatus(MgServerAdmin admin, String unitTestVm)
+    {
+        super("GetPackageStatus", admin, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "PACKAGENAME", param);
+
+            MgPackageStatusInformation status = _serverAdmin.getPackageStatus(param.get("PACKAGENAME"));
+            String code = status.getStatusCode();
+            return TestResult.String(code);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/LoadPackage.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/LoadPackage.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/LoadPackage.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,31 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class LoadPackage extends ServerAdminOperationExecutor
+{
+    public LoadPackage(MgServerAdmin admin, String unitTestVm)
+    {
+        super("LoadPackage", admin, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "PACKAGENAME", param);
+
+            _serverAdmin.loadPackage(param.get("PACKAGENAME"));
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/Offline.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/Offline.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/Offline.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,28 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class Offline extends ServerAdminOperationExecutor
+{
+    public Offline(MgServerAdmin admin, String unitTestVm)
+    {
+        super("Offline", admin, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            _serverAdmin.takeOffline();
+            return TestResult.Boolean(_serverAdmin.isOnline());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/Online.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/Online.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/Online.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,28 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class Online extends ServerAdminOperationExecutor
+{
+    public Online(MgServerAdmin admin, String unitTestVm)
+    {
+        super("Online", admin, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            _serverAdmin.bringOnline();
+            return TestResult.Boolean(_serverAdmin.isOnline());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/RenameLog.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/RenameLog.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/RenameLog.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,32 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class RenameLog extends ServerAdminOperationExecutor
+{
+    public RenameLog(MgServerAdmin admin, String unitTestVm)
+    {
+        super("RenameLog", admin, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "OLDFILENAME", param);
+            ReadParameterValue(paramSetId, "NEWFILENAME", param);
+
+            _serverAdmin.renameLog(param.get("OLDFILENAME"), param.get("NEWFILENAME"));
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/ServerAdminOperationExecutor.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/ServerAdminOperationExecutor.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/admin/ServerAdminOperationExecutor.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,18 @@
+package org.osgeo.mapguide.test.mapguide.operations.admin;
+
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+import com.almworks.sqlite4java.*;
+
+public abstract class ServerAdminOperationExecutor extends PlatformApiTestExecutor
+{
+    protected MgServerAdmin _serverAdmin;
+
+    protected ServerAdminOperationExecutor(String opName, MgServerAdmin serverAdmin, String unitTestVm)
+    {
+        super(opName, ApiTypes.Platform, unitTestVm);
+        _serverAdmin = serverAdmin;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/DescribeDrawing.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/DescribeDrawing.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/DescribeDrawing.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,37 @@
+package org.osgeo.mapguide.test.mapguide.operations.drawing;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class DescribeDrawing extends DrawingServiceOperationExecutor
+{
+    public DescribeDrawing(MgDrawingService drawSvc, String vm)
+    {
+        super("DescribeDrawing", drawSvc, vm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.get("RESOURCEID") != null)
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader reader = _drawingService.describeDrawing(resId);
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/DrawingServiceOperationExecutor.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/DrawingServiceOperationExecutor.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/DrawingServiceOperationExecutor.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,18 @@
+package org.osgeo.mapguide.test.mapguide.operations.drawing;
+
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+import com.almworks.sqlite4java.*;
+
+public abstract class DrawingServiceOperationExecutor extends PlatformApiTestExecutor
+{
+    protected MgDrawingService _drawingService;
+
+    protected DrawingServiceOperationExecutor(String opName, MgDrawingService drawSvc, String unitTestVm)
+    {
+        super(opName, ApiTypes.Platform, unitTestVm);
+        _drawingService = drawSvc;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/EnumerateDrawingLayers.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/EnumerateDrawingLayers.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/EnumerateDrawingLayers.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,42 @@
+package org.osgeo.mapguide.test.mapguide.operations.drawing;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class EnumerateDrawingLayers extends DrawingServiceOperationExecutor
+{
+    public EnumerateDrawingLayers(MgDrawingService drawSvc, String vm)
+    {
+        super("EnumerateDrawingLayers", drawSvc, vm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "SECTION", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.get("RESOURCEID") != null)
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            String section = "";
+            if (param.get("SECTION") != null)
+                section = param.get("SECTION");
+            MgStringCollection coll = _drawingService.enumerateLayers(resId, section);
+            MgByteReader reader = coll.toXml();
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/EnumerateDrawingSectionResources.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/EnumerateDrawingSectionResources.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/EnumerateDrawingSectionResources.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,41 @@
+package org.osgeo.mapguide.test.mapguide.operations.drawing;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class EnumerateDrawingSectionResources extends DrawingServiceOperationExecutor
+{
+    public EnumerateDrawingSectionResources(MgDrawingService drawSvc, String vm)
+    {
+        super("EnumerateDrawingSectionResources", drawSvc, vm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "SECTION", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.get("RESOURCEID") != null)
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            String section = "";
+            if (param.get("SECTION") != null)
+                section = param.get("SECTION");
+            MgByteReader reader = _drawingService.enumerateSectionResources(resId, section);
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/EnumerateDrawingSections.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/EnumerateDrawingSections.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/EnumerateDrawingSections.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,37 @@
+package org.osgeo.mapguide.test.mapguide.operations.drawing;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class EnumerateDrawingSections extends DrawingServiceOperationExecutor
+{
+    public EnumerateDrawingSections(MgDrawingService drawSvc, String vm)
+    {
+        super("EnumerateDrawingSections", drawSvc, vm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.get("RESOURCEID") != null)
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader reader = _drawingService.enumerateSections(resId);
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawing.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawing.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawing.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,37 @@
+package org.osgeo.mapguide.test.mapguide.operations.drawing;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GetDrawing extends DrawingServiceOperationExecutor
+{
+    public GetDrawing(MgDrawingService drawSvc, String vm)
+    {
+        super("GetDrawing", drawSvc, vm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.get("RESOURCEID") != null)
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader reader = _drawingService.getDrawing(resId);
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawingLayer.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawingLayer.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawingLayer.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,39 @@
+package org.osgeo.mapguide.test.mapguide.operations.drawing;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GetDrawingLayer extends DrawingServiceOperationExecutor
+{
+    public GetDrawingLayer(MgDrawingService drawSvc, String vm)
+    {
+        super("GetDrawingLayer", drawSvc, vm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "SECTION", param);
+            ReadParameterValue(paramSetId, "LAYER", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.get("RESOURCEID") != null)
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader reader = _drawingService.getLayer(resId, param.get("SECTION"), param.get("LAYER"));
+            return TestResult.FromByteReader(reader, "GETDRAWINGLAYER");
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawingSection.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawingSection.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawingSection.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,38 @@
+package org.osgeo.mapguide.test.mapguide.operations.drawing;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GetDrawingSection extends DrawingServiceOperationExecutor
+{
+    public GetDrawingSection(MgDrawingService drawSvc, String vm)
+    {
+        super("GetDrawingSection", drawSvc, vm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "SECTION", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.get("RESOURCEID") != null)
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader reader = _drawingService.getSection(resId, param.get("SECTION"));
+            return TestResult.FromByteReader(reader, "GETDRAWINGSECTION");
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawingSectionResource.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawingSectionResource.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/drawing/GetDrawingSectionResource.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,38 @@
+package org.osgeo.mapguide.test.mapguide.operations.drawing;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GetDrawingSectionResource extends DrawingServiceOperationExecutor
+{
+    public GetDrawingSectionResource(MgDrawingService drawSvc, String vm)
+    {
+        super("GetDrawingSectionResource", drawSvc, vm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "RESOURCENAME", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.get("RESOURCEID") != null)
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader reader = _drawingService.getSectionResource(resId, param.get("RESOURCENAME"));
+            return TestResult.FromByteReader(reader, "GETDRAWINGSECTIONRESOURCE");
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/AddGroup.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/AddGroup.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/AddGroup.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,38 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class AddGroup extends SiteServiceOperationExecutor
+{
+    public AddGroup(MgSite site, String unitTestVm)
+    {
+        super("AddGroup", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "GROUP", param);
+            ReadParameterValue(paramSetId, "DESCRIPTION", param);
+
+            String group = param.get("GROUP");
+            if (group == null)
+                group = "";
+            String desc = param.get("DESCRIPTION");
+            if (desc == null)
+                desc = "";
+            _site.addGroup(group, desc);
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/AddServer.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/AddServer.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/AddServer.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class AddServer extends SiteServiceOperationExecutor
+{
+    public AddServer(MgSite site, String unitTestVm)
+    {
+        super("AddServer", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "NAME", param);
+            ReadParameterValue(paramSetId, "DESCRIPTION", param);
+            ReadParameterValue(paramSetId, "ADDRESS", param);
+
+            _site.addServer(param.get("NAME"), param.get("DESCRIPTION"), param.get("ADDRESS"));
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/AddUser.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/AddUser.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/AddUser.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,34 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class AddUser extends SiteServiceOperationExecutor
+{
+    public AddUser(MgSite site, String unitTestVm)
+    {
+        super("AddUser", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "USERID", param);
+            ReadParameterValue(paramSetId, "USERNAME", param);
+            ReadParameterValue(paramSetId, "PASSWORD", param);
+            ReadParameterValue(paramSetId, "DESCRIPTION", param);
+
+            _site.addUser(param.get("USERID"), param.get("USERNAME"), param.get("PASSWORD"), param.get("DESCRIPTION"));
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/CreateSession.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/CreateSession.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/CreateSession.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,42 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class CreateSession extends SiteServiceOperationExecutor
+{
+    private IMapGuideSession _session;
+
+    public CreateSession(MgSite site, String unitTestVm, IMapGuideSession session)
+    {
+        super("CreateSession", site, unitTestVm);
+        _session = session;
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            MgUserInformation user = new MgUserInformation();
+            user.setMgUsernamePassword("Administrator", "admin");
+            user.setLocale("en");
+
+            MgSite site = new MgSite();
+            site.open(user);
+
+            String session = site.createSession();
+            _session.setSessionID(session);
+            site.close();
+
+            return TestResult.String(session);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/DeleteGroups.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/DeleteGroups.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/DeleteGroups.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class DeleteGroups extends SiteServiceOperationExecutor
+{
+    public DeleteGroups(MgSite site, String unitTestVm)
+    {
+        super("DeleteGroups", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "GROUPS", param);
+
+            MgStringCollection groups = CommonUtility.StringToMgStringCollection(param.get("GROUPS"));
+            _site.deleteGroups(groups);
+
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/DeleteUsers.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/DeleteUsers.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/DeleteUsers.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class DeleteUsers extends SiteServiceOperationExecutor
+{
+    public DeleteUsers(MgSite site, String unitTestVm)
+    {
+        super("DeleteUsers", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "USERS", param);
+
+            MgStringCollection users = CommonUtility.StringToMgStringCollection(param.get("USERS"));
+
+            _site.deleteUsers(users);
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/DestroySession.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/DestroySession.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/DestroySession.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,28 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class DestroySession extends SiteServiceOperationExecutor
+{
+    public DestroySession(MgSite site, String unitTestVm)
+    {
+        super("DestroySession", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            //This is what PHP one is giving us
+            return TestResult.String("Not Implemented Yet");
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateGroups.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateGroups.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateGroups.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,38 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class EnumerateGroups extends SiteServiceOperationExecutor
+{
+    public EnumerateGroups(MgSite site, String unitTestVm)
+    {
+        super("EnumerateGroups", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "USER", param);
+            ReadParameterValue(paramSetId, "ROLE", param);
+
+            String user = param.get("USER");
+            if (user == null)
+                user = "";
+            String role = param.get("ROLE");
+            if (role == null)
+                role = "";
+            MgByteReader reader = _site.enumerateGroups(user, role);
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateGroups2.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateGroups2.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateGroups2.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,42 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class EnumerateGroups2 extends SiteServiceOperationExecutor
+{
+    public EnumerateGroups2(MgSite site, String unitTestVm)
+    {
+        super("EnumerateGroups2", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "USER", param);
+            ReadParameterValue(paramSetId, "LOGIN", param);
+            ReadParameterValue(paramSetId, "PASSWORD", param);
+
+            MgUserInformation userInfo = new MgUserInformation();
+            userInfo.setMgUsernamePassword(param.get("LOGIN"), param.get("PASSWORD"));
+            userInfo.setLocale("en");
+
+            MgSite site = new MgSite();
+            site.open(userInfo);
+
+            MgByteReader reader = site.enumerateGroups(param.get("USER"));
+            site.close();
+
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateRoles.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateRoles.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateRoles.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class EnumerateRoles extends SiteServiceOperationExecutor
+{
+    public EnumerateRoles(MgSite site, String unitTestVm)
+    {
+        super("EnumerateRoles", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "USER", param);
+            ReadParameterValue(paramSetId, "GROUP", param);
+
+            MgStringCollection roles = _site.enumerateRoles(param.get("USER"), param.get("GROUP"));
+
+            return TestResult.StringCollection(roles);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateRoles2.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateRoles2.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateRoles2.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,42 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class EnumerateRoles2 extends SiteServiceOperationExecutor
+{
+    public EnumerateRoles2(MgSite site, String unitTestVm)
+    {
+        super("EnumerateRoles2", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "USER", param);
+            ReadParameterValue(paramSetId, "LOGIN", param);
+            ReadParameterValue(paramSetId, "PASSWORD", param);
+
+            MgUserInformation userInfo = new MgUserInformation();
+            userInfo.setMgUsernamePassword(param.get("LOGIN"), param.get("PASSWORD"));
+            userInfo.setLocale("en");
+
+            MgSite site = new MgSite();
+            site.open(userInfo);
+
+            MgStringCollection roles = site.enumerateRoles(param.get("USER"));
+            site.close();
+
+            return TestResult.StringCollection(roles);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateServers.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateServers.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateServers.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,28 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class EnumerateServers extends SiteServiceOperationExecutor
+{
+    public EnumerateServers(MgSite site, String unitTestVm)
+    {
+        super("EnumerateServers", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            MgByteReader reader = _site.enumerateServers();
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateUsers.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateUsers.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/EnumerateUsers.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,50 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class EnumerateUsers extends SiteServiceOperationExecutor
+{
+    public EnumerateUsers(MgSite site, String unitTestVm)
+    {
+        super("EnumerateUsers", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "GROUP", param);
+            ReadParameterValue(paramSetId, "ROLE", param);
+            ReadParameterValue(paramSetId, "INCLUDEGROUPS", param);
+
+            MgByteReader reader = null;
+            String group = param.get("GROUP");
+            if (group == null)
+                group = "";
+            if (param.get("ROLE") != null)
+            {
+                String role = param.get("ROLE");
+                if (role == null)
+                    role = "";
+                String includeGroups = param.get("INCLUDEGROUPS");
+                if (includeGroups == null)
+                    includeGroups = "1";
+                reader = _site.enumerateUsers(group, role, (includeGroups.equals("1")));
+            }
+            else
+            {
+                reader = _site.enumerateUsers(group);
+            }
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GetUserForSession.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GetUserForSession.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GetUserForSession.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,40 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class GetUserForSession extends SiteServiceOperationExecutor
+{
+    private IMapGuideSession _session;
+
+    public GetUserForSession(MgSite site, String unitTestVm, IMapGuideSession session)
+    {
+        super("GetUserForSession", site, unitTestVm);
+        _session = session;
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            MgSite site = new MgSite();
+            MgUserInformation user = new MgUserInformation();
+            String sessionId = "";
+            if (_session.getSessionID() != null)
+                sessionId = _session.getSessionID();
+            user.setMgSessionId(sessionId);
+            site.open(user);
+            String userId = site.getUserForSession();
+            site.close();
+            return TestResult.String(userId);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GrantGroupMembershipsToUsers.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GrantGroupMembershipsToUsers.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GrantGroupMembershipsToUsers.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,36 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GrantGroupMembershipsToUsers extends SiteServiceOperationExecutor
+{
+    public GrantGroupMembershipsToUsers(MgSite site, String unitTestVm)
+    {
+        super("GrantGroupMembershipsToUsers", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "GROUPS", param);
+            ReadParameterValue(paramSetId, "USERS", param);
+
+            MgStringCollection groups = CommonUtility.StringToMgStringCollection(param.get("GROUPS"));
+            MgStringCollection users = CommonUtility.StringToMgStringCollection(param.get("USERS"));
+
+            _site.grantGroupMembershipsToUsers(groups, users);
+
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GrantRoleMembershipsToGroups.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GrantRoleMembershipsToGroups.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GrantRoleMembershipsToGroups.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,36 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GrantRoleMembershipsToGroups extends SiteServiceOperationExecutor
+{
+    public GrantRoleMembershipsToGroups(MgSite site, String unitTestVm)
+    {
+        super("GrantRoleMembershipsToGroups", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "ROLES", param);
+            ReadParameterValue(paramSetId, "GROUPS", param);
+
+            MgStringCollection roles = CommonUtility.StringToMgStringCollection(param.get("ROLES"));
+            MgStringCollection groups = CommonUtility.StringToMgStringCollection(param.get("GROUPS"));
+
+            _site.grantRoleMembershipsToGroups(roles, groups);
+
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GrantRoleMembershipsToUsers.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GrantRoleMembershipsToUsers.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/GrantRoleMembershipsToUsers.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,36 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GrantRoleMembershipsToUsers extends SiteServiceOperationExecutor
+{
+    public GrantRoleMembershipsToUsers(MgSite site, String unitTestVm)
+    {
+        super("GrantRoleMembershipsToUsers", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "ROLES", param);
+            ReadParameterValue(paramSetId, "USERS", param);
+
+            MgStringCollection roles = CommonUtility.StringToMgStringCollection(param.get("ROLES"));
+            MgStringCollection users = CommonUtility.StringToMgStringCollection(param.get("USERS"));
+
+            _site.grantRoleMembershipsToUsers(roles, users);
+
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RemoveServer.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RemoveServer.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RemoveServer.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,31 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class RemoveServer extends SiteServiceOperationExecutor
+{
+    public RemoveServer(MgSite site, String unitTestVm)
+    {
+        super("RemoveServer", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "NAME", param);
+
+            _site.removeServer(param.get("NAME"));
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RevokeGroupMembershipsFromUsers.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RevokeGroupMembershipsFromUsers.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RevokeGroupMembershipsFromUsers.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,36 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class RevokeGroupMembershipsFromUsers extends SiteServiceOperationExecutor
+{
+    public RevokeGroupMembershipsFromUsers(MgSite site, String unitTestVm)
+    {
+        super("RevokeGroupMembershipsFromUsers", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "GROUPS", param);
+            ReadParameterValue(paramSetId, "USERS", param);
+
+            MgStringCollection groups = CommonUtility.StringToMgStringCollection(param.get("GROUPS"));
+            MgStringCollection users = CommonUtility.StringToMgStringCollection(param.get("USERS"));
+
+            _site.revokeGroupMembershipsFromUsers(groups, users);
+
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RevokeRoleMembershipsFromGroups.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RevokeRoleMembershipsFromGroups.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RevokeRoleMembershipsFromGroups.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,36 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class RevokeRoleMembershipsFromGroups extends SiteServiceOperationExecutor
+{
+    public RevokeRoleMembershipsFromGroups(MgSite site, String unitTestVm)
+    {
+        super("RevokeRoleMembershipsFromGroups", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "ROLES", param);
+            ReadParameterValue(paramSetId, "GROUPS", param);
+
+            MgStringCollection roles = CommonUtility.StringToMgStringCollection(param.get("ROLES"));
+            MgStringCollection groups = CommonUtility.StringToMgStringCollection(param.get("GROUPS"));
+
+            _site.revokeRoleMembershipsFromGroups(roles, groups);
+
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RevokeRoleMembershipsFromUsers.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RevokeRoleMembershipsFromUsers.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/RevokeRoleMembershipsFromUsers.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,36 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class RevokeRoleMembershipsFromUsers extends SiteServiceOperationExecutor
+{
+    public RevokeRoleMembershipsFromUsers(MgSite site, String unitTestVm)
+    {
+        super("RevokeRoleMembershipsFromUsers", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "ROLES", param);
+            ReadParameterValue(paramSetId, "USERS", param);
+
+            MgStringCollection roles = CommonUtility.StringToMgStringCollection(param.get("ROLES"));
+            MgStringCollection users = CommonUtility.StringToMgStringCollection(param.get("USERS"));
+
+            _site.revokeRoleMembershipsFromUsers(roles, users);
+
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/SiteServiceOperationExecutor.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/SiteServiceOperationExecutor.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/SiteServiceOperationExecutor.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,18 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+import com.almworks.sqlite4java.*;
+
+public abstract class SiteServiceOperationExecutor extends PlatformApiTestExecutor
+{
+    protected MgSite _site;
+
+    protected SiteServiceOperationExecutor(String opName, MgSite site, String unitTestVm)
+    {
+        super(opName, ApiTypes.Platform, unitTestVm);
+        _site = site;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/UpdateGroup.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/UpdateGroup.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/UpdateGroup.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,42 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class UpdateGroup extends SiteServiceOperationExecutor
+{
+    public UpdateGroup(MgSite site, String unitTestVm)
+    {
+        super("UpdateGroup", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "GROUP", param);
+            ReadParameterValue(paramSetId, "NEWGROUP", param);
+            ReadParameterValue(paramSetId, "NEWDESCRIPTION", param);
+
+            String group = param.get("GROUP");
+            if (group == null)
+                group = "";
+            String newGroup = param.get("NEWGROUP");
+            if (newGroup == null)
+                newGroup = "";
+            String newDesc = param.get("NEWDESCRIPTION");
+            if (newDesc == null)
+                newDesc = "";
+            _site.updateGroup(group, newGroup, newDesc);
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/UpdateServer.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/UpdateServer.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/UpdateServer.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,34 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class UpdateServer extends SiteServiceOperationExecutor
+{
+    public UpdateServer(MgSite site, String unitTestVm)
+    {
+        super("UpdateServer", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "OLDNAME", param);
+            ReadParameterValue(paramSetId, "NEWNAME", param);
+            ReadParameterValue(paramSetId, "NEWDESCRIPTION", param);
+            ReadParameterValue(paramSetId, "NEWADDRESS", param);
+
+            _site.updateServer(param.get("OLDNAME"), param.get("NEWNAME"), param.get("NEWDESCRIPTION"), param.get("NEWADDRESS"));
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/UpdateUser.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/UpdateUser.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/site/UpdateUser.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,35 @@
+package org.osgeo.mapguide.test.mapguide.operations.site;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class UpdateUser extends SiteServiceOperationExecutor
+{
+    public UpdateUser(MgSite site, String unitTestVm)
+    {
+        super("UpdateUser", site, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "USERID", param);
+            ReadParameterValue(paramSetId, "NEWUSERID", param);
+            ReadParameterValue(paramSetId, "NEWUSERNAME", param);
+            ReadParameterValue(paramSetId, "NEWPASSWORD", param);
+            ReadParameterValue(paramSetId, "NEWDESCRIPTION", param);
+
+            _site.updateUser(param.get("USERID"), param.get("NEWUSERID"), param.get("NEWUSERNAME"), param.get("NEWPASSWORD"), param.get("NEWDESCRIPTION"));
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_BackDescription.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_BackDescription.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_BackDescription.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_BackDescription extends WebLayoutOperationExecutor
+{
+    public WL_BackDescription(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_BackDescription", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            MgWebTaskBar tbar = tp.getTaskBar();
+            MgWebWidgetCollection tbuttons = tbar.getTaskButtons();
+            MgWebTaskBarWidget back = (MgWebTaskBarWidget)tbuttons.getWidget(MgWebTaskButtonType.Back);
+            return TestResult.String(back.getDescription());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_BackTooltip.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_BackTooltip.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_BackTooltip.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_BackTooltip extends WebLayoutOperationExecutor
+{
+    public WL_BackTooltip(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_BackTooltip", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            MgWebTaskBar tbar = tp.getTaskBar();
+            MgWebWidgetCollection tbuttons = tbar.getTaskButtons();
+            MgWebTaskBarWidget back = (MgWebTaskBarWidget)tbuttons.getWidget(MgWebTaskButtonType.Back);
+            return TestResult.String(back.getTooltip());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ForwardDescription.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ForwardDescription.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ForwardDescription.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_ForwardDescription extends WebLayoutOperationExecutor
+{
+    public WL_ForwardDescription(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_ForwardDescription", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            MgWebTaskBar tbar = tp.getTaskBar();
+            MgWebWidgetCollection tbuttons = tbar.getTaskButtons();
+            MgWebTaskBarWidget fwd = (MgWebTaskBarWidget)tbuttons.getWidget(MgWebTaskButtonType.Forward);
+            return TestResult.String(fwd.getDescription());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ForwardTooltip.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ForwardTooltip.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ForwardTooltip.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_ForwardTooltip extends WebLayoutOperationExecutor
+{
+    public WL_ForwardTooltip(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_ForwardTooltip", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            MgWebTaskBar tbar = tp.getTaskBar();
+            MgWebWidgetCollection tbuttons = tbar.getTaskButtons();
+            MgWebTaskBarWidget fwd = (MgWebTaskBarWidget)tbuttons.getWidget(MgWebTaskButtonType.Forward);
+            return TestResult.String(fwd.getTooltip());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetCenter.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetCenter.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetCenter.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,38 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_GetCenter extends WebLayoutOperationExecutor
+{
+    public WL_GetCenter(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_GetCenter", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgPoint center = _wl.getCenter();
+            if (center == null)
+            {
+                return TestResult.String("");
+            }
+            else
+            {
+                MgCoordinate coord = center.getCoordinate();
+                return TestResult.String(CommonUtility.DoubleToString(coord.getX()) + "/" + CommonUtility.DoubleToString(coord.getY()));
+            }
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetInformationPaneWidth.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetInformationPaneWidth.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetInformationPaneWidth.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_GetInformationPaneWidth extends WebLayoutOperationExecutor
+{
+    public WL_GetInformationPaneWidth(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_GetInformationPaneWidth", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebInformationPane ip = _wl.getInformationPane();
+            return TestResult.Integer(ip.getWidth());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetInitialTaskUrl.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetInitialTaskUrl.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetInitialTaskUrl.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_GetInitialTaskUrl extends WebLayoutOperationExecutor
+{
+    public WL_GetInitialTaskUrl(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_GetInitialTaskUrl", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            return TestResult.String(tp.getInitialTaskUrl());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetMapDefinition.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetMapDefinition.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetMapDefinition.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,29 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_GetMapDefinition extends WebLayoutOperationExecutor
+{
+    public WL_GetMapDefinition(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_GetMapDefinition", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            return TestResult.String(_wl.getMapDefinition());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetScale.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetScale.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetScale.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,29 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_GetScale extends WebLayoutOperationExecutor
+{
+    public WL_GetScale(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_GetScale", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            return TestResult.Double(_wl.getScale());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetTaskPaneWidth.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetTaskPaneWidth.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetTaskPaneWidth.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_GetTaskPaneWidth extends WebLayoutOperationExecutor
+{
+    public WL_GetTaskPaneWidth(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_GetTaskPaneWidth", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            return TestResult.Integer(tp.getWidth());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetTitle.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetTitle.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_GetTitle.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,29 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_GetTitle extends WebLayoutOperationExecutor
+{
+    public WL_GetTitle(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_GetTitle", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            return TestResult.String(_wl.getTitle());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);   
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_HomeDescription.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_HomeDescription.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_HomeDescription.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_HomeDescription extends WebLayoutOperationExecutor
+{
+    public WL_HomeDescription(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_HomeDescription", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            MgWebTaskBar tbar = tp.getTaskBar();
+            MgWebWidgetCollection tbuttons = tbar.getTaskButtons();
+            MgWebTaskBarWidget home = (MgWebTaskBarWidget)tbuttons.getWidget(MgWebTaskButtonType.Home);
+            return TestResult.String(home.getDescription());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_HomeTooltip.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_HomeTooltip.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_HomeTooltip.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_HomeTooltip extends WebLayoutOperationExecutor
+{
+    public WL_HomeTooltip(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_HomeTooltip", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            MgWebTaskBar tbar = tp.getTaskBar();
+            MgWebWidgetCollection tbuttons = tbar.getTaskButtons();
+            MgWebTaskBarWidget home = (MgWebTaskBarWidget)tbuttons.getWidget(MgWebTaskButtonType.Home);
+            return TestResult.String(home.getTooltip());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowContextMenu.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowContextMenu.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowContextMenu.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_ShowContextMenu extends WebLayoutOperationExecutor
+{
+    public WL_ShowContextMenu(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_ShowContextMenu", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebContextMenu ctx = _wl.getContextMenu();
+            return TestResult.Boolean(ctx.isVisible());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowLegend.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowLegend.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowLegend.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_ShowLegend extends WebLayoutOperationExecutor
+{
+    public WL_ShowLegend(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_ShowLegend", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebInformationPane ip = _wl.getInformationPane();
+            return TestResult.Boolean(ip.isLegendBandVisible());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowProperties.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowProperties.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowProperties.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_ShowProperties extends WebLayoutOperationExecutor
+{
+    public WL_ShowProperties(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_ShowProperties", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebInformationPane ip = _wl.getInformationPane();
+            return TestResult.Boolean(ip.isPropertiesBandVisible());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowStatusbar.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowStatusbar.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowStatusbar.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_ShowStatusbar extends WebLayoutOperationExecutor
+{
+    public WL_ShowStatusbar(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_ShowStatusbar", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebUiPane sb = _wl.getStatusBar();
+            return TestResult.Boolean(sb.isVisible());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowTaskbar.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowTaskbar.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowTaskbar.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,31 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_ShowTaskbar extends WebLayoutOperationExecutor
+{
+    public WL_ShowTaskbar(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_ShowTaskbar", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            MgWebTaskBar tb = tp.getTaskBar();
+            return TestResult.Boolean(tb.isVisible());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowTaskpane.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowTaskpane.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowTaskpane.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_ShowTaskpane extends WebLayoutOperationExecutor
+{
+    public WL_ShowTaskpane(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_ShowTaskpane", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            return TestResult.Boolean(tp.isVisible());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowToolbar.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowToolbar.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_ShowToolbar.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_ShowToolbar extends WebLayoutOperationExecutor
+{
+    public WL_ShowToolbar(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_ShowToolbar", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebToolBar tb = _wl.getToolBar();
+            return TestResult.Boolean(tb.isVisible());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TasksDescription.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TasksDescription.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TasksDescription.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_TasksDescription extends WebLayoutOperationExecutor
+{
+    public WL_TasksDescription(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_TasksDescription", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            MgWebTaskBar tbar = tp.getTaskBar();
+            MgWebWidgetCollection tbuttons = tbar.getTaskButtons();
+            MgWebTaskBarWidget tasks = (MgWebTaskBarWidget)tbuttons.getWidget(MgWebTaskButtonType.Tasks);
+            return TestResult.String(tasks.getDescription());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TasksName.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TasksName.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TasksName.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_TasksName extends WebLayoutOperationExecutor
+{
+    public WL_TasksName(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_TasksName", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            MgWebTaskBar tbar = tp.getTaskBar();
+            MgWebWidgetCollection tbuttons = tbar.getTaskButtons();
+            MgWebTaskBarWidget tasks = (MgWebTaskBarWidget)tbuttons.getWidget(MgWebTaskButtonType.Tasks);
+            return TestResult.String(tasks.getName());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TasksTooltip.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TasksTooltip.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TasksTooltip.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_TasksTooltip extends WebLayoutOperationExecutor
+{
+    public WL_TasksTooltip(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_TasksTooltip", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+            MgWebTaskPane tp = _wl.getTaskPane();
+            MgWebTaskBar tbar = tp.getTaskBar();
+            MgWebWidgetCollection tbuttons = tbar.getTaskButtons();
+            MgWebTaskBarWidget tasks = (MgWebTaskBarWidget)tbuttons.getWidget(MgWebTaskButtonType.Tasks);
+            return TestResult.String(tasks.getTooltip());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TestUiItem.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TestUiItem.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WL_TestUiItem.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,55 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+public class WL_TestUiItem extends WebLayoutOperationExecutor
+{
+    public WL_TestUiItem(IWebLayoutCreator layout, String unitTestVm)
+    {
+        super("WL_TestUiItem", layout, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            CreateWebLayoutFromResource(paramSetId);
+
+            MgWebWidgetCollection coll = null;
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "CONTAINER", param);
+            ReadParameterValue(paramSetId, "INDEX", param);
+
+            switch (param.get("CONTAINER"))
+            {
+                case "toolbar":
+                    MgWebToolBar tb = _wl.getToolBar();
+                    coll = tb.getWidgets();
+                    break;
+                case "tasklist":
+                    MgWebTaskPane tp = _wl.getTaskPane();
+                    MgWebTaskBar tbar = tp.getTaskBar();
+                    coll = tbar.getTaskList();
+                    break;
+                case "contextmenu":
+                    coll = _wl.getContextMenu();
+                    break;
+            }
+
+            MgWebWidget widget = coll.getWidget(Integer.parseInt(param.get("INDEX")));
+            if (widget == null)
+                return TestResult.String("Null widget");
+            else
+                return TestResult.String(FormatProperties(widget));
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WebLayoutOperationExecutor.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WebLayoutOperationExecutor.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/mapguide/operations/weblayout/WebLayoutOperationExecutor.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,91 @@
+package org.osgeo.mapguide.test.mapguide.operations.weblayout;
+
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+
+import com.almworks.sqlite4java.*;
+
+public abstract class WebLayoutOperationExecutor extends PlatformApiTestExecutor
+{
+    protected MgWebLayout _wl;
+    protected IWebLayoutCreator _creator;
+
+    protected WebLayoutOperationExecutor(String name, IWebLayoutCreator wl, String unitTestVm)
+    {
+        super(name, ApiTypes.Platform, unitTestVm);
+        _creator = wl;
+    }
+
+    protected void CreateWebLayoutFromResource(int paramSetId)
+    {
+        if (_wl != null)
+            return;
+
+        try {
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"WEBLAYOUT\"", paramSetId));
+            String wlId = null;
+            if (stmt.step()) {
+                wlId = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+                if (CommonUtility.IsNullOrEmpty(wlId))
+                {
+                    wlId = "Library://UnitTest/layouts/Test.WebLayout";
+                }
+            } else {
+                wlId = "Library://UnitTest/layouts/Test.WebLayout";
+            }
+            MgResourceIdentifier resId = new MgResourceIdentifier(wlId);
+            _wl = _creator.CreateWebLayout(resId);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    protected String FormatProperties(MgWebWidget it)
+    {
+        String name = "";
+        switch (it.getType())
+        { 
+            case MgWebWidgetType.Separator:
+                name = "";
+                break;
+            case MgWebWidgetType.Command:
+                MgWebCommand cmd = ((MgWebCommandWidget)it).getCommand();
+                name = cmd.getLabel();
+                break;
+            case MgWebWidgetType.Flyout:
+                name = ((MgWebFlyoutWidget)it).getLabel();
+                break;
+        }
+
+        String str = "[" + name + "/" + it.getType();
+        if (it.getType() == MgWebWidgetType.Separator)
+        {
+            return str + "]";
+        }
+        else if (it.getType() == MgWebWidgetType.Command)
+        {
+            MgWebCommand cmd = ((MgWebCommandWidget)it).getCommand();
+            return str + "/" + cmd.getName() + "]";
+        }
+        else if (it.getType() == MgWebWidgetType.Flyout)
+        {
+            MgWebFlyoutWidget fly = ((MgWebFlyoutWidget)it);
+            str = str + "/" + fly.getIconUrl();
+            MgWebWidgetCollection coll = fly.getSubItems();
+            for (int i = 0; i < coll.getCount(); i++)
+            {
+                MgWebWidget widget = coll.getWidget(i);
+                str = str + "/" + FormatProperties(widget);
+            }
+            str = str + "]";
+        }
+        else
+        {
+            return "[** error **]";
+        }
+
+        return str;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IApplySession.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IApplySession.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IApplySession.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+package org.osgeo.mapguide.test.operations;
+
+import org.osgeo.mapguide.*;
+
+public interface IApplySession
+{
+    void SetSessionId(String sessionId);
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IMapCreator.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IMapCreator.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IMapCreator.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,9 @@
+package org.osgeo.mapguide.test.operations;
+
+import org.osgeo.mapguide.*;
+
+public interface IMapCreator
+{
+    MgMapBase CreateMap(MgResourceIdentifier resId);
+    MgLayerBase CreateLayer(MgResourceIdentifier resId);
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IMapGuideSession.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IMapGuideSession.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IMapGuideSession.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,9 @@
+package org.osgeo.mapguide.test.operations;
+
+import org.osgeo.mapguide.*;
+
+public interface IMapGuideSession
+{
+    String getSessionID();
+    void setSessionID(String value);
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/ISessionCreator.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/ISessionCreator.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/ISessionCreator.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+package org.osgeo.mapguide.test.operations;
+
+import org.osgeo.mapguide.*;
+
+public interface ISessionCreator
+{
+    String CreateSession();
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IWebLayoutCreator.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IWebLayoutCreator.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/operations/IWebLayoutCreator.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+package org.osgeo.mapguide.test.operations;
+
+import org.osgeo.mapguide.*;
+
+public interface IWebLayoutCreator
+{
+    MgWebLayout CreateWebLayout(MgResourceIdentifier resId);
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/BatchPropertyCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/BatchPropertyCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/BatchPropertyCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,42 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class BatchPropertyCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgPropertyCollection coll1 = new MgPropertyCollection();
+        MgDoubleProperty dblProp = new MgDoubleProperty("DoubleProp", 1.1111);
+        coll1.addItem(dblProp);
+
+        MgPropertyCollection coll2 = new MgPropertyCollection();
+        MgInt32Property intProp = new MgInt32Property("IntProp", 1);
+        coll2.addItem(intProp);
+
+        MgPropertyCollection coll3 = new MgPropertyCollection();
+        MgSingleProperty single = new MgSingleProperty("SingleProp", (float)2.2222);
+        coll3.addItem(single);
+
+        MgBatchPropertyCollection coll = new MgBatchPropertyCollection();
+        coll.addItem(coll1);
+        coll.addItem(coll2);
+
+        Assert.AreEqual(2, coll.getCount());
+        Assert.AreEqual(MgPropertyType.Double, coll.getItem(0).getItem(0).getPropertyType());
+        Assert.AreEqual(MgPropertyType.Int32, coll.getItem(1).getItem(0).getPropertyType());
+
+        coll.setItem(1, coll3);
+
+        String str = "";
+        //for (MgPropertyCollection c : coll)
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            MgPropertyCollection c = coll.getItem(i);
+            str = str + ".getItem(" + c.getItem(0).getName() + ")";
+        }
+        Assert.AreEqual(".getItem(DoubleProp).getItem(SingleProp)", str);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderMemoryConstructorTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderMemoryConstructorTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderMemoryConstructorTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,36 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class ByteReaderMemoryConstructorTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        ByteReaderTestData.Init();
+        int nBytes = ByteReaderTestData.nBytes;
+        byte[] testBytes = ByteReaderTestData.testBytes;
+        String outfileName = ByteReaderTestData.outfileName;
+
+        byte[] buf = new byte[nBytes];
+        MgByteReader reader = new MgByteReader(testBytes, nBytes, "png");
+        Assert.AreEqual(nBytes, reader.getLength());
+        reader.read(buf, nBytes);
+        Assert.AreEqual(buf, testBytes);
+        Assert.AreEqual(0, reader.getLength());
+        reader.rewind();
+        Assert.AreEqual(nBytes, reader.getLength());
+
+        reader.toFile(outfileName);
+
+        try {
+            FileInputStream fp = new FileInputStream(outfileName);
+            fp.read(buf, 0, nBytes);
+            Assert.AreEqual(buf, testBytes);
+            fp.close();
+        } catch (Exception ex) {
+            Assert.Fail(CommonUtility.ExceptionToString(ex));
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderStringConstructorTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderStringConstructorTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderStringConstructorTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,21 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class ByteReaderStringConstructorTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        ByteReaderTestData.Init();
+        String testString = ByteReaderTestData.testString;
+        MgByteReader reader = new MgByteReader(testString, "text/html");
+        Assert.AreEqual(testString.length(), reader.getLength());
+        String buf = reader.toString();
+        Assert.AreEqual(testString, buf);
+        Assert.AreEqual(testString.length(), reader.getLength());
+        reader.rewind();
+        Assert.AreEqual(testString.length(), reader.getLength());
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,42 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class ByteReaderTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        ByteReaderTestData.Init();
+        int nBytes = ByteReaderTestData.nBytes;
+        int nBlocks = ByteReaderTestData.nBlocks;
+        byte[] testBytes = ByteReaderTestData.testBytes;
+        String infileName = ByteReaderTestData.infileName;
+        String outfileName = ByteReaderTestData.outfileName;
+        byte[] buf = new byte[nBytes];
+        MgByteReader reader = new MgByteReader(infileName, "png", false);
+        Assert.AreEqual(nBlocks * nBytes, reader.getLength());
+        reader.read(buf, nBytes);
+        Assert.AreEqual(buf, testBytes);
+        Assert.AreEqual((nBlocks - 1) * nBytes, reader.getLength());
+        reader.rewind();
+        Assert.AreEqual(nBlocks * nBytes, reader.getLength());
+        reader.toFile(outfileName);
+        reader.rewind();
+
+        try {
+            byte[] buf2 = new byte[nBytes];
+            FileInputStream fp = new FileInputStream(outfileName);
+            for (int j = 0; j < nBlocks; j++)
+            {
+                fp.read(buf2, 0, nBytes);
+                reader.read(buf, nBytes);
+                Assert.AreEqual(buf, buf2);
+            }
+            fp.close();
+        } catch (Exception ex) {
+            Assert.Fail(CommonUtility.ExceptionToString(ex));
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderTestData.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderTestData.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ByteReaderTestData.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,42 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class ByteReaderTestData
+{
+    public static byte[] testBytes;
+    public static int nBytes = 32768;
+    public static int nBlocks = 256;
+    public static String testString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+    public static String infileName;
+    public static String outfileName;
+    private static boolean bFirstTime = true;
+
+    public static void Init()
+    {
+        if (bFirstTime)
+        {
+            infileName = CommonUtility.GetTempFileName();
+            outfileName = CommonUtility.GetTempFileName();
+            testBytes = new byte[nBytes];
+            for (int i = 0; i < nBytes; i++)
+            {
+                testBytes[i] = (byte)(i % 255);
+            }
+
+            try {
+                FileOutputStream fp = new FileOutputStream(infileName);
+                for (int j = 0; j < nBlocks; j++)
+                {
+                    fp.write(testBytes, 0, nBytes);
+                }
+                fp.close();
+            } catch (Exception ex) {
+                Assert.Fail(CommonUtility.ExceptionToString(ex));
+            }
+
+            bFirstTime = false;
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ClassDefinitionCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ClassDefinitionCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/ClassDefinitionCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,180 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import java.util.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class ClassDefinitionCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgClassDefinition class1 = new MgClassDefinition();
+        class1.setName("class1");
+
+        MgClassDefinition class2 = new MgClassDefinition();
+        class2.setName("class2");
+
+        MgClassDefinition class3 = new MgClassDefinition();
+        class3.setName("class3");
+
+        MgClassDefinitionCollection coll = new MgClassDefinitionCollection();
+        coll.insert(0, class3);
+        coll.insert(0, class2);
+        coll.insert(0, class1);
+
+        Assert.AreEqual(3, coll.getCount());
+        Assert.AreEqual("class2", coll.getItem(1).getName());
+
+        MgClassDefinition tmp = coll.getItem(0);
+        coll.setItem(0, coll.getItem(1));
+        coll.setItem(1, coll.getItem(2));
+        coll.setItem(2, tmp);
+
+        String str = "";
+        for (MgClassDefinition def : coll)
+        {
+            str = str + ".getItem(" + def.getName() + ")";
+        }
+        Assert.AreEqual(".getItem(class2).getItem(class3).getItem(class1)", str);
+
+        MgFeatureSchema parent = new MgFeatureSchema("Parent", "");
+        MgClassDefinitionCollection items = parent.getClasses();
+        MgClassDefinition item1 = new MgClassDefinition(); item1.setName("Item1");
+        MgClassDefinition item2 = new MgClassDefinition(); item2.setName("Item2");
+        MgClassDefinition item3 = new MgClassDefinition(); item3.setName("Item3");
+        MgClassDefinition item4 = new MgClassDefinition(); item4.setName("Item4");
+
+        //Items inserted the MapGuide API way
+        items.addItem(item1);
+        items.addItem(item2);
+        items.addItem(item3);
+
+        Assert.IsTrue(items.getCount() == 3, "Expected 3 items");
+        Assert.IsTrue(!items.isEmpty(), "Expected non-empty item collection");
+
+        int i = 0;
+        //Looping the classic way
+        for (i = 0; i < items.getCount(); i++) {
+            switch (i){
+                case 0:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+        }
+
+        //Looping the iterative way
+        i = 0;
+        for (MgClassDefinition item : items) {
+            String itemName = item.getName();
+            switch (i){
+                case 0:
+                    Assert.IsTrue(itemName.equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(itemName.equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(itemName.equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+            i++;
+        }
+
+        items.clear();
+        Assert.IsTrue(items.getCount() == 0, "Expected empty item collection");
+        Assert.IsTrue(items.isEmpty(), "Expected empty item collection");
+
+        //Now test through java.util.Collection facade
+        Collection<MgClassDefinition> facade = items;
+        //add()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //contains()
+        Assert.IsTrue(facade.contains(item1), "item1 should exist");
+        Assert.IsTrue(facade.contains(item2), "item2 should exist");
+        Assert.IsTrue(facade.contains(item3), "item3 should exist");
+
+        ArrayList<MgClassDefinition> testCollection = new ArrayList<MgClassDefinition>();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+
+        //containsAll()
+        Assert.IsTrue(facade.containsAll(testCollection), "All items in test collection should match");
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item4);
+        Assert.IsTrue(!facade.containsAll(testCollection), "Should not have matched test collection");
+
+        facade.clear();
+        Assert.IsTrue(facade.size() == 0, "Expected 0 items");
+        Assert.IsTrue(facade.isEmpty(), "Expected empty item collection");
+
+        //addAll()
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.addAll(testCollection), "Expected addAll() success");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //remove()
+        Assert.IsTrue(!facade.remove(item4), "Expected remove() of non-existent item to return false");
+        Assert.IsTrue(!facade.remove(new MgFeatureSchema()), "Expected remove() of different type to return false");
+        Assert.IsTrue(facade.remove(item2), "Expected remove() of item2 to return true");
+
+        //removeAll()
+        testCollection.remove(item2);
+        Assert.IsTrue(facade.removeAll(testCollection), "Expected removeAll() to succeed");
+
+        //retainAll()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 schemas");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty schema collection");
+
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.retainAll(testCollection), "Expected retainAll() to succeed");
+        Assert.IsTrue(facade.contains(item1), "Expected item1 to exist");
+        Assert.IsTrue(!facade.contains(item2), "Expected item2 to not exist");
+        Assert.IsTrue(facade.contains(item3), "Expected item3 to exist");
+
+        //toArray()
+        Object[] itemArray = facade.toArray();
+        Assert.IsTrue(itemArray != null, "Expected non-null item array");
+        Assert.IsTrue(itemArray.length == 2, "Expected 2 item array");
+        for (Object o : itemArray) {
+            Assert.IsTrue(o instanceof MgClassDefinition, "Expected item to be MgClassDefinition");
+        }
+        MgClassDefinition[] fitems = facade.toArray(new MgClassDefinition[0]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new MgClassDefinition[1]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new MgClassDefinition[3]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 3, "Expected 3 item array");
+        Assert.IsTrue(fitems[0] != null, "Expected non-null item #1");
+        Assert.IsTrue(fitems[1] != null, "Expected non-null item #2");
+        Assert.IsTrue(fitems[2] == null, "Expected null item #3");
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CoordinateCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CoordinateCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CoordinateCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,37 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class CoordinateCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgGeometryFactory gf = new MgGeometryFactory();
+        MgCoordinate c1 = gf.createCoordinateXY(1.0, 1.0);
+        MgCoordinate c2 = gf.createCoordinateXY(2.0, 2.0);
+        MgCoordinate c3 = gf.createCoordinateXY(3.0, 3.0);
+        MgCoordinate c4 = gf.createCoordinateXY(4.0, 4.0);
+
+        MgCoordinateCollection coll = new MgCoordinateCollection();
+        coll.add(c1);
+        coll.insert(1, c2);
+        coll.add(c3);
+        coll.add(c4);
+
+        Assert.AreEqual(4, coll.getCount());
+        Assert.AreEqual(1.0, coll.getItem(0).getX());
+        coll.setItem(3, coll.getItem(2));
+        Assert.AreEqual(3.0, coll.getItem(3).getX());
+
+        double sum = 0.0;
+        //for (MgCoordinate coord : coll)
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            MgCoordinate coord = coll.getItem(i);
+            sum += coord.getX();
+        }
+        Assert.AreEqual(9.0, sum);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurvePolygonCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurvePolygonCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurvePolygonCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,37 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class CurvePolygonCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        GeomBuild build = new GeomBuild();
+        MgCurvePolygon geom1 = build.createCurvePolygon(2.0, 5);
+        MgCurvePolygon geom2 = build.createCurvePolygon(12.0, 5);
+        MgCurvePolygon geom3 = build.createCurvePolygon(2.0, 5);
+
+        MgCurvePolygonCollection coll = new MgCurvePolygonCollection();
+        coll.add(geom1);
+        coll.add(geom2);
+        coll.add(geom3);
+
+        Assert.AreEqual(3, coll.getCount());
+        Assert.IsTrue(geom1.equals(coll.getItem(0)));
+        Assert.IsTrue(coll.getItem(0).equals(coll.getItem(2)));
+        Assert.IsFalse(coll.getItem(0).equals(coll.getItem(1)));
+        coll.setItem(0, coll.getItem(1));
+        Assert.IsTrue(coll.getItem(0).equals(coll.getItem(1)));
+
+        double width = 0.0;
+        //for (MgCurvePolygon geom : coll)
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            MgCurvePolygon geom = coll.getItem(i);
+            width += geom.envelope().getWidth();
+        }
+        Assert.AreEqual(geom1.envelope().getWidth() * 3.0, width);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurveRingCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurveRingCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurveRingCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,37 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class CurveRingCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        GeomBuild build = new GeomBuild();
+        MgCurveRing geom1 = build.createCurveRing(2.0);
+        MgCurveRing geom2 = build.createCurveRing(12.0);
+        MgCurveRing geom3 = build.createCurveRing(2.0);
+
+        MgCurveRingCollection coll = new MgCurveRingCollection();
+        coll.add(geom1);
+        coll.add(geom2);
+        coll.add(geom3);
+
+        Assert.AreEqual(3, coll.getCount());
+        Assert.IsTrue(geom1.envelope().contains(coll.getItem(0).envelope()));
+        Assert.IsTrue(coll.getItem(0).envelope().contains(coll.getItem(2).envelope()));
+        Assert.IsFalse(coll.getItem(0).envelope().contains(coll.getItem(1).envelope()));
+        coll.setItem(0, coll.getItem(1));
+        Assert.IsTrue(coll.getItem(0).envelope().contains(coll.getItem(1).envelope()));
+
+        double width = 0.0;
+        //for (MgCurveRing geom : coll)
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            MgCurveRing geom = coll.getItem(i);
+            width += geom.envelope().getWidth();
+        }
+        Assert.AreEqual(geom1.envelope().getWidth() * 3.0, width);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurveSegmentCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurveSegmentCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurveSegmentCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,37 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class CurveSegmentCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        GeomBuild build = new GeomBuild();
+        MgArcSegment geom1 = build.createArcSegment(2.0);
+        MgArcSegment geom2 = build.createArcSegment(12.0);
+        MgArcSegment geom3 = build.createArcSegment(2.0);
+
+        MgCurveSegmentCollection coll = new MgCurveSegmentCollection();
+        coll.add(geom1);
+        coll.add(geom2);
+        coll.add(geom3);
+
+        Assert.AreEqual(3, coll.getCount());
+        Assert.IsTrue(geom1.envelope().contains(coll.getItem(0).envelope()));
+        Assert.IsTrue(coll.getItem(0).envelope().contains(coll.getItem(2).envelope()));
+        Assert.IsFalse(coll.getItem(0).envelope().contains(coll.getItem(1).envelope()));
+        coll.setItem(0, coll.getItem(1));
+        Assert.IsTrue(coll.getItem(0).envelope().contains(coll.getItem(1).envelope()));
+
+        double width = 0.0;
+        //for (MgCurveSegment geom : coll)
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            MgCurveSegment geom = coll.getItem(i);
+            width += geom.envelope().getWidth();
+        }
+        Assert.AreEqual(geom1.envelope().getWidth() * 3.0, width);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurveStringCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurveStringCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/CurveStringCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,37 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class CurveStringCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        GeomBuild build = new GeomBuild();
+        MgCurveString geom1 = build.createCurveString(2.0);
+        MgCurveString geom2 = build.createCurveString(12.0);
+        MgCurveString geom3 = build.createCurveString(2.0);
+
+        MgCurveStringCollection coll = new MgCurveStringCollection();
+        coll.add(geom1);
+        coll.add(geom2);
+        coll.add(geom3);
+
+        Assert.AreEqual(3, coll.getCount());
+        Assert.IsTrue(geom1.equals(coll.getItem(0)));
+        Assert.IsTrue(coll.getItem(0).equals(coll.getItem(2)));
+        Assert.IsFalse(coll.getItem(0).equals(coll.getItem(1)));
+        coll.setItem(0, coll.getItem(1));
+        Assert.IsTrue(coll.getItem(0).equals(coll.getItem(1)));
+
+        double width = 0.0;
+        //for (MgCurveString geom : coll)
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            MgCurveString geom = coll.getItem(i);
+            width += geom.envelope().getWidth();
+        }
+        Assert.AreEqual(geom1.envelope().getWidth() * 3.0, width);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/FeatureCommandCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/FeatureCommandCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/FeatureCommandCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,36 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class FeatureCommandCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgPropertyCollection propVals = new MgPropertyCollection();
+        MgInt32Property prop = new MgInt32Property("prop", 1);
+        propVals.addItem(prop);
+        MgUpdateFeatures update = new MgUpdateFeatures("class2", propVals, "where cat < dog");
+        MgInsertFeatures insert = new MgInsertFeatures("class3", propVals);
+        MgDeleteFeatures del = new MgDeleteFeatures("class1", "where cat > dog");
+
+        MgFeatureCommandCollection coll = new MgFeatureCommandCollection();
+        coll.add(update);
+        coll.add(insert);
+        coll.add(del);
+
+        Assert.AreEqual(3, coll.getCount());
+        Assert.AreEqual(MgFeatureCommandType.DeleteFeatures, coll.getItem(2).getCommandType());
+        coll.setItem(0, coll.getItem(1));
+
+        String txt = "";
+        //for (MgFeatureCommand cmd : coll)
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            MgFeatureCommand cmd = coll.getItem(i);
+            txt += "[" + cmd.getCommandType() + "]";
+        }
+        Assert.AreEqual("[0][0][2]", txt);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/FeatureSchemaCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/FeatureSchemaCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/FeatureSchemaCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,170 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import java.util.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class FeatureSchemaCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgFeatureSchema schema1 = new MgFeatureSchema("schema1", "desc1");
+        MgFeatureSchema schema2 = new MgFeatureSchema("schema2", "desc2");
+        MgFeatureSchema schema3 = new MgFeatureSchema("schema3", "desc3");
+
+        MgFeatureSchemaCollection coll = new MgFeatureSchemaCollection();
+        coll.add(schema1);
+        coll.add(schema2);
+        coll.add(schema3);
+
+        Assert.AreEqual(3, coll.getCount());
+        Assert.AreEqual("schema3", coll.getItem(2).getName());
+        coll.setItem(0, coll.getItem(2));
+
+        String txt = "";
+        for (MgFeatureSchema schema : coll)
+        {
+            txt += "[" + schema.getName() + "]";
+        }
+        Assert.AreEqual("[schema3][schema2][schema3]", txt);
+
+        MgFeatureSchemaCollection items = new MgFeatureSchemaCollection();
+        MgFeatureSchema item1 = new MgFeatureSchema("Item1", "");
+        MgFeatureSchema item2 = new MgFeatureSchema("Item2", "");
+        MgFeatureSchema item3 = new MgFeatureSchema("Item3", "");
+        MgFeatureSchema item4 = new MgFeatureSchema("Item4", "");
+
+        //Items inserted the MapGuide API way
+        items.addItem(item1);
+        items.addItem(item2);
+        items.addItem(item3);
+
+        Assert.IsTrue(items.getCount() == 3, "Expected 3 items");
+        Assert.IsTrue(!items.isEmpty(), "Expected non-empty item collection");
+
+        int i = 0;
+        //Looping the classic way
+        for (i = 0; i < items.getCount(); i++) {
+            switch (i){
+                case 0:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+        }
+
+        //Looping the iterative way
+        i = 0;
+        for (MgFeatureSchema item : items) {
+            String itemName = item.getName();
+            switch (i){
+                case 0:
+                    Assert.IsTrue(itemName.equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(itemName.equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(itemName.equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+            i++;
+        }
+
+        items.clear();
+        Assert.IsTrue(items.getCount() == 0, "Expected empty item collection");
+        Assert.IsTrue(items.isEmpty(), "Expected empty item collection");
+
+        //Now test through java.util.Collection facade
+        Collection<MgFeatureSchema> facade = items;
+        //add()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //contains()
+        Assert.IsTrue(facade.contains(item1), "item1 should exist");
+        Assert.IsTrue(facade.contains(item2), "item2 should exist");
+        Assert.IsTrue(facade.contains(item3), "item3 should exist");
+
+        ArrayList<MgFeatureSchema> testCollection = new ArrayList<MgFeatureSchema>();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+
+        //containsAll()
+        Assert.IsTrue(facade.containsAll(testCollection), "All items in test collection should match");
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item4);
+        Assert.IsTrue(!facade.containsAll(testCollection), "Should not have matched test collection");
+
+        facade.clear();
+        Assert.IsTrue(facade.size() == 0, "Expected 0 items");
+        Assert.IsTrue(facade.isEmpty(), "Expected empty item collection");
+
+        //addAll()
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.addAll(testCollection), "Expected addAll() success");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //remove()
+        Assert.IsTrue(!facade.remove(item4), "Expected remove() of non-existent item to return false");
+        Assert.IsTrue(!facade.remove(new MgClassDefinition()), "Expected remove() of different type to return false");
+        Assert.IsTrue(facade.remove(item2), "Expected remove() of item2 to return true");
+
+        //removeAll()
+        testCollection.remove(item2);
+        Assert.IsTrue(facade.removeAll(testCollection), "Expected removeAll() to succeed");
+
+        //retainAll()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 schemas");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty schema collection");
+
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.retainAll(testCollection), "Expected retainAll() to succeed");
+        Assert.IsTrue(facade.contains(item1), "Expected item1 to exist");
+        Assert.IsTrue(!facade.contains(item2), "Expected item2 to not exist");
+        Assert.IsTrue(facade.contains(item3), "Expected item3 to exist");
+
+        //toArray()
+        Object[] itemArray = facade.toArray();
+        Assert.IsTrue(itemArray != null, "Expected non-null item array");
+        Assert.IsTrue(itemArray.length == 2, "Expected 2 item array");
+        for (Object o : itemArray) {
+            Assert.IsTrue(o instanceof MgFeatureSchema, "Expected item to be MgFeatureSchema");
+        }
+        MgFeatureSchema[] fitems = facade.toArray(new MgFeatureSchema[0]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new MgFeatureSchema[1]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new MgFeatureSchema[3]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 3, "Expected 3 item array");
+        Assert.IsTrue(fitems[0] != null, "Expected non-null item #1");
+        Assert.IsTrue(fitems[1] != null, "Expected non-null item #2");
+        Assert.IsTrue(fitems[2] == null, "Expected null item #3");
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/GeomBuild.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/GeomBuild.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/GeomBuild.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,336 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GeomBuild
+{
+    private MgGeometryFactory factory = new MgGeometryFactory();
+
+    public MgPoint createPoint()
+    {
+        MgCoordinate coord = factory.createCoordinateXYZ(5.0, 3.0, 2.0);
+        return factory.createPoint(coord);
+    }
+
+    public MgLineString createLineString(double offset)
+    {
+        MgCoordinateCollection coordCol = new MgCoordinateCollection();
+
+        MgCoordinate coord1 = factory.createCoordinateXY(offset + 0.0, 1.0);
+        MgCoordinate coord2 = factory.createCoordinateXY(offset + 2.0, 3.0);
+        MgCoordinate coord3 = factory.createCoordinateXY(offset + 4.0, 5.0);
+
+        coordCol.add(coord1);
+        coordCol.add(coord2);
+        coordCol.add(coord3);
+
+        return factory.createLineString(coordCol);
+    }
+
+
+
+    public MgLinearRing createLinearRing(double offset)
+    {
+        MgCoordinate oCoord1 = factory.createCoordinateXY(0.0, offset);
+        MgCoordinate oCoord2 = factory.createCoordinateXY(5.0, offset);
+        MgCoordinate oCoord3 = factory.createCoordinateXY(5.0, offset + 5.0);
+        MgCoordinate oCoord4 = factory.createCoordinateXY(0.0, offset + 5.0);
+        MgCoordinate oCoord5 = factory.createCoordinateXY(0.0, offset);
+
+        MgCoordinateCollection outerRingCoord = new MgCoordinateCollection();
+        outerRingCoord.add(oCoord1);
+        outerRingCoord.add(oCoord2);
+        outerRingCoord.add(oCoord3);
+        outerRingCoord.add(oCoord4);
+        outerRingCoord.add(oCoord5);
+
+        return factory.createLinearRing(outerRingCoord);
+    }
+
+
+    public MgPolygon createPolygon(double offset)
+    {
+
+
+        // OuterRing
+        MgCoordinate oCoord1 = factory.createCoordinateXY(offset + 0.0, 0.0);
+        MgCoordinate oCoord2 = factory.createCoordinateXY(offset + 5.0, 0.0);
+        MgCoordinate oCoord3 = factory.createCoordinateXY(offset + 5.0, 5.0);
+        MgCoordinate oCoord4 = factory.createCoordinateXY(offset + 0.0, 5.0);
+        MgCoordinate oCoord5 = factory.createCoordinateXY(offset + 0.0, 0.0);
+
+        MgCoordinateCollection outerRingCoord = new MgCoordinateCollection();
+        outerRingCoord.add(oCoord1);
+        outerRingCoord.add(oCoord2);
+        outerRingCoord.add(oCoord3);
+        outerRingCoord.add(oCoord4);
+        outerRingCoord.add(oCoord5);
+
+        // Inner Ring1
+        MgCoordinate i1Coord1 = factory.createCoordinateXY(offset + 1.0, 1.0);
+        MgCoordinate i1Coord2 = factory.createCoordinateXY(offset + 2.0, 1.0);
+        MgCoordinate i1Coord3 = factory.createCoordinateXY(offset + 2.0, 2.0);
+        MgCoordinate i1Coord4 = factory.createCoordinateXY(offset + 1.0, 1.0);
+
+        MgCoordinateCollection inner1RingCoord = new MgCoordinateCollection();
+        inner1RingCoord.add(i1Coord1);
+        inner1RingCoord.add(i1Coord2);
+        inner1RingCoord.add(i1Coord3);
+        inner1RingCoord.add(i1Coord4);
+
+        // Inner Ring2
+        MgCoordinate i2Coord1 = factory.createCoordinateXY(offset + 3.0, 3.0);
+        MgCoordinate i2Coord2 = factory.createCoordinateXY(offset + 4.0, 3.0);
+        MgCoordinate i2Coord3 = factory.createCoordinateXY(offset + 4.0, 4.0);
+        MgCoordinate i2Coord4 = factory.createCoordinateXY(offset + 3.0, 3.0);
+
+        MgCoordinateCollection inner2RingCoord = new MgCoordinateCollection();
+        inner2RingCoord.add(i2Coord1);
+        inner2RingCoord.add(i2Coord2);
+        inner2RingCoord.add(i2Coord3);
+        inner2RingCoord.add(i2Coord4);
+
+        MgLinearRing extRing = factory.createLinearRing(outerRingCoord);
+        MgLinearRing intRing1 = factory.createLinearRing(inner1RingCoord);
+        MgLinearRing intRing2 = factory.createLinearRing(inner2RingCoord);
+
+        MgLinearRingCollection intRings = new MgLinearRingCollection();
+        intRings.add(intRing1);
+        intRings.add(intRing2);
+
+        return factory.createPolygon(extRing, intRings);
+    }
+
+
+    public MgCurveString createCurveString(double offset)
+    {
+
+
+        // create and return a curvestring consisting of
+        // one circulararc segment and one linearstring segment
+
+        // arcseg  = (0,0), (0,1), (1,2)
+        // lineseg = (1,2), (3,0), (3,2)
+
+        // ArcSegment
+        MgCoordinate startPos = factory.createCoordinateXY(offset + 0.0, offset + 0.0);
+        MgCoordinate midPos = factory.createCoordinateXY(offset + 0.0, offset + 1.0);
+        MgCoordinate endPos = factory.createCoordinateXY(offset + 1.0, offset + 2.0);
+        MgArcSegment arcSeg = factory.createArcSegment(startPos, endPos, midPos);
+
+        // Linear Segment
+        MgCoordinateCollection points = new MgCoordinateCollection();
+        MgCoordinate pt1 = factory.createCoordinateXY(offset + 1.0, offset + 2.0);
+        MgCoordinate pt2 = factory.createCoordinateXY(offset + 3.0, offset + 0.0);
+        MgCoordinate pt3 = factory.createCoordinateXY(offset + 3.0, offset + 2.0);
+        points.add(pt1);
+        points.add(pt2);
+        points.add(pt3);
+        MgLinearSegment lineSeg = factory.createLinearSegment(points);
+
+        // CurveSegment
+        MgCurveSegmentCollection curveSegs = new MgCurveSegmentCollection();
+        curveSegs.add(arcSeg);
+        curveSegs.add(lineSeg);
+
+        return factory.createCurveString(curveSegs);
+    }
+
+
+    public MgCurveRing createCurveRing(double offset)
+    {
+        // Ring is a closed entity.
+        // create and return a ring consisting of
+        // one circulararc segment and one linearstring segment
+
+        // arcseg  = (0,0), (0,1), (1,2)
+        // lineseg = (1,2), (0,0)
+
+
+
+        // ArcSegment
+        MgCoordinate startPos = factory.createCoordinateXY(offset + 0.0, offset + 0.0);
+        MgCoordinate midPos = factory.createCoordinateXY(offset + 0.0, offset + 1.0);
+        MgCoordinate endPos = factory.createCoordinateXY(offset + 1.0, offset + 2.0);
+        MgArcSegment arcSeg = factory.createArcSegment(startPos, endPos, midPos);
+
+        // Linear Segment
+        MgCoordinateCollection points = new MgCoordinateCollection();
+        MgCoordinate fromPt = factory.createCoordinateXY(offset + 1.0, offset + 2.0);
+        MgCoordinate toPt = factory.createCoordinateXY(offset + 0.0, offset + 0.0);
+        points.add(fromPt);
+        points.add(toPt);
+        MgLinearSegment lineSeg = factory.createLinearSegment(points);
+
+        // Curve Segment
+        MgCurveSegmentCollection curveSegs = new MgCurveSegmentCollection();
+        curveSegs.add(arcSeg);
+        curveSegs.add(lineSeg);
+
+        return factory.createCurveRing(curveSegs);
+    }
+
+
+    public MgCurvePolygon createCurvePolygon(double offset, int increment)
+    {
+
+
+        MgCurveRing extRing = createCurveRing(offset + increment);
+
+        MgCurveRingCollection intRings = new MgCurveRingCollection();
+
+        MgCurveRing ring1 = createCurveRing(offset + 2 * increment);
+        MgCurveRing ring2 = createCurveRing(offset + 3 * increment);
+
+        intRings.add(ring1);
+        intRings.add(ring2);
+
+        return factory.createCurvePolygon(extRing, intRings);
+    }
+
+
+    public MgMultiPoint createMultiPoint()
+    {
+
+
+        MgCoordinate coord1 = factory.createCoordinateXYZ(1.0, 2.0, 3.0);
+        MgPoint point1 = factory.createPoint(coord1);
+
+        MgCoordinate coord2 = factory.createCoordinateXYZ(4.0, 5.0, 6.0);
+        MgPoint point2 = factory.createPoint(coord2);
+
+        MgCoordinate coord3 = factory.createCoordinateXYZ(7.0, 8.0, 9.0);
+        MgPoint point3 = factory.createPoint(coord3);
+
+        MgPointCollection pnts = new MgPointCollection();
+        pnts.add(point1);
+        pnts.add(point2);
+        pnts.add(point3);
+
+        return factory.createMultiPoint(pnts);
+    }
+
+
+    public MgMultiLineString createMultiLineString()
+    {
+
+
+        MgCoordinate coord1 = factory.createCoordinateXYZ(0.0, 1.0, 2.0);
+        MgCoordinate coord2 = factory.createCoordinateXYZ(3.0, 4.0, 5.0);
+        MgCoordinate coord3 = factory.createCoordinateXYZ(6.0, 7.0, 8.0);
+
+        MgCoordinateCollection coordColA = new MgCoordinateCollection();
+        coordColA.add(coord1);
+        coordColA.add(coord2);
+        coordColA.add(coord3);
+
+        MgCoordinate coord4 = factory.createCoordinateXYZ(9.0, 10.0, 11.0);
+        MgCoordinate coord5 = factory.createCoordinateXYZ(12.0, 13.0, 14.0);
+        MgCoordinate coord6 = factory.createCoordinateXYZ(15.0, 16.0, 17.0);
+
+        MgCoordinateCollection coordColB = new MgCoordinateCollection();
+        coordColB.add(coord4);
+        coordColB.add(coord5);
+        coordColB.add(coord6);
+
+        MgLineString lineString1 = factory.createLineString(coordColA);
+        MgLineString lineString2 = factory.createLineString(coordColB);
+
+        MgLineStringCollection lineStrings = new MgLineStringCollection();
+        lineStrings.add(lineString1);
+        lineStrings.add(lineString2);
+
+        return factory.createMultiLineString(lineStrings);
+    }
+
+
+    public MgMultiPolygon createMultiPolygon()
+    {
+
+
+        MgPolygon polygon1 = createPolygon(0.0);
+        MgPolygon polygon2 = createPolygon(0.0);
+
+        MgPolygonCollection polygons = new MgPolygonCollection();
+        polygons.add(polygon1);
+        polygons.add(polygon2);
+
+        return factory.createMultiPolygon(polygons);
+    }
+
+
+    MgMultiCurveString createMultiCurveString()
+    {
+
+
+        MgCurveString curveString1 = createCurveString(100);
+        MgCurveString curveString2 = createCurveString(200);
+        MgCurveString curveString3 = createCurveString(300);
+
+        MgCurveStringCollection curveStrings = new MgCurveStringCollection();
+        curveStrings.add(curveString1);
+        curveStrings.add(curveString2);
+        curveStrings.add(curveString3);
+
+        return factory.createMultiCurveString(curveStrings);
+    }
+
+
+    public MgMultiCurvePolygon createMultiCurvePolygon(int numCurvePolys, double offset)
+    {
+
+
+        MgCurvePolygonCollection curvePolys = new MgCurvePolygonCollection();
+        for (int i = 0; i < numCurvePolys; i++)
+        {
+            MgCurvePolygon curvePoly = createCurvePolygon(i + offset, 1);
+            curvePolys.add(curvePoly);
+        }
+
+        return factory.createMultiCurvePolygon(curvePolys);
+    }
+
+
+    public MgMultiGeometry createMultiGeometry()
+    {
+
+
+        MgGeometryCollection geometries = new MgGeometryCollection();
+        MgGeometry geometry = null;
+
+        // CurvePolygon
+        geometry = (MgGeometry)createCurvePolygon(0, 1);
+        geometries.add(geometry);
+
+        // CurveString
+        // Not doing CurveString because of some unfixed defect.
+        // It may be the same one that sometimes affects MultiPolygon.
+        geometry = (MgGeometry)createCurveString(100);
+        geometries.add(geometry);
+
+        // LineString
+        geometry = (MgGeometry)createLineString(1.0);
+        geometries.add(geometry);
+
+        // Point
+        geometry = (MgGeometry)createPoint();
+        geometries.add(geometry);
+
+        // Polygon
+        geometry = createPolygon(0.0);
+        geometries.add(geometry);
+
+        // Make MultiGeometry from the many geometries collected above.
+        return factory.createMultiGeometry(geometries);
+    }
+
+    public MgArcSegment createArcSegment(double offset)
+    {
+        MgCoordinate startPos = factory.createCoordinateXY(offset + 0.0, offset + 0.0);
+        MgCoordinate midPos = factory.createCoordinateXY(offset + 0.0, offset + 1.0);
+        MgCoordinate endPos = factory.createCoordinateXY(offset + 1.0, offset + 2.0);
+        return factory.createArcSegment(startPos, endPos, midPos);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/GeometryCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/GeometryCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/GeometryCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,37 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class GeometryCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgWktReaderWriter wkt = new MgWktReaderWriter();
+        MgPoint geom1 = (MgPoint)wkt.read("POINT XY (1.0 1.0)");
+        MgPoint geom2 = (MgPoint)wkt.read("POINT XY (2.0 2.0)");
+        MgPoint geom3 = (MgPoint)wkt.read("POINT XY (1.0 1.0)");
+
+        MgGeometryCollection coll = new MgGeometryCollection();
+        coll.add(geom1);
+        coll.add(geom2);
+        coll.add(geom3);
+
+        Assert.AreEqual(3, coll.getCount());
+        Assert.IsTrue(geom1.equals(coll.getItem(0)));
+        Assert.IsTrue(coll.getItem(0).equals(coll.getItem(2)));
+        Assert.IsFalse(coll.getItem(0).equals(coll.getItem(1)));
+        coll.setItem(0, coll.getItem(1));
+        Assert.IsTrue(coll.getItem(0).equals(coll.getItem(1)));
+
+        double x = 0.0;
+        //for (MgGeometry geom : coll)
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            MgGeometry geom = coll.getItem(i);
+            x += ((MgPoint)geom).getCoordinate().getX();
+        }
+        Assert.AreEqual(5.0, x);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/IntCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/IntCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/IntCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,28 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class IntCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgIntCollection coll = new MgIntCollection();
+        coll.addItem(10);
+        coll.addItem(20);
+        coll.addItem(30);
+        coll.addItem(40);
+        coll.addItem(50);
+        Assert.AreEqual(20, coll.getItem(1));
+        Assert.AreEqual(30, coll.getItem(2));
+        Assert.AreEqual(5, coll.getCount());
+
+        int j = 0;
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            j += coll.getItem(i);
+        }
+        Assert.AreEqual(150, j);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LayerCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LayerCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LayerCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,162 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import java.util.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class LayerCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgMapBase map = factory.createMap();
+        String coordsys = "GEOGCS[\"LL84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]";
+        MgEnvelope env = new MgEnvelope(10, 10, 20, 20);
+        map.create(coordsys, env, "Test");
+
+        MgResourceService resSvc = (MgResourceService)factory.createService(MgServiceType.ResourceService);
+        MgLayerCollection items = map.getLayers();
+        items.clear();
+        MgResourceIdentifier ldfId = new MgResourceIdentifier("Library://Samples/Sheboygan/Layers/Parcels.LayerDefinition");
+        MgLayerBase item1 = new MgLayer(ldfId, resSvc);
+        item1.setName("Item1");
+        MgLayerBase item2 = new MgLayer(ldfId, resSvc);
+        item2.setName("Item2");
+        MgLayerBase item3 = new MgLayer(ldfId, resSvc);
+        item3.setName("Item3");
+        MgLayerBase item4 = new MgLayer(ldfId, resSvc);
+        item4.setName("Item4");
+
+        //Items inserted the MapGuide API way
+        items.addItem(item1);
+        items.addItem(item2);
+        items.addItem(item3);
+
+        Assert.IsTrue(items.getCount() == 3, "Expected 3 items");
+        Assert.IsTrue(!items.isEmpty(), "Expected non-empty item collection");
+
+        int i = 0;
+        //Looping the classic way
+        for (i = 0; i < items.getCount(); i++) {
+            switch (i){
+                case 0:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+        }
+
+        //Looping the iterative way
+        i = 0;
+        for (MgLayerBase item : items) {
+            String itemName = item.getName();
+            switch (i){
+                case 0:
+                    Assert.IsTrue(itemName.equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(itemName.equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(itemName.equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+            i++;
+        }
+
+        items.clear();
+        Assert.IsTrue(items.getCount() == 0, "Expected empty item collection");
+        Assert.IsTrue(items.isEmpty(), "Expected empty item collection");
+
+        //Now test through java.util.Collection facade
+        Collection<MgLayerBase> facade = items;
+        //add()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //contains()
+        Assert.IsTrue(facade.contains(item1), "item1 should exist");
+        Assert.IsTrue(facade.contains(item2), "item2 should exist");
+        Assert.IsTrue(facade.contains(item3), "item3 should exist");
+
+        ArrayList<MgLayerBase> testCollection = new ArrayList<MgLayerBase>();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+
+        //containsAll()
+        Assert.IsTrue(facade.containsAll(testCollection), "All items in test collection should match");
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item4);
+        Assert.IsTrue(!facade.containsAll(testCollection), "Should not have matched test collection");
+
+        facade.clear();
+        Assert.IsTrue(facade.size() == 0, "Expected 0 items");
+        Assert.IsTrue(facade.isEmpty(), "Expected empty item collection");
+
+        //addAll()
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.addAll(testCollection), "Expected addAll() success");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //remove()
+        Assert.IsTrue(!facade.remove(item4), "Expected remove() of non-existent item to return false");
+        Assert.IsTrue(!facade.remove(new MgFeatureSchema()), "Expected remove() of different type to return false");
+        Assert.IsTrue(facade.remove(item2), "Expected remove() of item2 to return true");
+
+        //removeAll()
+        testCollection.remove(item2);
+        Assert.IsTrue(facade.removeAll(testCollection), "Expected removeAll() to succeed");
+
+        //retainAll()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 schemas");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty schema collection");
+
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.retainAll(testCollection), "Expected retainAll() to succeed");
+        Assert.IsTrue(facade.contains(item1), "Expected item1 to exist");
+        Assert.IsTrue(!facade.contains(item2), "Expected item2 to not exist");
+        Assert.IsTrue(facade.contains(item3), "Expected item3 to exist");
+
+        //toArray()
+        Object[] itemArray = facade.toArray();
+        Assert.IsTrue(itemArray != null, "Expected non-null item array");
+        Assert.IsTrue(itemArray.length == 2, "Expected 2 item array");
+        for (Object o : itemArray) {
+            Assert.IsTrue(o instanceof MgLayerBase, "Expected item to be MgLayerBase");
+        }
+        MgLayerBase[] fitems = facade.toArray(new MgLayerBase[0]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new MgLayerBase[1]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new MgLayerBase[3]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 3, "Expected 3 item array");
+        Assert.IsTrue(fitems[0] != null, "Expected non-null item #1");
+        Assert.IsTrue(fitems[1] != null, "Expected non-null item #2");
+        Assert.IsTrue(fitems[2] == null, "Expected null item #3");
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LayerGroupCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LayerGroupCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LayerGroupCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,157 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import java.util.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class LayerGroupCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgMapBase map = factory.createMap();
+        String coordsys = "GEOGCS[\"LL84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]";
+        MgEnvelope env = new MgEnvelope(10, 10, 20, 20);
+        map.create(coordsys, env, "Test");
+
+        MgLayerGroupCollection items = map.getLayerGroups();
+        items.clear();
+        MgResourceIdentifier ldfId = new MgResourceIdentifier("Library://Samples/Sheboygan/Layers/Parcels.LayerDefinition");
+        MgLayerGroup item1 = new MgLayerGroup("Item1");
+        MgLayerGroup item2 = new MgLayerGroup("Item2");
+        MgLayerGroup item3 = new MgLayerGroup("Item3");
+        MgLayerGroup item4 = new MgLayerGroup("Item4");
+
+        //Items inserted the MapGuide API way
+        items.addItem(item1);
+        items.addItem(item2);
+        items.addItem(item3);
+
+        Assert.IsTrue(items.getCount() == 3, "Expected 3 items");
+        Assert.IsTrue(!items.isEmpty(), "Expected non-empty item collection");
+
+        int i = 0;
+        //Looping the classic way
+        for (i = 0; i < items.getCount(); i++) {
+            switch (i){
+                case 0:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+        }
+
+        //Looping the iterative way
+        i = 0;
+        for (MgLayerGroup item : items) {
+            String itemName = item.getName();
+            switch (i){
+                case 0:
+                    Assert.IsTrue(itemName.equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(itemName.equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(itemName.equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+            i++;
+        }
+
+        items.clear();
+        Assert.IsTrue(items.getCount() == 0, "Expected empty item collection");
+        Assert.IsTrue(items.isEmpty(), "Expected empty item collection");
+
+        //Now test through java.util.Collection facade
+        Collection<MgLayerGroup> facade = items;
+        //add()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //contains()
+        Assert.IsTrue(facade.contains(item1), "item1 should exist");
+        Assert.IsTrue(facade.contains(item2), "item2 should exist");
+        Assert.IsTrue(facade.contains(item3), "item3 should exist");
+
+        ArrayList<MgLayerGroup> testCollection = new ArrayList<MgLayerGroup>();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+
+        //containsAll()
+        Assert.IsTrue(facade.containsAll(testCollection), "All items in test collection should match");
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item4);
+        Assert.IsTrue(!facade.containsAll(testCollection), "Should not have matched test collection");
+
+        facade.clear();
+        Assert.IsTrue(facade.size() == 0, "Expected 0 items");
+        Assert.IsTrue(facade.isEmpty(), "Expected empty item collection");
+
+        //addAll()
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.addAll(testCollection), "Expected addAll() success");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //remove()
+        Assert.IsTrue(!facade.remove(item4), "Expected remove() of non-existent item to return false");
+        Assert.IsTrue(!facade.remove(new MgFeatureSchema()), "Expected remove() of different type to return false");
+        Assert.IsTrue(facade.remove(item2), "Expected remove() of item2 to return true");
+
+        //removeAll()
+        testCollection.remove(item2);
+        Assert.IsTrue(facade.removeAll(testCollection), "Expected removeAll() to succeed");
+
+        //retainAll()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 schemas");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty schema collection");
+
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.retainAll(testCollection), "Expected retainAll() to succeed");
+        Assert.IsTrue(facade.contains(item1), "Expected item1 to exist");
+        Assert.IsTrue(!facade.contains(item2), "Expected item2 to not exist");
+        Assert.IsTrue(facade.contains(item3), "Expected item3 to exist");
+
+        //toArray()
+        Object[] itemArray = facade.toArray();
+        Assert.IsTrue(itemArray != null, "Expected non-null item array");
+        Assert.IsTrue(itemArray.length == 2, "Expected 2 item array");
+        for (Object o : itemArray) {
+            Assert.IsTrue(o instanceof MgLayerGroup, "Expected item to be MgLayerBase");
+        }
+        MgLayerGroup[] fitems = facade.toArray(new MgLayerGroup[0]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new MgLayerGroup[1]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new MgLayerGroup[3]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 3, "Expected 3 item array");
+        Assert.IsTrue(fitems[0] != null, "Expected non-null item #1");
+        Assert.IsTrue(fitems[1] != null, "Expected non-null item #2");
+        Assert.IsTrue(fitems[2] == null, "Expected null item #3");
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LineStringCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LineStringCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LineStringCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,37 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class LineStringCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        GeomBuild build = new GeomBuild();
+        MgLineString geom1 = build.createLineString(5.0);
+        MgLineString geom2 = build.createLineString(11.0);
+        MgLineString geom3 = build.createLineString(5.0);
+
+        MgLineStringCollection coll = new MgLineStringCollection();
+        coll.add(geom1);
+        coll.add(geom2);
+        coll.add(geom3);
+
+        Assert.AreEqual(3, coll.getCount());
+        Assert.IsTrue(geom1.equals(coll.getItem(0)));
+        Assert.IsTrue(coll.getItem(0).equals(coll.getItem(2)));
+        Assert.IsFalse(coll.getItem(0).equals(coll.getItem(1)));
+        coll.setItem(0, coll.getItem(1));
+        Assert.IsTrue(coll.getItem(0).equals(coll.getItem(1)));
+
+        double width = 0.0;
+        //for (MgLineString geom : coll)
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            MgLineString geom = coll.getItem(i);
+            width += geom.envelope().getWidth();
+        }
+        Assert.AreEqual(geom1.envelope().getWidth() * 3.0, width);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LinearRingCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LinearRingCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/LinearRingCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,37 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class LinearRingCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        GeomBuild build = new GeomBuild();
+        MgLinearRing geom1 = build.createLinearRing(1.0);
+        MgLinearRing geom2 = build.createLinearRing(5.0);
+        MgLinearRing geom3 = build.createLinearRing(1.0);
+
+        MgLinearRingCollection coll = new MgLinearRingCollection();
+        coll.add(geom1);
+        coll.add(geom2);
+        coll.add(geom3);
+
+        Assert.AreEqual(3, coll.getCount());
+        Assert.IsTrue(geom1.envelope().contains(coll.getItem(0).envelope()));
+        Assert.IsTrue(coll.getItem(0).envelope().contains(coll.getItem(2).envelope()));
+        Assert.IsFalse(coll.getItem(0).envelope().contains(coll.getItem(1).envelope()));
+        coll.setItem(0, coll.getItem(1));
+        Assert.IsTrue(coll.getItem(0).envelope().contains(coll.getItem(1).envelope()));
+
+        double width = 0.0;
+        //for (MgLinearRing geom : coll)
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            MgLinearRing geom = coll.getItem(i);
+            width += geom.envelope().getWidth();
+        }
+        Assert.AreEqual(geom1.envelope().getWidth() * 3.0, width);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/MapCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/MapCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/MapCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,41 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class MapCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        String coordsys = "GEOGCS[\"LL84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]";
+        MgEnvelope env = new MgEnvelope(10, 10, 20, 20);
+        MgMapBase map1 = factory.createMap();
+        map1.create(coordsys, env, "map1");
+        MgMapBase map2 = factory.createMap();
+        map2.create(coordsys, env, "map2");
+        MgMapBase map3 = factory.createMap();
+        map3.create(coordsys, env, "map3");
+        MgMapBase map4 = factory.createMap();
+        map4.create(coordsys, env, "map4");
+
+        MgMapCollection coll = new MgMapCollection();
+        coll.add(map1);
+        coll.insert(1, map2);
+        coll.add(map3);
+
+        Assert.AreEqual(3, coll.getCount());
+        Assert.AreEqual(coll.getItem(2).getName(), "map3");
+
+        coll.setItem(1, map4);
+
+        String txt = "";
+        //for (MgMapBase map : coll)
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            MgMapBase map = coll.getItem(i);
+            txt += "[" + map.getName() + "]";
+        }
+        Assert.AreEqual("[map1][map4][map3]", txt);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/PropertyCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/PropertyCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/PropertyCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,191 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import java.util.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class PropertyCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgPropertyCollection coll = new MgPropertyCollection();
+        MgDoubleProperty dblProp = new MgDoubleProperty("DoubleProp", 1.1111);
+        MgInt32Property intProp = new MgInt32Property("IntProp", 1);
+        MgDateTime dateTime = new MgDateTime((short)2006, (short)9, (short)21);
+        MgDateTimeProperty dateProp = new MgDateTimeProperty("DateProp", dateTime);
+        MgSingleProperty single = new MgSingleProperty("SingleProp", (float)2.2222);
+        coll.add(dblProp);
+        coll.add(intProp);
+        coll.add(dateProp);
+        coll.setItem(2, single);
+
+        MgProperty prop1 = coll.getItem(0);
+        MgProperty prop2 = coll.getItem(1);
+        MgProperty prop3 = coll.getItem(2);
+        Assert.AreEqual(1.1111, ((MgDoubleProperty)prop1).getValue());
+        Assert.AreEqual(MgPropertyType.Double, prop1.getPropertyType());
+        Assert.AreEqual(MgPropertyType.Int32, prop2.getPropertyType());
+        Assert.AreEqual(MgPropertyType.Single, prop3.getPropertyType());
+        Assert.AreEqual((float)2.2222, ((MgSingleProperty)prop3).getValue());
+        Assert.AreEqual(3, coll.getCount());
+
+        Assert.AreEqual(MgPropertyType.Double, prop1.getPropertyType());
+
+        String str = "";
+        for (MgProperty prop : coll)
+        {
+            str = str + "[" + prop.getName() + "]";
+        }
+        Assert.AreEqual("[DoubleProp][IntProp][SingleProp]", str);
+
+        MgPropertyCollection items = new MgPropertyCollection();
+        MgProperty item1 = new MgStringProperty("Item1", "Hello World");
+        MgProperty item2 = new MgInt32Property("Item2", 42);
+        MgProperty item3 = new MgBooleanProperty("Item3", false);
+        MgProperty item4 = new MgByteProperty("Item4", (byte)4);
+
+        //Items inserted the MapGuide API way
+        items.addItem(item1);
+        items.addItem(item2);
+        items.addItem(item3);
+
+        Assert.IsTrue(items.getCount() == 3, "Expected 3 items");
+        Assert.IsTrue(!items.isEmpty(), "Expected non-empty item collection");
+
+        int i = 0;
+        //Looping the classic way
+        for (i = 0; i < items.getCount(); i++) {
+            switch (i){
+                case 0:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+        }
+
+        //Looping the iterative way
+        i = 0;
+        for (MgProperty item : items) {
+            String itemName = item.getName();
+            switch (i){
+                case 0:
+                    Assert.IsTrue(itemName.equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(itemName.equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(itemName.equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+            i++;
+        }
+
+        items.clear();
+        Assert.IsTrue(items.getCount() == 0, "Expected empty item collection");
+        Assert.IsTrue(items.isEmpty(), "Expected empty item collection");
+
+        //Now test through java.util.Collection facade
+        Collection<MgProperty> facade = items;
+        //add()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //contains()
+        Assert.IsTrue(facade.contains(item1), "item1 should exist");
+        Assert.IsTrue(facade.contains(item2), "item2 should exist");
+        Assert.IsTrue(facade.contains(item3), "item3 should exist");
+
+        ArrayList<MgProperty> testCollection = new ArrayList<MgProperty>();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+
+        //containsAll()
+        Assert.IsTrue(facade.containsAll(testCollection), "All items in test collection should match");
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item4);
+        Assert.IsTrue(!facade.containsAll(testCollection), "Should not have matched test collection");
+
+        facade.clear();
+        Assert.IsTrue(facade.size() == 0, "Expected 0 items");
+        Assert.IsTrue(facade.isEmpty(), "Expected empty item collection");
+
+        //addAll()
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.addAll(testCollection), "Expected addAll() success");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //remove()
+        Assert.IsTrue(!facade.remove(item4), "Expected remove() of non-existent item to return false");
+        Assert.IsTrue(!facade.remove(new MgClassDefinition()), "Expected remove() of different type to return false");
+        Assert.IsTrue(facade.remove(item2), "Expected remove() of item2 to return true");
+
+        //removeAll()
+        testCollection.remove(item2);
+        Assert.IsTrue(facade.removeAll(testCollection), "Expected removeAll() to succeed");
+
+        //retainAll()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 schemas");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty schema collection");
+
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.retainAll(testCollection), "Expected retainAll() to succeed");
+        Assert.IsTrue(facade.contains(item1), "Expected item1 to exist");
+        Assert.IsTrue(!facade.contains(item2), "Expected item2 to not exist");
+        Assert.IsTrue(facade.contains(item3), "Expected item3 to exist");
+
+        //toArray()
+        Object[] itemArray = facade.toArray();
+        Assert.IsTrue(itemArray != null, "Expected non-null item array");
+        Assert.IsTrue(itemArray.length == 2, "Expected 2 item array");
+        i = 0;
+        for (Object o : itemArray) {
+            Assert.IsTrue(o instanceof MgProperty, "Expected item to be MgProperty");
+            switch(i) {
+                case 0:
+                    Assert.IsTrue(o instanceof MgStringProperty, "Expected item to be MgStringProperty");
+                    break;
+                case 1:
+                    Assert.IsTrue(o instanceof MgBooleanProperty, "Expected item to be MgBooleanProperty");
+                    break;
+            }
+            i++;
+        }
+
+        MgProperty[] fitems = facade.toArray(new MgProperty[0]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new MgProperty[1]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new MgProperty[3]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 3, "Expected 3 item array");
+        Assert.IsTrue(fitems[0] != null, "Expected non-null item #1");
+        Assert.IsTrue(fitems[1] != null, "Expected non-null item #2");
+        Assert.IsTrue(fitems[2] == null, "Expected null item #3");
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/PropertyDefinitionCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/PropertyDefinitionCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/PropertyDefinitionCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,178 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import java.util.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class PropertyDefinitionCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgDataPropertyDefinition def1 = new MgDataPropertyDefinition("def1");
+        MgDataPropertyDefinition def2 = new MgDataPropertyDefinition("def2");
+        MgDataPropertyDefinition def3 = new MgDataPropertyDefinition("def3");
+        MgDataPropertyDefinition def4 = new MgDataPropertyDefinition("def4");
+
+        MgPropertyDefinitionCollection coll = new MgPropertyDefinitionCollection();
+        coll.add(def1);
+        coll.add(def2);
+        coll.insert(2, def4);
+        coll.insert(2, def3);
+
+        Assert.AreEqual(4, coll.getCount());
+        Assert.AreEqual("def1", coll.getItem(0).getName());
+
+        MgPropertyDefinition tmp = coll.getItem(0);
+        coll.remove(def1);
+        Assert.AreEqual(3, coll.getCount());
+        coll.insert(0, tmp);
+
+        String txt = "";
+        for (MgPropertyDefinition def : coll)
+        {
+            txt += ".getItem(" + def.getName() + ")";
+        }
+        Assert.AreEqual(".getItem(def1).getItem(def2).getItem(def3).getItem(def4)", txt);
+
+        MgClassDefinition parent = new MgClassDefinition();
+        parent.setName("Parent");
+        MgPropertyDefinitionCollection items = parent.getProperties();
+        MgPropertyDefinition item1 = new MgDataPropertyDefinition("Item1");
+        MgPropertyDefinition item2 = new MgDataPropertyDefinition("Item2");
+        MgPropertyDefinition item3 = new MgDataPropertyDefinition("Item3");
+        MgPropertyDefinition item4 = new MgDataPropertyDefinition("Item4");
+
+        //Items inserted the MapGuide API way
+        items.addItem(item1);
+        items.addItem(item2);
+        items.addItem(item3);
+
+        Assert.IsTrue(items.getCount() == 3, "Expected 3 items");
+        Assert.IsTrue(!items.isEmpty(), "Expected non-empty item collection");
+
+        int i = 0;
+        //Looping the classic way
+        for (i = 0; i < items.getCount(); i++) {
+            switch (i){
+                case 0:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(items.getItem(i).getName().equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+        }
+
+        //Looping the iterative way
+        i = 0;
+        for (MgPropertyDefinition item : items) {
+            String itemName = item.getName();
+            switch (i){
+                case 0:
+                    Assert.IsTrue(itemName.equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(itemName.equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(itemName.equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+            i++;
+        }
+
+        items.clear();
+        Assert.IsTrue(items.getCount() == 0, "Expected empty item collection");
+        Assert.IsTrue(items.isEmpty(), "Expected empty item collection");
+
+        //Now test through java.util.Collection facade
+        Collection<MgPropertyDefinition> facade = items;
+        //add()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //contains()
+        Assert.IsTrue(facade.contains(item1), "item1 should exist");
+        Assert.IsTrue(facade.contains(item2), "item2 should exist");
+        Assert.IsTrue(facade.contains(item3), "item3 should exist");
+
+        ArrayList<MgPropertyDefinition> testCollection = new ArrayList<MgPropertyDefinition>();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+
+        //containsAll()
+        Assert.IsTrue(facade.containsAll(testCollection), "All items in test collection should match");
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item4);
+        Assert.IsTrue(!facade.containsAll(testCollection), "Should not have matched test collection");
+
+        facade.clear();
+        Assert.IsTrue(facade.size() == 0, "Expected 0 items");
+        Assert.IsTrue(facade.isEmpty(), "Expected empty item collection");
+
+        //addAll()
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.addAll(testCollection), "Expected addAll() success");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //remove()
+        Assert.IsTrue(!facade.remove(item4), "Expected remove() of non-existent item to return false");
+        Assert.IsTrue(!facade.remove(new MgFeatureSchema()), "Expected remove() of different type to return false");
+        Assert.IsTrue(facade.remove(item2), "Expected remove() of item2 to return true");
+
+        //removeAll()
+        testCollection.remove(item2);
+        Assert.IsTrue(facade.removeAll(testCollection), "Expected removeAll() to succeed");
+
+        //retainAll()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 schemas");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty schema collection");
+
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.retainAll(testCollection), "Expected retainAll() to succeed");
+        Assert.IsTrue(facade.contains(item1), "Expected item1 to exist");
+        Assert.IsTrue(!facade.contains(item2), "Expected item2 to not exist");
+        Assert.IsTrue(facade.contains(item3), "Expected item3 to exist");
+
+        //toArray()
+        Object[] itemArray = facade.toArray();
+        Assert.IsTrue(itemArray != null, "Expected non-null item array");
+        Assert.IsTrue(itemArray.length == 2, "Expected 2 item array");
+        for (Object o : itemArray) {
+            Assert.IsTrue(o instanceof MgPropertyDefinition, "Expected item to be MgPropertyDefinition");
+        }
+        MgPropertyDefinition[] fitems = facade.toArray(new MgPropertyDefinition[0]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new MgPropertyDefinition[1]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new MgPropertyDefinition[3]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 3, "Expected 3 item array");
+        Assert.IsTrue(fitems[0] != null, "Expected non-null item #1");
+        Assert.IsTrue(fitems[1] != null, "Expected non-null item #2");
+        Assert.IsTrue(fitems[2] == null, "Expected null item #3");
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/StringCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/StringCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/StringCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,172 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import java.util.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class StringCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        Vector<String> strColl = new Vector<String>();
+        strColl.add("string1");
+        strColl.add("string2");
+        strColl.add("string3");
+        strColl.add("string3");
+
+        MgStringCollection coll1 = new MgStringCollection();
+        coll1.add("Hello");
+
+        MgStringCollection coll2 = new MgStringCollection();
+        for (String str : strColl) {
+            coll2.add(str);
+        }
+        Assert.AreEqual(4, coll2.getCount());
+        MgStringCollection coll3 = coll2;
+        Assert.AreEqual(4, coll3.getCount());
+        for (int i = 0; i < 4; i++)
+        {
+            Assert.AreEqual(strColl.get(i), coll2.getItem(i));
+            Assert.AreEqual(strColl.get(i), coll3.getItem(i));
+        }
+
+        MgStringCollection items = new MgStringCollection();
+        String item1 = "Item1";
+        String item2 = "Item2";
+        String item3 = "Item3";
+        String item4 = "Item4";
+
+        //Items inserted the MapGuide API way
+        items.addItem(item1);
+        items.addItem(item2);
+        items.addItem(item3);
+
+        Assert.IsTrue(items.getCount() == 3, "Expected 3 items");
+        Assert.IsTrue(!items.isEmpty(), "Expected non-empty item collection");
+
+        int i = 0;
+        //Looping the classic way
+        for (i = 0; i < items.getCount(); i++) {
+            switch (i){
+                case 0:
+                    Assert.IsTrue(items.getItem(i).equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(items.getItem(i).equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(items.getItem(i).equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+        }
+
+        //Looping the iterative way
+        i = 0;
+        for (String item : items) {
+            String itemName = item;
+            switch (i){
+                case 0:
+                    Assert.IsTrue(itemName.equals("Item1"), "Expected item #" + (i+1) + " to be Item1");
+                    break;
+                case 1:
+                    Assert.IsTrue(itemName.equals("Item2"), "Expected item #" + (i+1) + " to be Item2");
+                    break;
+                case 2:
+                    Assert.IsTrue(itemName.equals("Item3"), "Expected item #" + (i+1) + " to be Item3");
+                    break;
+            }
+            i++;
+        }
+
+        items.clear();
+        Assert.IsTrue(items.getCount() == 0, "Expected empty item collection");
+        Assert.IsTrue(items.isEmpty(), "Expected empty item collection");
+
+        //Now test through java.util.Collection facade
+        Collection<String> facade = items;
+        //add()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //contains()
+        Assert.IsTrue(facade.contains(item1), "item1 should exist");
+        Assert.IsTrue(facade.contains(item2), "item2 should exist");
+        Assert.IsTrue(facade.contains(item3), "item3 should exist");
+
+        ArrayList<String> testCollection = new ArrayList<String>();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+
+        //containsAll()
+        Assert.IsTrue(facade.containsAll(testCollection), "All items in test collection should match");
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item4);
+        Assert.IsTrue(!facade.containsAll(testCollection), "Should not have matched test collection");
+
+        facade.clear();
+        Assert.IsTrue(facade.size() == 0, "Expected 0 items");
+        Assert.IsTrue(facade.isEmpty(), "Expected empty item collection");
+
+        //addAll()
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item2);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.addAll(testCollection), "Expected addAll() success");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 items");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty item collection");
+
+        //remove()
+        Assert.IsTrue(!facade.remove(item4), "Expected remove() of non-existent item to return false");
+        Assert.IsTrue(!facade.remove(new MgClassDefinition()), "Expected remove() of different type to return false");
+        Assert.IsTrue(facade.remove(item2), "Expected remove() of item2 to return true");
+
+        //removeAll()
+        testCollection.remove(item2);
+        Assert.IsTrue(facade.removeAll(testCollection), "Expected removeAll() to succeed");
+
+        //retainAll()
+        Assert.IsTrue(facade.add(item1), "Could not add item1 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item2), "Could not add item2 the java.util.Collection way");
+        Assert.IsTrue(facade.add(item3), "Could not add item3 the java.util.Collection way");
+        Assert.IsTrue(facade.size() == 3, "Expected 3 schemas");
+        Assert.IsTrue(!facade.isEmpty(), "Expected non-empty schema collection");
+
+        testCollection.clear();
+        testCollection.add(item1);
+        testCollection.add(item3);
+        Assert.IsTrue(facade.retainAll(testCollection), "Expected retainAll() to succeed");
+        Assert.IsTrue(facade.contains(item1), "Expected item1 to exist");
+        Assert.IsTrue(!facade.contains(item2), "Expected item2 to not exist");
+        Assert.IsTrue(facade.contains(item3), "Expected item3 to exist");
+
+        //toArray()
+        Object[] itemArray = facade.toArray();
+        Assert.IsTrue(itemArray != null, "Expected non-null item array");
+        Assert.IsTrue(itemArray.length == 2, "Expected 2 item array");
+        for (Object o : itemArray) {
+            Assert.IsTrue(o instanceof String, "Expected item to be String");
+        }
+        String[] fitems = facade.toArray(new String[0]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new String[1]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 2, "Expected 2 item array");
+
+        fitems = facade.toArray(new String[3]);
+        Assert.IsTrue(fitems != null, "Expected non-null item array");
+        Assert.IsTrue(fitems.length == 3, "Expected 3 item array");
+        Assert.IsTrue(fitems[0] != null, "Expected non-null item #1");
+        Assert.IsTrue(fitems[1] != null, "Expected non-null item #2");
+        Assert.IsTrue(fitems[2] == null, "Expected null item #3");
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/StringPropertyCollectionTest.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/StringPropertyCollectionTest.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/StringPropertyCollectionTest.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,37 @@
+package org.osgeo.mapguide.test.platform;
+
+import java.io.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.common.*;
+
+public class StringPropertyCollectionTest implements IExternalTest
+{
+    public void Execute(IPlatformFactory factory, ITestLogger logger)
+    {
+        MgStringProperty prop1 = new MgStringProperty("prop1", "val1");
+        MgStringProperty prop2 = new MgStringProperty("prop2", "val2");
+        MgStringProperty prop3 = new MgStringProperty("prop3", "val3");
+        MgStringProperty prop4 = new MgStringProperty("prop4", "val4");
+
+        MgStringPropertyCollection coll = new MgStringPropertyCollection();
+        coll.add(prop1);
+        coll.add(prop2);
+        coll.remove(prop3);
+        coll.remove(coll.getItem(1));
+        Assert.AreEqual(1, coll.getCount());
+        coll.add(prop3);
+        coll.setItem(1, prop2);
+        coll.insert(2, prop3);
+        Assert.AreEqual(2, coll.indexOf(prop3));
+        coll.add(prop4);
+
+        String txt = "";
+        //for (MgStringProperty prop : coll)
+        for (int i = 0; i < coll.getCount(); i++)
+        {
+            MgStringProperty prop = coll.getItem(i);
+            txt += ".getItem(" + prop.getName() + ")";
+        }
+        Assert.AreEqual(".getItem(prop1).getItem(prop2).getItem(prop3).getItem(prop4)", txt);
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/DescribeFeatureSchema.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/DescribeFeatureSchema.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/DescribeFeatureSchema.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,44 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class DescribeFeatureSchema extends FeatureServiceOperationExecutor
+{
+    public DescribeFeatureSchema(MgFeatureService featSvc, String unitTestVm)
+    {
+        super("DescribeFeatureSchema", featSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "SCHEMA", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            String result = _featureService.describeSchemaAsXml(resId, param.get("SCHEMA"));
+
+            return TestResult.XmlString(result);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/ExecuteSqlQuery.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/ExecuteSqlQuery.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/ExecuteSqlQuery.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,45 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class ExecuteSqlQuery extends FeatureServiceOperationExecutor
+{
+    public ExecuteSqlQuery(MgFeatureService featSvc, String unitTestVm)
+    {
+        super("ExecuteSqlQuery", featSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "SQL", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgSqlDataReader reader = _featureService.executeSqlQuery(resId, param.get("SQL"));
+            MgByteReader byteReader = reader.toXml();
+            reader.close();
+            return TestResult.FromByteReader(byteReader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/FeatureServiceOperationExecutor.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/FeatureServiceOperationExecutor.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/FeatureServiceOperationExecutor.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,18 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+import com.almworks.sqlite4java.*;
+
+public abstract class FeatureServiceOperationExecutor extends PlatformApiTestExecutor
+{
+    protected MgFeatureService _featureService;
+
+    protected FeatureServiceOperationExecutor(String opName, MgFeatureService featSvc, String dbPath)
+    {
+        super(opName, ApiTypes.Platform, dbPath);
+        _featureService = featSvc;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetClasses.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetClasses.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetClasses.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,43 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetClasses extends FeatureServiceOperationExecutor
+{
+    public GetClasses(MgFeatureService featSvc, String unitTestVm)
+    {
+        super("GetClasses", featSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "SCHEMANAME", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgStringCollection result = _featureService.getClasses(resId, param.get("SCHEMANAME"));
+            return TestResult.StringCollection(result);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetConnectionPropertyValues.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetConnectionPropertyValues.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetConnectionPropertyValues.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,38 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetConnectionPropertyValues extends FeatureServiceOperationExecutor
+{
+    public GetConnectionPropertyValues(MgFeatureService featSvc, String unitTestVm)
+    {
+        super("GetConnectionPropertyValues", featSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "PROVIDER", param);
+            ReadParameterValue(paramSetId, "PROPERTY", param);
+            ReadParameterValue(paramSetId, "CONNECTIONSTRING", param);
+
+            MgStringCollection result = _featureService.getConnectionPropertyValues(param.get("PROVIDER"), param.get("PROPERTY"), param.get("CONNECTIONSTRING"));
+            return TestResult.StringCollection(result);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetFeatureProviders.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetFeatureProviders.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetFeatureProviders.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,33 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetFeatureProviders extends FeatureServiceOperationExecutor
+{
+    public GetFeatureProviders(MgFeatureService featSvc, String dbPath)
+    {
+        super("GetFeatureProviders", featSvc, dbPath);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            MgByteReader reader = _featureService.getFeatureProviders();
+            return TestResult.FromByteReader(null);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetLongTransactions.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetLongTransactions.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetLongTransactions.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,45 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetLongTransactions extends FeatureServiceOperationExecutor
+{
+    public GetLongTransactions(MgFeatureService featSvc, String unitTestVm)
+    {
+        super("GetLongTransactions", featSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "ACTIVEONLY", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgLongTransactionReader reader = _featureService.getLongTransactions(resId, (param.get("ACTIVEONLY").equals("1")));
+            MgByteReader byteReader = reader.toXml();
+            reader.close();
+            return TestResult.FromByteReader(byteReader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetProviderCapabilities.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetProviderCapabilities.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetProviderCapabilities.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,39 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetProviderCapabilities extends FeatureServiceOperationExecutor
+{
+    public GetProviderCapabilities(MgFeatureService featSvc, String dbPath)
+    {
+        super("GetProviderCapabilities", featSvc, dbPath);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "PROVIDER", param);
+
+            String p1 = "";
+            if (param.containsKey("PROVIDER"))
+                p1 = param.get("PROVIDER");
+            MgByteReader reader = _featureService.getCapabilities(p1);
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetSchemas.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetSchemas.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetSchemas.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,42 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetSchemas extends FeatureServiceOperationExecutor
+{
+    public GetSchemas(MgFeatureService featSvc, String unitTestVm)
+    {
+        super("GetSchemas", featSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgStringCollection result = _featureService.getSchemas(resId);
+            return TestResult.StringCollection(result);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetSpatialContexts.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetSpatialContexts.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/GetSpatialContexts.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,45 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetSpatialContexts extends FeatureServiceOperationExecutor
+{
+    public GetSpatialContexts(MgFeatureService featSvc, String unitTestVm)
+    {
+        super("GetSpatialContexts", featSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "ACTIVEONLY", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgSpatialContextReader reader = _featureService.getSpatialContexts(resId, (param.get("ACTIVEONLY").equals("1")));
+            MgByteReader byteReader = reader.toXml();
+            reader.close();
+            return TestResult.FromByteReader(byteReader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/SelectAggregates.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/SelectAggregates.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/SelectAggregates.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,66 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class SelectAggregates extends FeatureServiceOperationExecutor
+{
+    public SelectAggregates(MgFeatureService featSvc, String unitTestVm)
+    {
+        super("SelectAggregates", featSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "CLASSNAME", param);
+            //ReadParameterValue(paramSetId, "PROPERTIES", param);
+            //ReadParameterValue(paramSetId, "FILTER", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgFeatureAggregateOptions query = new MgFeatureAggregateOptions();
+            //TODO: Set query options
+            /*
+            if (param["FILTER"] != null)
+            {
+                query.SetFilter(param["FILTER"]);
+            }
+            */
+            //PROPERTIES should be stored in the database as comma separated string without spaces
+            /*
+            if (param["PROPERTIES"] != null)
+            {
+                string[] props = param["PROPERTIES"].Split(',');
+                foreach (var p in props)
+                {
+                    query.AddFeatureProperty(p);
+                }
+            }*/
+
+            MgDataReader reader = _featureService.selectAggregate(resId, param.get("CLASSNAME"), query);
+            MgByteReader byteReader = reader.toXml();
+            reader.close();
+            return TestResult.FromByteReader(byteReader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/SelectFeatures.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/SelectFeatures.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/SelectFeatures.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,64 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class SelectFeatures extends FeatureServiceOperationExecutor
+{
+    public SelectFeatures(MgFeatureService featSvc, String unitTestVm)
+    {
+        super("SelectFeatures", featSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "CLASSNAME", param);
+            ReadParameterValue(paramSetId, "PROPERTIES", param);
+            ReadParameterValue(paramSetId, "FILTER", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgFeatureQueryOptions query = new MgFeatureQueryOptions();
+            //TODO: Set query options
+            if (param.containsKey("FILTER"))
+            {
+                query.setFilter(param.get("FILTER"));
+            }
+
+            //PROPERTIES should be stored in the database as comma separated string without spaces
+            if (param.containsKey("PROPERTIES"))
+            {
+                String [] props = param.get("PROPERTIES").split(",");
+                for (String p : props)
+                {
+                    query.addFeatureProperty(p);
+                }
+            }
+
+            MgFeatureReader reader = _featureService.selectFeatures(resId, param.get("CLASSNAME"), query);
+            MgByteReader byteReader = reader.toXml();
+            reader.close();
+            return TestResult.FromByteReader(byteReader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/SetLongTransaction.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/SetLongTransaction.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/SetLongTransaction.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,57 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class SetLongTransaction extends FeatureServiceOperationExecutor
+{
+    private ISessionCreator _creator;
+    private IApplySession _apply;
+
+    public SetLongTransaction(MgFeatureService featSvc, String unitTestVm, ISessionCreator creator, IApplySession apply)
+    {
+        super("SetLongTransaction", featSvc, unitTestVm);
+        _creator = creator;
+        _apply = apply;
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "LONGTRANSACTIONNAME", param);
+            ReadParameterValue(paramSetId, "CREATESESSION", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            if (param.get("CREATESESSION").equals("TRUE"))
+            {
+                String sessionId = _creator.CreateSession();
+                _apply.SetSessionId(sessionId);
+            }
+
+            boolean bRes = _featureService.setLongTransaction(resId, param.get("LONGTRANSACTIONNAME"));
+
+            return TestResult.Boolean(bRes);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/TestConnection.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/TestConnection.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/feature/TestConnection.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,56 @@
+package org.osgeo.mapguide.test.platform.operations.feature;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class TestConnection extends FeatureServiceOperationExecutor
+{
+    public TestConnection(MgFeatureService featSvc, String unitTestVm)
+    {
+        super("TestConnection", featSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+
+            boolean bRes = false;
+            MgResourceIdentifier resId = null;
+            if (!CommonUtility.IsNullOrEmpty(param.get("RESOURCEID")))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+                bRes = _featureService.testConnection(resId);
+            }
+            else
+            {
+                ReadParameterValue(paramSetId, "PROVIDER", param);
+                ReadParameterValue(paramSetId, "CONNECTIONSTRING", param);
+                String providerName = "";
+                if (param.containsKey("PROVIDER"))
+                    providerName = param.get("PROVIDER");
+                String connString = "";
+                if (param.containsKey("CONNECTIONSTRING"))
+                    connString = param.get("CONNECTIONSTRING");
+                bRes = _featureService.testConnection(providerName, connString);
+            }
+
+            return TestResult.Boolean(bRes);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/AddLayer.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/AddLayer.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/AddLayer.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,61 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class AddLayer extends MapLayerOperationExecutor
+{
+    public AddLayer(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("AddLayer", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement layerStmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"LAYERDEFINITION\"", paramSetId));
+            String layerDefinition = "";
+            if (layerStmt.step()) {
+                layerDefinition = CommonUtility.GetStringFromStatement(layerStmt, "ParamValue");   
+            }
+
+            SQLiteStatement groupStmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"GROUPNAME\"", paramSetId));
+            String groupName = "";
+            if (groupStmt.step()) {
+                groupName = CommonUtility.GetStringFromStatement(groupStmt, "ParamValue");
+            }
+
+            MgResourceIdentifier ldfId = new MgResourceIdentifier(layerDefinition);
+            MgLayerBase layer = _creator.CreateLayer(ldfId);
+
+            if (!CommonUtility.IsNullOrEmpty(groupName))
+            {
+                MgLayerGroupCollection groups = _map.getLayerGroups();
+                int gidx = groups.indexOf(groupName);
+                if (gidx >= 0)
+                    layer.setGroup(groups.getItem(gidx));
+            }
+
+            MgLayerCollection layers = _map.getLayers();
+            layers.add(layer);
+
+            return TestResult.Integer(layers.getCount());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/AddLayerGroup.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/AddLayerGroup.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/AddLayerGroup.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,56 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class AddLayerGroup extends MapLayerOperationExecutor
+{
+    public AddLayerGroup(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("AddLayerGroup", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement grpStmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"GROUPNAME\"", paramSetId));
+            String groupName = "";
+            if (grpStmt.step()) {
+                groupName = CommonUtility.GetStringFromStatement(grpStmt, "ParamValue");
+            }
+
+            SQLiteStatement parentStmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"PARENTGROUPNAME\"", paramSetId));
+            String parentGroupName = "";
+            if (parentStmt.step()) {
+                parentGroupName = CommonUtility.GetStringFromStatement(parentStmt, "ParamValue");
+            }
+
+            MgLayerGroupCollection groups = _map.getLayerGroups();
+            MgLayerGroup group = new MgLayerGroup(groupName);
+            if (!CommonUtility.IsNullOrEmpty(parentGroupName))
+            {
+                MgLayerGroup parentGroup = groups.getItem(parentGroupName);
+                group.setGroup(parentGroup);
+            }
+            groups.add(group);
+
+            return TestResult.Integer(groups.getCount());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetCoordinateSystem.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetCoordinateSystem.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetCoordinateSystem.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetCoordinateSystem extends MapLayerOperationExecutor
+{
+    public GetCoordinateSystem(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetCoordinateSystem", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+            return TestResult.String(_map.getMapSRS());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetDataExtent.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetDataExtent.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetDataExtent.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,31 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetDataExtent extends MapLayerOperationExecutor
+{
+    public GetDataExtent(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetDataExtent", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+            MgEnvelope extent = _map.getDataExtent();
+            return TestResult.Envelope(extent);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetDisplayInLegend.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetDisplayInLegend.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetDisplayInLegend.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,44 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetDisplayInLegend extends MapLayerOperationExecutor
+{
+    public GetDisplayInLegend(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetDisplayInLegend", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"LAYERNAME\"", paramSetId));
+            if (stmt.step()) {
+                String layerName = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+
+                MgLayerBase layer = this.FindLayer(layerName);
+                if (layer == null)
+                    return TestResult.String("***Unknown Layer***");
+                return TestResult.Boolean(layer.getDisplayInLegend());
+            }
+            throw new RuntimeException("No layer param found for param set: " + paramSetId);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetGroups.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetGroups.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetGroups.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,38 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetGroups extends MapLayerOperationExecutor
+{
+    public GetGroups(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetGroups", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+            MgLayerGroupCollection groups = _map.getLayerGroups();
+            String[] names = new String[groups.getCount()];
+            for (int i = 0; i < groups.getCount(); i++)
+            {
+                MgLayerGroup group = groups.getItem(i);
+                names[i] = group.getName();
+            }
+
+            return TestResult.String(CommonUtility.JoinStringArray("/", names));
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerDefinition.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerDefinition.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerDefinition.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,45 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetLayerDefinition extends MapLayerOperationExecutor
+{
+    public GetLayerDefinition(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetLayerDefinition", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"LAYERNAME\"", paramSetId));
+            if (stmt.step()) {
+                String layerName = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+
+                MgLayerBase layer = this.FindLayer(layerName);
+                if (layer == null)
+                    return TestResult.String("***Unknown Layer***");
+                MgResourceIdentifier ldfId = layer.getLayerDefinition();
+                return TestResult.String(ldfId.toString());
+            }
+            throw new RuntimeException("No layer param found for param set: " + paramSetId);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerFeatureClass.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerFeatureClass.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerFeatureClass.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,42 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetLayerFeatureClass extends MapLayerOperationExecutor
+{
+    public GetLayerFeatureClass(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetLayerFeatureClass", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"LAYERNAME\"", paramSetId));
+            if (stmt.step()) {
+                String layerName = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+
+                MgLayerBase layer = this.FindLayer(layerName);
+                return TestResult.String(layer.getFeatureClassName());
+            }
+            throw new RuntimeException("No layer param found for param set: " + paramSetId);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerFeatureSource.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerFeatureSource.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerFeatureSource.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,44 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetLayerFeatureSource extends MapLayerOperationExecutor
+{
+    public GetLayerFeatureSource(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetLayerFeatureSource", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"LAYERNAME\"", paramSetId));
+            if (stmt.step()) {
+                String layerName = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+
+                MgLayerBase layer = this.FindLayer(layerName);
+                if (layer == null)
+                    return TestResult.String("***Unknown Layer***");
+                return TestResult.String(layer.getFeatureSourceId());
+            }
+            throw new RuntimeException("No layer param found for param set: " + paramSetId);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerVisibility.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerVisibility.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayerVisibility.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,44 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetLayerVisibility extends MapLayerOperationExecutor
+{
+    public GetLayerVisibility(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetLayerVisibility", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"LAYERNAME\"", paramSetId));
+            if (stmt.step()) {
+                String layerName = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+
+                MgLayerBase layer = this.FindLayer(layerName);
+                if (layer == null)
+                    return TestResult.String("***Unknown Layer***");
+                return TestResult.Boolean(layer.getVisible());
+            }
+            throw new RuntimeException("No layer param found for param set: " + paramSetId);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayers.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayers.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLayers.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,38 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetLayers extends MapLayerOperationExecutor
+{
+    public GetLayers(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetLayers", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+            MgLayerCollection layers = _map.getLayers();
+            String[] names = new String[layers.getCount()];
+            for (int i = 0; i < layers.getCount(); i++)
+            {
+                MgLayerBase layer = layers.getItem(i);
+                names[i] = layer.getName();
+            }
+
+            return TestResult.String(CommonUtility.JoinStringArray("/", names));
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLegendLabel.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLegendLabel.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetLegendLabel.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,44 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetLegendLabel extends MapLayerOperationExecutor
+{
+    public GetLegendLabel(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetLegendLabel", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"LAYERNAME\"", paramSetId));
+            if (stmt.step()) {
+                String layerName = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+
+                MgLayerBase layer = this.FindLayer(layerName);
+                if (layer == null)
+                    return TestResult.String("***Unknown Layer***");
+                return TestResult.String(layer.getLegendLabel());
+            }
+            throw new RuntimeException("No layer param found for param set: " + paramSetId);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetMapExtent.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetMapExtent.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetMapExtent.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,31 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetMapExtent extends MapLayerOperationExecutor
+{
+    public GetMapExtent(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetMapExtent", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+            MgEnvelope extent = _map.getMapExtent();
+            return TestResult.Envelope(extent);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetMapName.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetMapName.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetMapName.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetMapName extends MapLayerOperationExecutor
+{
+    public GetMapName(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetMapName", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+            return TestResult.String(_map.getName());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetViewCenter.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetViewCenter.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetViewCenter.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetViewCenter extends MapLayerOperationExecutor
+{
+    public GetViewCenter(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetViewCenter", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+            return TestResult.Point(_map.getViewCenter());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetViewScale.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetViewScale.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/GetViewScale.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,30 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetViewScale extends MapLayerOperationExecutor
+{
+    public GetViewScale(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("GetViewScale", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+            return TestResult.Double(_map.getViewScale());
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/HideGroup.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/HideGroup.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/HideGroup.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,45 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class HideGroup extends MapLayerOperationExecutor
+{
+    public HideGroup(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("HideGroup", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"GROUPNAME\"", paramSetId));
+            if (stmt.step()) {
+                String groupName = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+
+                MgLayerGroupCollection groups = _map.getLayerGroups();
+                MgLayerGroup group = groups.getItem(groupName);
+
+                group.setVisible(false);
+                return TestResult.Boolean(group.getVisible());
+            }
+            throw new RuntimeException("No group param found for param set: " + paramSetId);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/IsLayerVisible.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/IsLayerVisible.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/IsLayerVisible.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,44 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class IsLayerVisible extends MapLayerOperationExecutor
+{
+    public IsLayerVisible(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("IsLayerVisible", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"LAYERNAME\"", paramSetId));
+            if (stmt.step()) {
+                String layerName = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+
+                MgLayerBase layer = this.FindLayer(layerName);
+                if (layer == null)
+                    return TestResult.String("***Unknown Layer***");
+                return TestResult.Boolean(layer.isVisible());
+            }
+            throw new RuntimeException("No layer param found for param set: " + paramSetId);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/LayerExists.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/LayerExists.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/LayerExists.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,43 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class LayerExists extends MapLayerOperationExecutor
+{
+    public LayerExists(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("LayerExists", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"LAYERNAME\"", paramSetId));
+            if (stmt.step()) {
+                String layerName = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+
+                MgLayerBase layer = this.FindLayer(layerName);
+
+                return TestResult.Boolean((layer != null));
+            }
+            throw new RuntimeException("No layer param found for param set: " + paramSetId);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/MapLayerOperationExecutor.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/MapLayerOperationExecutor.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/MapLayerOperationExecutor.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,59 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public abstract class MapLayerOperationExecutor extends PlatformApiTestExecutor
+{
+    protected MgMapBase _map;
+    protected MgResourceService _resourceService;
+    protected IMapCreator _creator;
+
+    protected MapLayerOperationExecutor(String opName, MgResourceService resSvc, String unitTestVm, IMapCreator creator)
+    {
+        super(opName, ApiTypes.Platform, unitTestVm);
+        _resourceService = resSvc;
+        _creator = creator;
+    }
+
+    protected void CreateMapFromResource(int paramSetId)
+    {
+        if (_map != null)
+            return;
+
+        try {
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"MAPDEFINITION\"", paramSetId));
+            String mapName = null;
+            if (stmt.step()) {
+                mapName = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+                if (CommonUtility.IsNullOrEmpty(mapName))
+                {
+                    mapName = "Library://maplayertest/World.MapDefinition";
+                }
+            } else {
+                mapName = "Library://maplayertest/World.MapDefinition";
+            }
+            MgResourceIdentifier mdfId = new MgResourceIdentifier(mapName);
+            _map = _creator.CreateMap(mdfId);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    protected MgLayerBase FindLayer(String name)
+    {
+        if (_map != null)
+        {
+            MgLayerCollection layers = _map.getLayers();
+            int lidx = layers.indexOf(name);
+            if (lidx >= 0)
+                return layers.getItem(lidx);
+        }
+        return null;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/RemoveGroup.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/RemoveGroup.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/RemoveGroup.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,46 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class RemoveGroup extends MapLayerOperationExecutor
+{
+    public RemoveGroup(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("RemoveGroup", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"GROUPNAME\"", paramSetId));
+            if (stmt.step()) {
+                String groupName = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+
+                MgLayerGroupCollection groups = _map.getLayerGroups();
+                MgLayerGroup group = groups.getItem(groupName);
+
+                groups.remove(group);
+
+                return TestResult.Ok();
+            }
+            throw new RuntimeException("No group param found for param set: " + paramSetId);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/ShowGroup.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/ShowGroup.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/maplayer/ShowGroup.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,45 @@
+package org.osgeo.mapguide.test.platform.operations.maplayer;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.operations.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class ShowGroup extends MapLayerOperationExecutor
+{
+    public ShowGroup(MgResourceService resSvc, String vm, IMapCreator creator)
+    {
+        super("ShowGroup", resSvc, vm, creator);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            this.CreateMapFromResource(paramSetId);
+
+            SQLiteStatement stmt = _conn.prepare(String.format("Select ParamValue from Params WHERE ParamSet=%d AND ParamName=\"GROUPNAME\"", paramSetId));
+            if (stmt.step()) {
+                String groupName = CommonUtility.GetStringFromStatement(stmt, "ParamValue");
+
+                MgLayerGroupCollection groups = _map.getLayerGroups();
+                MgLayerGroup group = groups.getItem(groupName);
+
+                group.setVisible(true);
+                return TestResult.Boolean(group.getVisible());
+            }
+            throw new RuntimeException("No group param found for param set: " + paramSetId);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/ApplyResourcePackage.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/ApplyResourcePackage.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/ApplyResourcePackage.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,44 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class ApplyResourcePackage extends ResourceServiceOperationExecutor
+{
+    public ApplyResourcePackage(MgResourceService resSvc, String unitTestVm)
+    {
+        super("ApplyResourcePackage", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "PACKAGE", param, true);
+
+            String packagePath = "";
+            if (param.get("PACKAGE") != null)
+                packagePath = param.get("PACKAGE");
+
+            if (CommonUtility.IsWindows()) {
+                //Console.WriteLine("\t\tPackage Path: " + packagePath);
+                MgByteReader reader = CommonUtility.GetByteReaderFromPath(packagePath, false);
+
+                _resourceService.applyResourcePackage(reader);
+
+                return TestResult.Ok();
+            } else {
+                throw new RuntimeException("FIXME: ApplyResourcePackage will kill the mgserver on invalid package files");
+            }
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/ChangeResourceOwner.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/ChangeResourceOwner.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/ChangeResourceOwner.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,44 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class ChangeResourceOwner extends ResourceServiceOperationExecutor
+{
+    public ChangeResourceOwner(MgResourceService resSvc, String unitTestVm)
+    {
+        super("ChangeResourceOwner", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "OWNER", param);
+            ReadParameterValue(paramSetId, "INCLUDEDESCENDANTS", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.get("RESOURCEID") != null)
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            boolean includeDescendants = false;
+            if (param.get("INCLUDEDESCENDANTS") != null)
+                includeDescendants = (param.get("INCLUDEDESCENDANTS").equals("1"));
+            _resourceService.changeResourceOwner(resId, param.get("OWNER"), includeDescendants);
+
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/CopyResource.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/CopyResource.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/CopyResource.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,50 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class CopyResource extends ResourceServiceOperationExecutor
+{
+    public CopyResource(MgResourceService resSvc, String unitTestVm)
+    {
+        super("CopyResource", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "SOURCE", param);
+            ReadParameterValue(paramSetId, "DESTINATION", param);
+
+            MgResourceIdentifier srcId = null;
+            if (param.containsKey("SOURCE"))
+            {
+                srcId = new MgResourceIdentifier(param.get("SOURCE"));
+            }
+
+            MgResourceIdentifier dstId = null;
+            if (param.containsKey("DESTINATION"))
+            {
+                dstId = new MgResourceIdentifier(param.get("DESTINATION"));
+            }
+
+            _resourceService.copyResource(srcId, dstId, false);
+
+            return TestResult.FromByteReader(null);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/DeleteResource.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/DeleteResource.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/DeleteResource.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,43 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class DeleteResource extends ResourceServiceOperationExecutor
+{
+    public DeleteResource(MgResourceService resSvc, String unitTestVm)
+    {
+        super("DeleteResource", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            _resourceService.deleteResource(resId);
+
+            return TestResult.FromByteReader(null);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/DeleteResourceData.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/DeleteResourceData.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/DeleteResourceData.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,47 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class DeleteResourceData extends ResourceServiceOperationExecutor
+{
+    public DeleteResourceData(MgResourceService resSvc, String unitTestVm)
+    {
+        super("DeleteResourceData", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "DATANAME", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            String dataName = "";
+            if (param.containsKey("DATANAME"))
+                dataName = param.get("DATANAME");
+            _resourceService.deleteResourceData(resId, dataName);
+
+            return TestResult.FromByteReader(null);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/EnumerateResourceData.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/EnumerateResourceData.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/EnumerateResourceData.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,43 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class EnumerateResourceData extends ResourceServiceOperationExecutor
+{
+    public EnumerateResourceData(MgResourceService resSvc, String unitTestVm)
+    {
+        super("EnumerateResourceData", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader reader = _resourceService.enumerateResourceData(resId);
+
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/EnumerateResourceReferences.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/EnumerateResourceReferences.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/EnumerateResourceReferences.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,43 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class EnumerateResourceReferences extends ResourceServiceOperationExecutor
+{
+    public EnumerateResourceReferences(MgResourceService resSvc, String unitTestVm)
+    {
+        super("EnumerateResourceReferences", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader reader = _resourceService.enumerateReferences(resId);
+
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/EnumerateResources.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/EnumerateResources.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/EnumerateResources.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,48 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class EnumerateResources extends ResourceServiceOperationExecutor
+{
+    public EnumerateResources(MgResourceService resSvc, String unitTestVm)
+    {
+        super("EnumerateResources", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "TYPE", param);
+            ReadParameterValue(paramSetId, "DEPTH", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.get("RESOURCEID") != null)
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+            String type = "";
+            if (param.get("TYPE") != null)
+                type = param.get("TYPE");
+            int depth = -1;
+            if (param.get("DEPTH") != null)
+                depth = Integer.parseInt(param.get("DEPTH"));
+
+            MgByteReader byteReader = _resourceService.enumerateResources(resId, depth, type);
+
+            return TestResult.FromByteReader(byteReader, "GETRESOURCEDATA");
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetRepositoryContent.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetRepositoryContent.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetRepositoryContent.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,43 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetRepositoryContent extends ResourceServiceOperationExecutor
+{
+    public GetRepositoryContent(MgResourceService resSvc, String unitTestVm)
+    {
+        super("GetRepositoryContent", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader result = _resourceService.getRepositoryContent(resId);
+
+            return TestResult.FromByteReader(result);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetRepositoryHeader.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetRepositoryHeader.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetRepositoryHeader.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,43 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetRepositoryHeader extends ResourceServiceOperationExecutor
+{
+    public GetRepositoryHeader(MgResourceService resSvc, String unitTestVm)
+    {
+        super("GetRepositoryHeader", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader result = _resourceService.getRepositoryHeader(resId);
+
+            return TestResult.FromByteReader(result);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetResourceContent.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetResourceContent.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetResourceContent.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,44 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetResourceContent extends ResourceServiceOperationExecutor
+{
+    public GetResourceContent(MgResourceService resSvc, String unitTestVm)
+    {
+        super("GetResourceContent", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "PREPROCESS", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader reader = _resourceService.getResourceContent(resId);
+
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetResourceData.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetResourceData.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetResourceData.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,44 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetResourceData extends ResourceServiceOperationExecutor
+{
+    public GetResourceData(MgResourceService resSvc, String unitTestVm)
+    {
+        super("GetResourceData", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "DATANAME", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader reader = _resourceService.getResourceData(resId, param.get("DATANAME"));
+
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetResourceHeader.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetResourceHeader.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/GetResourceHeader.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,43 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class GetResourceHeader extends ResourceServiceOperationExecutor
+{
+    public GetResourceHeader(MgResourceService resSvc, String unitTestVm)
+    {
+        super("GetResourceHeader", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader reader = _resourceService.getResourceHeader(resId);
+
+            return TestResult.FromByteReader(reader);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/InheritPermissionsFrom.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/InheritPermissionsFrom.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/InheritPermissionsFrom.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,43 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class InheritPermissionsFrom extends ResourceServiceOperationExecutor
+{
+    public InheritPermissionsFrom(MgResourceService resSvc, String unitTestVm)
+    {
+        super("InheritPermissionsFrom", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            _resourceService.inheritPermissionsFrom(resId);
+
+            return TestResult.FromByteReader(null);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/MoveResource.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/MoveResource.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/MoveResource.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,50 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class MoveResource extends ResourceServiceOperationExecutor
+{
+    public MoveResource(MgResourceService resSvc, String unitTestVm)
+    {
+        super("MoveResource", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "SOURCE", param);
+            ReadParameterValue(paramSetId, "DESTINATION", param);
+
+            MgResourceIdentifier srcId = null;
+            if (param.containsKey("SOURCE"))
+            {
+                srcId = new MgResourceIdentifier(param.get("SOURCE"));
+            }
+
+            MgResourceIdentifier dstId = null;
+            if (param.containsKey("DESTINATION"))
+            {
+                dstId = new MgResourceIdentifier(param.get("DESTINATION"));
+            }
+
+            _resourceService.moveResource(srcId, dstId, false);
+
+            return TestResult.FromByteReader(null);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/RenameResourceData.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/RenameResourceData.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/RenameResourceData.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,45 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class RenameResourceData extends ResourceServiceOperationExecutor
+{
+    public RenameResourceData(MgResourceService resSvc, String unitTestVm)
+    {
+        super("RenameResourceData", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "OLDDATANAME", param);
+            ReadParameterValue(paramSetId, "NEWDATANAME", param);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            _resourceService.renameResourceData(resId, param.get("OLDDATANAME"), param.get("NEWDATANAME"), false);
+
+            return TestResult.FromByteReader(null);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/ResourceServiceOperationExecutor.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/ResourceServiceOperationExecutor.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/ResourceServiceOperationExecutor.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,18 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+
+import com.almworks.sqlite4java.*;
+
+public abstract class ResourceServiceOperationExecutor extends PlatformApiTestExecutor
+{
+    protected MgResourceService _resourceService;
+
+    protected ResourceServiceOperationExecutor(String opName, MgResourceService resSvc, String unitTestVm)
+    {
+        super(opName, ApiTypes.Platform, unitTestVm);
+        _resourceService = resSvc;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/SetResource.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/SetResource.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/SetResource.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,44 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class SetResource extends ResourceServiceOperationExecutor
+{
+    public SetResource(MgResourceService resSvc, String unitTestVm)
+    {
+        super("SetResource", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            CommonUtility.ReadParameterValue(_conn, paramSetId, "RESOURCEID", param);
+            CommonUtility.ReadParameterValue(_conn, paramSetId, "CONTENT", param, true);
+            CommonUtility.ReadParameterValue(_conn, paramSetId, "HEADER", param, true);
+
+            MgResourceIdentifier resId = null;
+            if (param.get("RESOURCEID") != null)
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader content = CommonUtility.GetByteReaderFromPath(param.get("CONTENT"));
+            MgByteReader header = CommonUtility.GetByteReaderFromPath(param.get("HEADER"));
+
+            _resourceService.setResource(resId, content, header);
+
+            return TestResult.Ok();
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/SetResourceData.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/SetResourceData.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/SetResourceData.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,63 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class SetResourceData extends ResourceServiceOperationExecutor
+{
+    public SetResourceData(MgResourceService resSvc, String unitTestVm)
+    {
+        super("SetResourceData", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "DATANAME", param);
+            ReadParameterValue(paramSetId, "DATATYPE", param);
+            ReadParameterValue(paramSetId, "DATA", param, true);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            String extension = CommonUtility.GetExtension(param.get("DATANAME"));
+            String mimeType = CommonUtility.GetMimeType(extension);
+            MgByteSource dataSource = new MgByteSource(param.get("DATA"));
+            dataSource.setMimeType(mimeType);
+            MgByteReader dataReader = dataSource.getReader();
+
+            _resourceService.setResourceData(resId, param.get("DATANAME"), param.get("DATATYPE"), dataReader);
+
+            return TestResult.FromByteReader(null);
+        }
+        catch (MgException ex)
+        {
+            //HACK/FIXME: The test suite is passing paths with incorrect case to this operation (presumably to exercise
+            //this operation in a Linux environment where case-sensitive paths matter), but there's no way in my knowledge 
+            //to perform platform-specific verification of test results. So what we have is an intentionally failing test 
+            //for a platform that has no means to verify that.
+            //
+            //As a workaround, when such bad paths are encountered (that should present themselves as thrown
+            //MgFileNotFoundException objects), return the result that is expected on Windows: An empty result.
+            if (!CommonUtility.IsWindows() && (ex instanceof MgFileNotFoundException)) {
+                return TestResult.FromByteReader(null);
+            } else {
+                return TestResult.FromMgException(ex);
+            }
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/UpdateRepository.java
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/UpdateRepository.java	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Java/src/org/osgeo/mapguide/test/platform/operations/resource/UpdateRepository.java	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,48 @@
+package org.osgeo.mapguide.test.platform.operations.resource;
+
+import java.util.*;
+import com.almworks.sqlite4java.*;
+import org.osgeo.mapguide.*;
+import org.osgeo.mapguide.test.*;
+import org.osgeo.mapguide.test.common.*;
+import org.osgeo.mapguide.test.platform.*;
+
+public class UpdateRepository extends ResourceServiceOperationExecutor
+{
+    public UpdateRepository(MgResourceService resSvc, String unitTestVm)
+    {
+        super("UpdateRepository", resSvc, unitTestVm);
+    }
+
+    public TestResult Execute(int paramSetId)
+    {
+        try
+        {
+            HashMap<String, String> param = new HashMap<String, String>();
+            ReadParameterValue(paramSetId, "RESOURCEID", param);
+            ReadParameterValue(paramSetId, "CONTENT", param, true);
+            ReadParameterValue(paramSetId, "HEADER", param, true);
+
+            MgResourceIdentifier resId = null;
+            if (param.containsKey("RESOURCEID"))
+            {
+                resId = new MgResourceIdentifier(param.get("RESOURCEID"));
+            }
+
+            MgByteReader content = CommonUtility.GetByteReaderFromPath(param.get("CONTENT"));
+            MgByteReader header = CommonUtility.GetByteReaderFromPath(param.get("HEADER"));
+
+            _resourceService.updateRepository(resId, content, header);
+
+            return TestResult.FromByteReader(null);
+        }
+        catch (MgException ex)
+        {
+            return TestResult.FromMgException(ex);
+        }
+        catch (Exception ex)
+        {
+            return TestResult.FromException(ex);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ApiEntUnitTests.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ApiEntUnitTests.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ApiEntUnitTests.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,47 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("HtmlPrinter.php");
+//This script generates the HTML form for Api tests
+
+if (!defined('ApiUnitTests_MAIN_METHOD'))
+{
+    define('ApiUnitTests_MAIN_METHOD', 'ApiUnitTests::main');
+    $test = new ApiUnitTests();
+    $test->main();
+}
+
+class ApiUnitTests
+{
+    public function main()
+    {
+        HtmlPrinter::PrintHtmlHeader("ApiEnt Tests");
+        HtmlPrinter::PrintFormHeader("Run.php");
+
+        //Add your tests here
+        //HtmlPrinter::PrintTestCases("YourTestName", "Path to database file where your data is stored", "Api")
+        HtmlPrinter::PrintTestCases("FeatureService", "../../TestData/FeatureService/FeatureServiceTest.dump", "ApiEnt");
+
+        HtmlPrinter::PrintSelectTestMode();
+        HtmlPrinter::PrintSelectOutput();
+        HtmlPrinter::PrintFormFooter("ApiEnt", "RunTests");
+        HtmlPrinter::PrintHtmlFooter();
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ApiUnitTests.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ApiUnitTests.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ApiUnitTests.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,56 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("HtmlPrinter.php");
+//This script generates the HTML form for Api tests
+
+if (!defined('ApiUnitTests_MAIN_METHOD'))
+{
+    define('ApiUnitTests_MAIN_METHOD', 'ApiUnitTests::main');
+    $test = new ApiUnitTests();
+    $test->main();
+}
+
+class ApiUnitTests
+{
+    public function main()
+    {
+        HtmlPrinter::PrintHtmlHeader("Api Tests");
+        HtmlPrinter::PrintFormHeader("Run.php");
+
+        //Add your tests here
+        //HtmlPrinter::PrintTestCases("YourTestName", "Path to database file where your data is stored", "Api")
+        HtmlPrinter::PrintTestCases("ResourceService", "../../TestData/ResourceService/ResourceServiceTest.dump", "Api");
+        HtmlPrinter::PrintTestCases("DrawingService", "../../TestData/DrawingService/DrawingServiceTest.dump", "Api");
+        HtmlPrinter::PrintTestCases("FeatureService", "../../TestData/FeatureService/FeatureServiceTest.dump", "Api");
+        HtmlPrinter::PrintTestCases("MappingService", "../../TestData/MappingService/MappingServiceTest.dump", "Api");
+        HtmlPrinter::PrintTestCases("SiteServiceTests", "../../TestData/SiteService/SiteServiceTest.dump", "Api");
+        HtmlPrinter::PrintTestCases("ServerAdminTests", "../../TestData/ServerAdmin/ServerAdminTest.dump", "Api");
+        HtmlPrinter::PrintTestCases("MapLayerTests", "../../TestData/MapLayer/MapLayerTest.dump", "Api");
+        HtmlPrinter::PrintTestCases("WebLayoutTests", "../../TestData/WebLayout/WebLayoutTest.dump", "Api");
+
+        HtmlPrinter::PrintTestCases("UnicodeTests", "../../TestData/Unicode/UnicodeTest.dump", "Api");
+
+        HtmlPrinter::PrintSelectTestMode();
+        HtmlPrinter::PrintSelectOutput();
+        HtmlPrinter::PrintFormFooter("Api", "RunTests");
+        HtmlPrinter::PrintHtmlFooter();
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/DrawingServiceAPI.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/DrawingServiceAPI.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/DrawingServiceAPI.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,319 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("master.php");
+require_once("Result.php");
+require_once("Utils.php");
+
+//This file defines the operations that are currently supported for Drawing Service API by the unit test infrastructure.
+//There is one function per operation. Each function tries to retrieve the parameters that are required for its corresponding
+//operation and execute the operation
+//Edit that file to add more operations for Drawing Service API
+
+class DrawingServiceAPI
+{
+    private $unitTestParamVm;
+    private $cred;
+    private $site;
+    private $arrayParam;
+    private $drawingSrvc;
+    private $mimeType;
+
+    function __construct($db)
+    {
+        $this->mimeType="text/xml";
+        Utils::MapAgentInit(WEBCONFIGINI);
+
+        $this->cred = new MgUserInformation();
+        $this->cred->SetMgUsernamePassword("Administrator","admin");
+        $this->cred->SetLocale("en");
+
+        $this->site = new MgSiteConnection();
+        $this->site->Open($this->cred);
+        $this->drawingSrvc = $this->site->CreateService(MgServiceType::DrawingService);
+
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->arrayParam = array();
+    }
+
+    function __destruct()
+    {
+        unset($this->site);
+        unset($this->cred);
+        unset($this->unitTestParamVm);
+        unset($this->arrayParam);
+    }
+
+    function DescribeDrawing ($paramSet)
+    {
+        try
+        {
+            $repId = null;
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->drawingSrvc->DescribeDrawing($repId);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetDrawing($paramSet)
+    {
+        try
+        {
+            $repId = null;
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam['RESOURCEID']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->drawingSrvc->GetDrawing($repId);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateDrawingLayers($paramSet)
+    {
+        try
+        {
+            $contentReader=null;
+            $headerReader=null;
+            $repId = null;
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam['RESOURCEID']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SECTION\"");
+            $this->arrayParam['SECTION']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $collection = $this->drawingSrvc->EnumerateLayers($repId, $this->arrayParam['SECTION']);
+            $byteReader = $collection->ToXml();
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+    function GetDrawingLayer($paramSet)
+    {
+        try
+        {
+            $repId = null;
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SECTION\"");
+            $this->arrayParam["SECTION"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LAYER\"");
+            $this->arrayParam["LAYER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->drawingSrvc->GetLayer($repId, $this->arrayParam['SECTION'], $this->arrayParam['LAYER']);
+
+            $this->mimeType = $byteReader->GetMimeType();
+
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetDrawingSection($paramSet)
+    {
+        try
+        {
+            $repId = null;
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SECTION\"");
+            $this->arrayParam['SECTION']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+            $byteReader = $this->drawingSrvc->GetSection($repId, $this->arrayParam['SECTION']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateDrawingSections($paramSet)
+    {
+        try
+        {
+            $repId = null;
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->drawingSrvc->EnumerateSections($repId);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+
+    }
+
+    function EnumerateDrawingSectionResources($paramSet)
+    {
+        try
+        {
+            $repId = null;
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SECTION\"");
+            $this->arrayParam['SECTION']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->drawingSrvc->EnumerateSectionResources($repId, $this->arrayParam['SECTION']);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetDrawingSectionResource($paramSet)
+    {
+        try
+        {
+            $repId = null;
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCENAME\"");
+            $this->arrayParam['RESOURCENAME']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->drawingSrvc->GetSectionResource($repId, $this->arrayParam['RESOURCENAME']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    //TODO: Not completed yet. Converts $xmlString into a StringCollection object
+    function ToMgStringCollection($xmlString)
+    {
+        $stringCollection = new MgStringCollection();
+        while ( (substr_count($xmlString, "<item>")) > 0)
+        {
+            $start = strpos($xmlString, "<item>");
+            $end = strpos($xmlString, "</item>");
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/DrawingServiceHttpRequests.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/DrawingServiceHttpRequests.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/DrawingServiceHttpRequests.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,213 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("HttpRequest.php");
+require_once("Utils.php");
+
+//This file defines the operations that are currently supported by the unit test infrastructure for Drawing Service methods exposed through Htpp.
+//There is one function per operation. Each function tries to retrieve the parameters that are required for its corresponding
+//operation and send the Http request
+//Edit that file to add more operations from Drawing Service that are exposed through Http
+
+class DrawingServiceHttpRequests
+{
+    private $db;
+    private $unitTestParamVm;
+    private $URL;
+
+    function __construct($db, $newURL)
+    {
+        $this->db = $db;
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->httpRequest = new HttpRequest();
+        $this->URL = $newURL;
+    }
+
+    function __destruct()
+    {
+        unset($this->unitTestParamVm);
+        unset($this->httpRequest);
+        unset($this->db);
+    }
+
+    function DescribeDrawing($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetDrawing($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateDrawingLayers($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SECTION\"");
+            $arrayParam["SECTION"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetDrawingLayer($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SECTION\"");
+            $arrayParam["SECTION"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LAYER\"");
+            $arrayParam["LAYER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetDrawingSection($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SECTION\"");
+            $arrayParam["SECTION"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function GetDrawingSectionResource($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCENAME\"");
+            $arrayParam["RESOURCENAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateDrawingSections($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateDrawingSectionResources($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SECTION\"");
+            $arrayParam["SECTION"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ExecuteOperation.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ExecuteOperation.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ExecuteOperation.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,646 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+
+require_once("DrawingServiceAPI.php");
+require_once("FeatureServiceAPI.php");
+require_once("ResourceServiceAPI.php");
+require_once("SiteServiceAPI.php");
+require_once("ServerAdminAPI.php");
+require_once("MapLayerAPI.php");
+require_once("MappingServiceAPI.php");
+require_once("WebLayoutAPI.php");
+
+require_once("ResourceServiceHttpRequests.php");
+require_once("DrawingServiceHttpRequests.php");
+require_once("FeatureServiceHttpRequests.php");
+require_once("SiteServiceHttpRequests.php");
+require_once("MappingServiceHttpRequests.php");
+require_once("WfsHttpRequests.php");
+require_once("WmsHttpRequests.php");
+
+require_once("Result.php");
+
+//This class creates an object for each possible service that is supported by the infrastructure
+//Then, it parses the operation name and based on that executes the operation
+//Edit this file to add more services and/or operations to the infrastructure
+class ExecuteOperation
+{
+    private $resourceServiceOperation;
+    private $drawingServiceOperation;
+    private $featureServiceOperation;
+    private $mappingServiceOperation;
+    private $siteServiceOperation;
+    private $serverAdminOperation;
+    private $mapLayerOperation;
+    private $webLayoutOperation;
+    private $wfsOperation;
+    private $wmsOperation;
+
+    public function __construct($requestType, $db, $Url)
+    {
+        $this->contentType="";
+        if (substr_count($requestType, "Http"))
+        {
+            $this->resourceServiceOperation = new ResourceServiceHttpRequests($db, $Url);
+            $this->drawingServiceOperation = new DrawingServiceHttpRequests($db, $Url);
+            $this->featureServiceOperation = new FeatureServiceHttpRequests($db, $Url);
+            $this->siteServiceOperation = new SiteServiceHttpRequests($db, $Url);
+            $this->mappingServiceOperation = new MappingServiceHttpRequests($db, $Url);
+            $this->wfsOperation = new WfsHttpRequests($db, $Url);
+            $this->wmsOperation = new WmsHttpRequests($db, $Url);
+        }
+        elseif (substr_count($requestType,"Api"))
+        {
+            $this->resourceServiceOperation = new ResourceServiceAPI($db);
+            $this->drawingServiceOperation = new DrawingServiceAPI($db);
+            $this->featureServiceOperation = new FeatureServiceAPI($db);
+            $this->siteServiceOperation = new SiteServiceAPI($db);
+            $this->serverAdminOperation = new ServerAdminAPI($db);
+            $this->mapLayerOperation = new MapLayerAPI($db);
+            $this->mappingServiceOperation = new MappingServiceAPI($db);
+            $this->webLayoutOperation = new WebLayoutAPI($db);
+        }
+        else
+        {
+            //Error
+        }
+    }
+
+    public function __destruct()
+    {
+        unset($this->resourceServiceOperation);
+        unset($this->drawingServiceOperation);
+        unset($this->featureServiceOperation);
+        unset($this->serverAdminOperation);
+        unset($this->mapLayerOperation);
+        unset($this->mappingServiceOperation);
+        unset($this->webLayoutOperation);
+    }
+
+    public function Execute($paramSet, $operationName)
+    {
+        $actualResult=new Result();
+        //Start Resource Service
+        if ($operationName == "ENUMERATERESOURCES")
+        {
+            $actualResult = $this->resourceServiceOperation->EnumerateResources($paramSet);
+        }
+        elseif ($operationName == "DELETERESOURCE")
+        {
+            $actualResult = $this->resourceServiceOperation->DeleteResource($paramSet);
+        }
+        elseif ($operationName == "GETRESOURCECONTENT")
+        {
+            $actualResult = $this->resourceServiceOperation->GetResourceContent($paramSet);
+        }
+        elseif ($operationName == "GETRESOURCEHEADER")
+        {
+            $actualResult = $this->resourceServiceOperation->GetResourceHeader($paramSet);
+        }
+        elseif ($operationName == "ENUMERATERESOURCEDATA")
+        {
+            $actualResult = $this->resourceServiceOperation->EnumerateResourceData($paramSet);
+        }
+        elseif ($operationName == "GETRESOURCEDATA")
+        {
+            $actualResult = $this->resourceServiceOperation->GetResourceData($paramSet);
+        }
+        elseif ($operationName =="SETRESOURCEDATA")
+        {
+            $actualResult = $this->resourceServiceOperation->SetResourceData($paramSet);
+        }
+        elseif ($operationName =="UPDATERESOURCEDATA")
+        {
+            $actualResult = $this->resourceServiceOperation->UpdateResourceData($paramSet);
+        }
+        elseif ($operationName == "DELETERESOURCEDATA")
+        {
+            $actualResult = $this->resourceServiceOperation->DeleteResourceData($paramSet);
+        }
+        elseif ($operationName == "MOVERESOURCE")
+        {
+            $actualResult = $this->resourceServiceOperation->MoveResource($paramSet);
+        }
+        elseif ($operationName == "APPLYRESOURCEPACKAGE")
+        {
+            $actualResult = $this->resourceServiceOperation->ApplyResourcePackage($paramSet);
+        }
+        elseif ($operationName == "UPDATEREPOSITORY")
+        {
+            $actualResult = $this->resourceServiceOperation->UpdateRepository($paramSet);
+        }
+        elseif ($operationName == "ENUMERATEREPOSITORIES")
+        {
+            $actualResult = $this->resourceServiceOperation->EnumerateRepository($paramSet);
+        }
+        elseif ($operationName == "GETREPOSITORYCONTENT")
+        {
+            $actualResult = $this->resourceServiceOperation->GetRepositoryContent($paramSet);
+        }
+        elseif ($operationName == "GETREPOSITORYHEADER")
+        {
+            $actualResult = $this->resourceServiceOperation->GetRepositoryHeader($paramSet);
+        }
+        elseif ($operationName == "RENAMERESOURCEDATA")
+        {
+            $actualResult = $this->resourceServiceOperation->RenameResourceData($paramSet);
+        }
+        elseif ($operationName == "ENUMERATERESOURCEREFERENCES")
+        {
+            $actualResult = $this->resourceServiceOperation->EnumerateResourceReferences($paramSet);
+        }
+        elseif ($operationName == "COPYRESOURCE")
+        {
+            $actualResult = $this->resourceServiceOperation->CopyResource($paramSet);
+        }
+        elseif ($operationName == "CHANGERESOURCEOWNER")
+        {
+            $actualResult = $this->resourceServiceOperation->ChangeResourceOwner($paramSet);
+        }
+        elseif ($operationName == "INHERITPERMISSIONSFROM")
+        {
+            $actualResult = $this->resourceServiceOperation->InheritPermissionsFrom($paramSet);
+        }
+        elseif ($operationName == "SETRESOURCE")
+        {
+            $actualResult = $this->resourceServiceOperation->SetResource($paramSet);
+        }
+        //End Resource Service
+        //Start Drawing Service
+        elseif ($operationName =="DESCRIBEDRAWING")
+        {
+            $actualResult = $this->drawingServiceOperation->DescribeDrawing($paramSet);
+        }
+        elseif ($operationName == "GETDRAWING")
+        {
+            $actualResult = $this->drawingServiceOperation->GetDrawing($paramSet);
+        }
+        elseif ($operationName == "ENUMERATEDRAWINGLAYERS")
+        {
+            $actualResult = $this->drawingServiceOperation->EnumerateDrawingLayers($paramSet);
+        }
+        elseif ($operationName == "GETDRAWINGLAYER")
+        {
+            $actualResult = $this->drawingServiceOperation->GetDrawingLayer($paramSet);
+        }
+        elseif ($operationName == "GETDRAWINGSECTION")
+        {
+            $actualResult = $this->drawingServiceOperation->GetDrawingSection($paramSet);
+        }
+        elseif ($operationName == "GETDRAWINGSECTIONRESOURCE")
+        {
+            $actualResult = $this->drawingServiceOperation->GetDrawingSectionResource($paramSet);
+        }
+        elseif ($operationName == "ENUMERATEDRAWINGSECTIONS")
+        {
+            $actualResult = $this->drawingServiceOperation->EnumerateDrawingSections($paramSet);
+        }
+        elseif ($operationName == "ENUMERATEDRAWINGSECTIONRESOURCES")
+        {
+            $actualResult = $this->drawingServiceOperation->EnumerateDrawingSectionResources($paramSet);
+        }
+        //End Drawing Service
+        //Start Feature Service
+        elseif ($operationName =="GETFEATUREPROVIDERS")
+        {
+            $actualResult = $this->featureServiceOperation->GetFeatureProviders($paramSet);
+        }
+        elseif ($operationName == "GETPROVIDERCAPABILITIES")
+        {
+            $actualResult = $this->featureServiceOperation->GetProviderCapabilities($paramSet);
+        }
+        elseif ($operationName == "GETCONNECTIONPROPERTYVALUES")
+        {
+            $actualResult = $this->featureServiceOperation->GetConnectionPropertyValues($paramSet);
+        }
+        elseif ($operationName == "DESCRIBEFEATURESCHEMA")
+        {
+            $actualResult = $this->featureServiceOperation->DescribeFeatureSchema($paramSet);
+        }
+        elseif ($operationName == "SELECTFEATURES")
+        {
+            $actualResult = $this->featureServiceOperation->SelectFeatures($paramSet);
+        }
+        elseif ($operationName == "SELECTAGGREGATES")
+        {
+            $actualResult = $this->featureServiceOperation->SelectAggregates($paramSet);
+        }
+        elseif ($operationName == "EXECUTESQLQUERY")
+        {
+            $actualResult = $this->featureServiceOperation->ExecuteSqlQuery($paramSet);
+        }
+        elseif ($operationName == "GETSPATIALCONTEXTS")
+        {
+            $actualResult = $this->featureServiceOperation->GetSpatialContexts($paramSet);
+        }
+        elseif ($operationName == "GETLONGTRANSACTIONS")
+        {
+            $actualResult = $this->featureServiceOperation->GetLongTransactions($paramSet);
+        }
+        elseif ($operationName == "SETLONGTRANSACTION")
+        {
+            $actualResult = $this->featureServiceOperation->SetLongTransaction($paramSet);
+        }
+        elseif ($operationName == "TESTCONNECTION")
+        {
+            $actualResult = $this->featureServiceOperation->TestConnection($paramSet);
+        }
+        elseif ($operationName == "GETSCHEMAS")
+        {
+            $actualResult = $this->featureServiceOperation->GetSchemas($paramSet);
+        }
+        elseif ($operationName == "GETCLASSES")
+        {
+            $actualResult = $this->featureServiceOperation->GetClasses($paramSet);
+        }
+        //End Feature Service
+        //Start Site Service
+        elseif ($operationName=="CREATESESSION")
+        {
+            $actualResult = $this->siteServiceOperation->CreateSession($paramSet);
+        }
+        elseif ($operationName=="GETUSERFORSESSION")
+        {
+            $actualResult = $this->siteServiceOperation->GetUserForSession($paramSet);
+        }
+        elseif ($operationName=="ENUMERATEUSERS")
+        {
+            $actualResult = $this->siteServiceOperation->EnumerateUsers($paramSet);
+        }
+        elseif ($operationName=="ADDUSER")
+        {
+            $actualResult = $this->siteServiceOperation->AddUser($paramSet);
+        }
+        elseif ($operationName=="UPDATEUSER")
+        {
+            $actualResult = $this->siteServiceOperation->UpdateUser($paramSet);
+        }
+        elseif ($operationName=="DELETEUSERS")
+        {
+            $actualResult = $this->siteServiceOperation->DeleteUsers($paramSet);
+        }
+        elseif ($operationName=="ENUMERATEGROUPS")
+        {
+            $actualResult = $this->siteServiceOperation->EnumerateGroups($paramSet);
+        }
+        elseif ($operationName=="ENUMERATEGROUPS2")
+        {
+            $actualResult = $this->siteServiceOperation->EnumerateGroups2($paramSet);
+        }
+        elseif ($operationName=="ADDGROUP")
+        {
+            $actualResult = $this->siteServiceOperation->AddGroup($paramSet);
+        }
+        elseif ($operationName=="UPDATEGROUP")
+        {
+            $actualResult = $this->siteServiceOperation->UpdateGroup($paramSet);
+        }
+        elseif ($operationName=="DELETEGROUPS")
+        {
+            $actualResult = $this->siteServiceOperation->DeleteGroups($paramSet);
+        }
+        elseif ($operationName=="GRANTGROUPMEMBERSHIPSTOUSERS")
+        {
+            $actualResult = $this->siteServiceOperation->GrantGroupMembershipsToUsers($paramSet);
+        }
+        elseif ($operationName=="GRANTROLEMEMBERSHIPSTOUSERS")
+        {
+            $actualResult = $this->siteServiceOperation->GrantRoleMembershipsToUsers($paramSet);
+        }
+        elseif ($operationName=="REVOKEGROUPMEMBERSHIPSFROMUSERS")
+        {
+            $actualResult=$this->siteServiceOperation->RevokeGroupMembershipsFromUsers($paramSet);
+        }
+        elseif ($operationName=="REVOKEROLEMEMBERSHIPSFROMUSERS")
+        {
+            $actualResult=$this->siteServiceOperation->RevokeRoleMembershipsFromUsers($paramSet);
+        }
+        elseif ($operationName=="GRANTROLEMEMBERSHIPSTOGROUPS")
+        {
+            $actualResult = $this->siteServiceOperation->GrantRoleMembershipsToGroups($paramSet);
+        }
+        elseif ($operationName=="REVOKEROLEMEMBERSHIPSFROMGROUPS")
+        {
+            $actualResult = $this->siteServiceOperation->RevokeRoleMembershipsFromGroups($paramSet);
+        }
+        elseif ($operationName=="ENUMERATEROLES")
+        {
+            $actualResult = $this->siteServiceOperation->EnumerateRoles($paramSet);
+        }
+        elseif ($operationName=="ENUMERATEROLES2")
+        {
+            $actualResult = $this->siteServiceOperation->EnumerateRoles2($paramSet);
+        }
+
+        //End Site Service
+        //Start Server Admin
+        elseif ($operationName=="GETINFO")
+        {
+            $actualResult = $this->serverAdminOperation->GetProperties($paramSet);
+        }
+        elseif ($operationName=="GETSTATISTICS")
+        {
+            $actualResult = $this->serverAdminOperation->GetProperties($paramSet);
+        }
+        elseif ($operationName=="OFFLINE")
+        {
+            $actualResult = $this->serverAdminOperation->TakeOffline($paramSet);
+        }
+        elseif ($operationName=="ONLINE")
+        {
+            $actualResult = $this->serverAdminOperation->BringOnline($paramSet);
+        }
+        elseif ($operationName=="GETLOG")
+        {
+            $actualResult = $this->serverAdminOperation->GetLog($paramSet);
+        }
+        elseif ($operationName=="GETLOGBYDATE")
+        {
+            $actualResult = $this->serverAdminOperation->GetLogByDate($paramSet);
+        }
+        elseif ($operationName=="CLEARLOG")
+        {
+            $actualResult = $this->serverAdminOperation->ClearLog($paramSet);
+        }
+        elseif ($operationName=="ENUMERATELOGS")
+        {
+            $actualResult = $this->serverAdminOperation->EnumerateLogs($paramSet);
+        }
+        elseif ($operationName=="RENAMELOG")
+        {
+            $actualResult = $this->serverAdminOperation->RenameLog($paramSet);
+        }
+        elseif ($operationName=="DELETELOG")
+        {
+            $actualResult = $this->serverAdminOperation->DeleteLog($paramSet);
+        }
+        elseif ($operationName=="ENUMERATEPACKAGES")
+        {
+            $actualResult = $this->serverAdminOperation->EnumeratePackages($paramSet);
+        }
+        elseif ($operationName=="DELETEPACKAGE")
+        {
+            $actualResult = $this->serverAdminOperation->DeletePackage($paramSet);
+        }
+        elseif ($operationName=="LOADPACKAGE")
+        {
+            $actualResult = $this->serverAdminOperation->LoadPackage($paramSet);
+        }
+        elseif ($operationName=="GETPACKAGELOG")
+        {
+            $actualResult = $this->serverAdminOperation->GetPackageLog($paramSet);
+        }
+        elseif ($operationName=="GETPACKAGESTATUS")
+        {
+            $actualResult = $this->serverAdminOperation->GetPackageStatus($paramSet);
+        }
+        //End Server Admin
+        //Start MapLayer API
+        elseif ($operationName=="GETMAPEXTENT")
+        {
+            $actualResult = $this->mapLayerOperation->GetMapExtent($paramSet);
+        }
+        elseif ($operationName=="GETCOORDINATESYSTEM")
+        {
+            $actualResult = $this->mapLayerOperation->GetCoordinateSystem($paramSet);
+        }
+        elseif ($operationName=="GETMAPNAME")
+        {
+            $actualResult = $this->mapLayerOperation->GetName($paramSet);
+        }
+        elseif ($operationName=="GETLAYERS")
+        {
+            $actualResult = $this->mapLayerOperation->GetLayers($paramSet);
+        }
+        elseif ($operationName=="GETGROUPS")
+        {
+            $actualResult = $this->mapLayerOperation->GetLayerGroups($paramSet);
+        }
+        elseif ($operationName=="GETLAYERVISIBILITY")
+        {
+            $actualResult = $this->mapLayerOperation->GetLayerVisibility($paramSet);
+        }
+        elseif ($operationName=="GETDISPLAYINLEGEND")
+        {
+            $actualResult = $this->mapLayerOperation->GetDisplayInLegend($paramSet);
+        }
+        elseif ($operationName=="GETLEGENDLABEL")
+        {
+            $actualResult = $this->mapLayerOperation->GetLegendLabel($paramSet);
+        }
+        elseif ($operationName=="GETLAYERDEFINITION")
+        {
+            $actualResult = $this->mapLayerOperation->GetLayerDefinition($paramSet);
+        }
+        elseif ($operationName=="ISLAYERVISIBLE")
+        {
+            $actualResult = $this->mapLayerOperation->IsLayerVisible($paramSet);
+        }
+        elseif ($operationName=="ADDLAYERGROUP")
+        {
+            $actualResult = $this->mapLayerOperation->AddGroup($paramSet);
+        }
+        elseif ($operationName=="ADDLAYER")
+        {
+            $actualResult = $this->mapLayerOperation->AddLayer($paramSet);
+        }
+        elseif ($operationName=="HIDEGROUP")
+        {
+            $actualResult = $this->mapLayerOperation->HideGroup($paramSet);
+        }
+        elseif ($operationName=="SHOWGROUP")
+        {
+            $actualResult = $this->mapLayerOperation->ShowGroup($paramSet);
+        }
+        elseif ($operationName=="GETVIEWCENTER")
+        {
+            $actualResult = $this->mapLayerOperation->GetViewCenter($paramSet);
+        }
+        elseif ($operationName=="GETVIEWSCALE")
+        {
+            $actualResult = $this->mapLayerOperation->GetViewScale($paramSet);
+        }
+        elseif ($operationName=="GETDATAEXTENT")
+        {
+            $actualResult = $this->mapLayerOperation->GetDataExtent($paramSet);
+        }
+        elseif ($operationName=="REMOVEGROUP")
+        {
+            $actualResult = $this->mapLayerOperation->RemoveGroup($paramSet);
+        }
+        elseif ($operationName=="LAYEREXISTS")
+        {
+            $actualResult = $this->mapLayerOperation->LayerExists($paramSet);
+        }
+        elseif ($operationName == "GETLAYERFEATURESOURCE")
+        {
+            $actualResult = $this->mapLayerOperation->GetFeatureSourceId($paramSet);
+        }
+        elseif ($operationName == "GETLAYERFEATURECLASS")
+        {
+            $actualResult = $this->mapLayerOperation->GetFeatureClassName($paramSet);
+        }
+        elseif ($operationName == "GETMAP")
+        {
+            $actualResult = $this->mappingServiceOperation->GetMap($paramSet);
+        }
+        //End MapLayer API
+        //Start WebLayout API
+        elseif ($operationName == "WL_GETTITLE")
+        {
+            $actualResult = $this->webLayoutOperation->GetTitle($paramSet);
+        }
+        elseif ($operationName == "WL_GETMAPDEFINITION")
+        {
+            $actualResult = $this->webLayoutOperation->GetMapDefinition($paramSet);
+        }
+        elseif ($operationName == "WL_GETSCALE")
+        {
+            $actualResult = $this->webLayoutOperation->GetScale($paramSet);
+        }
+        elseif ($operationName == "WL_GETCENTER")
+        {
+            $actualResult = $this->webLayoutOperation->GetCenter($paramSet);
+        }
+        elseif ($operationName == "WL_SHOWTOOLBAR")
+        {
+            $actualResult = $this->webLayoutOperation->ShowToolbar($paramSet);
+        }
+        elseif ($operationName == "WL_SHOWSTATUSBAR")
+        {
+            $actualResult = $this->webLayoutOperation->ShowStatusbar($paramSet);
+        }
+        elseif ($operationName == "WL_SHOWTASKPANE")
+        {
+            $actualResult = $this->webLayoutOperation->ShowTaskpane($paramSet);
+        }
+        elseif ($operationName == "WL_SHOWTASKBAR")
+        {
+            $actualResult = $this->webLayoutOperation->ShowTaskbar($paramSet);
+        }
+        elseif ($operationName == "WL_SHOWLEGEND")
+        {
+            $actualResult = $this->webLayoutOperation->ShowLegend($paramSet);
+        }
+        elseif ($operationName == "WL_SHOWPROPERTIES")
+        {
+            $actualResult = $this->webLayoutOperation->ShowProperties($paramSet);
+        }
+        elseif ($operationName == "WL_GETTASKPANEWIDTH")
+        {
+            $actualResult = $this->webLayoutOperation->GetTaskPaneWidth($paramSet);
+        }
+        elseif ($operationName == "WL_GETINFORMATIONPANEWIDTH")
+        {
+            $actualResult = $this->webLayoutOperation->GetInformationPaneWidth($paramSet);
+        }
+        elseif ($operationName == "WL_GETINITIALTASKURL")
+        {
+            $actualResult = $this->webLayoutOperation->GetInitialTaskUrl($paramSet);
+        }
+        elseif ($operationName == "WL_SHOWCONTEXTMENU")
+        {
+            $actualResult = $this->webLayoutOperation->ShowContextMenu($paramSet);
+        }
+        elseif ($operationName == "WL_TESTUIITEM")
+        {
+            $actualResult = $this->webLayoutOperation->TestWidget($paramSet);
+        }
+        elseif ($operationName == "WL_HOMETOOLTIP")
+        {
+            $actualResult = $this->webLayoutOperation->HomeTooltip($paramSet);
+        }
+        elseif ($operationName == "WL_HOMEDESCRIPTION")
+        {
+            $actualResult = $this->webLayoutOperation->HomeDescription($paramSet);
+        }
+        elseif ($operationName == "WL_BACKTOOLTIP")
+        {
+            $actualResult = $this->webLayoutOperation->BackTooltip($paramSet);
+        }
+        elseif ($operationName == "WL_BACKDESCRIPTION")
+        {
+            $actualResult = $this->webLayoutOperation->BackDescription($paramSet);
+        }
+        elseif ($operationName == "WL_FORWARDTOOLTIP")
+        {
+            $actualResult = $this->webLayoutOperation->ForwardTooltip($paramSet);
+        }
+        elseif ($operationName == "WL_FORWARDDESCRIPTION")
+        {
+            $actualResult = $this->webLayoutOperation->ForwardDescription($paramSet);
+        }
+        elseif ($operationName == "WL_TASKSNAME")
+        {
+            $actualResult = $this->webLayoutOperation->TasksName($paramSet);
+        }
+        elseif ($operationName == "WL_TASKSTOOLTIP")
+        {
+            $actualResult = $this->webLayoutOperation->TasksTooltip($paramSet);
+        }
+        elseif ($operationName == "WL_TASKSDESCRIPTION")
+        {
+            $actualResult = $this->webLayoutOperation->TasksDescription($paramSet);
+        }
+
+        //End WebLayout API
+        //Start Wfs
+        elseif($operationName == "WfsGetCapabilities")
+        {
+            $actualResult = $this->wfsOperation->GetCapabilities($paramSet);
+        }
+        elseif($operationName == "WfsDescribeFeatureType")
+        {
+            $actualResult = $this->wfsOperation->DescribeFeatureType($paramSet);
+        }
+        elseif($operationName == "WfsGetFeature_GET")
+        {
+            $actualResult = $this->wfsOperation->GetFeature($paramSet);
+        }
+        elseif($operationName == "WfsGetFeature_POST")
+        {
+            $actualResult = $this->wfsOperation->GetFeature($paramSet, "POST");
+        }
+        //End Wfs
+        //Start Wms
+        elseif($operationName == "WmsGetCapabilities")
+        {
+            $actualResult = $this->wmsOperation->GetCapabilities($paramSet);
+        }
+        elseif($operationName == "WmsGetMap")
+        {
+
+            $actualResult = $this->wmsOperation->GetMap($paramSet);
+        }
+        elseif($operationName == "WmsGetFeatureInfo")
+        {
+
+            $actualResult = $this->wmsOperation->GetFeatureInfo($paramSet);
+        }
+        //End Wms
+        else
+        {
+            $actualResult = new Result ("Unknown operation", "text/plain");
+        }
+
+        return $actualResult;
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/FeatureServiceAPI.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/FeatureServiceAPI.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/FeatureServiceAPI.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,533 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("master.php");
+require_once("Utils.php");
+require_once("Result.php");
+
+//This file defines the operations that are currently supported by the unit test infrastructure for Feature Service API .
+//There is one function per operation. Each function tries to retrieve the parameters that are required for its corresponding
+//operation and executes the operation
+//Edit that file to add more operations for Feature Service API
+
+class FeatureServiceAPI
+{
+    private $unitTestParamVm;
+    private $cred;
+    private $site;
+    private $arrayParam;
+    private $featureSrvc;
+    private $mimeType;
+
+
+    function __construct($db)
+    {
+        $this->mimeType="text/xml";
+        Utils::MapAgentInit(WEBCONFIGINI);
+
+        $this->cred = new MgUserInformation();
+        $this->cred->SetMgUsernamePassword("Administrator","admin");
+        $this->cred->SetLocale("en");
+
+        $this->site = new MgSiteConnection();
+        $this->site->Open($this->cred);
+        $this->featureSrvc = $this->site->CreateService(MgServiceType::FeatureService);
+
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->arrayParam = array();
+    }
+
+
+    function __destruct()
+    {
+        unset($this->site);
+        unset($this->cred);
+        unset($this->unitTestParamVm);
+        unset($this->arrayParam);
+    }
+
+
+    function GetFeatureProviders($paramSet)
+    {
+        try
+        {
+            $byteReader = $this->featureSrvc->GetFeatureProviders();
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function GetProviderCapabilities($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PROVIDER\"");
+            $this->arrayParam['PROVIDER']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $byteReader = $this->featureSrvc->GetCapabilities($this->arrayParam['PROVIDER']);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function GetConnectionPropertyValues($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PROVIDER\"");
+            $this->arrayParam['PROVIDER']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PROPERTY\"");
+            $arrayParam["PROPERTY"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CONNECTIONSTRING\"");
+            $arrayParam["CONNECTIONSTRING"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $connectionProperties = $this->featureSrvc->GetConnectionPropertyValues($this->arrayParam['PROVIDER'], $arrayParam["PROPERTY"], $arrayParam["CONNECTIONSTRING"]);
+
+            return new Result(Utils::MgStringCollectionToString($connectionProperties), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function DescribeFeatureSchema($paramSet)
+    {
+        try
+        {
+            $repId = null;
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam['RESOURCEID']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SCHEMA\"");
+            $this->arrayParam['SCHEMA']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $xmlSchema = $this->featureSrvc->DescribeSchemaAsXml($repId, $this->arrayParam['SCHEMA']);
+
+            return new Result($xmlSchema, "text/xml");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function SelectFeatures($paramSet)
+    {
+        try
+        {
+            $repId = null;
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CLASSNAME\"");
+            $this->arrayParam["CLASSNAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            //TODO: used the value retrieved from the database to create the MgStringCollection object
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PROPERTIES\""))
+            {
+                $this->arrayParam["PROPERTIES"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"FILTER\""))
+            {
+                $this->arrayParam["FILTER"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            $queryOptions = new MgFeatureQueryOptions();
+            //TODO: Set query options
+            if (array_key_exists("FILTER", $this->arrayParam))
+            {
+                $queryOptions->SetFilter($this->arrayParam["FILTER"]);
+            }
+
+            //PROPERTIES should be stored in the database as comma separated string without spaces
+            if (array_key_exists("PROPERTIES", $this->arrayParam))
+            {
+                $stringArray = explode(",", $this->arrayParam["PROPERTIES"]);
+
+                for ($i=0; $i<count($stringArray); $i++)
+                {
+                    $queryOptions->AddFeatureProperty($stringArray[$i]);
+                }
+            }
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $featureReader = $this->featureSrvc->SelectFeatures($repId, $this->arrayParam['CLASSNAME'], $queryOptions);
+            $byteReader = $featureReader->ToXml();
+            $featureReader->Close();
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    //TODO: Verify the implementation is correct
+    function SelectAggregates($paramSet)
+    {
+        try
+        {
+            $repId = null;
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CLASSNAME\"");
+            $this->arrayParam["CLASSNAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $collection = new MgStringCollection();
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $options = new MgFeatureAggregateOptions();
+
+            $featureReader = $this->featureSrvc->SelectAggregate($repId, $this->arrayParam['CLASSNAME'], $options);
+
+            $byteReader = $featureReader->ToXml();
+            $featureReader->Close();
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function ExecuteSqlQuery($paramSet)
+    {
+        try
+        {
+            $repId = null;
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam['RESOURCEID']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SQL\"");
+            $this->arrayParam['SQL']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $sqlReader = $this->featureSrvc->ExecuteSqlQuery($repId, $this->arrayParam['SQL']);
+            $byteReader = $sqlReader->ToXml();
+            $sqlReader->Close();
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function GetSpatialContexts($paramSet)
+    {
+        try
+        {
+            $repId = null;
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam['RESOURCEID']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"ACTIVEONLY\"");
+            $this->arrayParam['ACTIVEONLY']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            if ($this->arrayParam['ACTIVEONLY']== "0")
+            {
+                $active=false;
+            }
+            else
+            {
+                $active=true;
+            }
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $spatialContextReader = $this->featureSrvc->GetSpatialContexts($repId, $active);
+
+            $byteReader = $spatialContextReader->ToXml();
+            $spatialContextReader->Close();
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function GetLongTransactions($paramSet)
+    {
+        try
+        {
+            $repId = null;
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam['RESOURCEID']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"ACTIVEONLY\"");
+            $this->arrayParam['ACTIVEONLY']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            if ($this->arrayParam['ACTIVEONLY']=="0")
+            {
+                $active=true;
+            }
+            else
+            {
+                $active=false;
+            }
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $longTransactionReader = $this->featureSrvc->GetLongTransactions($repId, $active);
+            $byteReader = $longTransactionReader->ToXml();
+            $longTransactionReader->Close();
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function SetLongTransaction($paramSet)
+    {
+        try
+        {
+            $repId = null;
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam['RESOURCEID']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LONGTRANSACTIONNAME\"");
+            $this->arrayParam['LONGTRANSACTIONNAME']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CREATESESSION\"");
+            $this->arrayParam['CREATESESSION']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            if ($this->arrayParam['CREATESESSION']=="TRUE")
+            {
+                $mgsite = $this->site->GetSite();
+                $sessionID = $mgsite->CreateSession();
+                $this->cred->SetMgSessionId($sessionID);
+            }
+
+            $response = $this->featureSrvc->SetLongTransaction($repId, $this->arrayParam['LONGTRANSACTIONNAME']);
+            $result = new Result(Utils::BooleanToString($response), "text/plain");
+
+            return $result;
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function TestConnection($paramSet)
+    {
+        try
+        {
+            $response=false;
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            if ($this->unitTestParamVm->GetString("ParamValue") != "")
+            {
+                $repId = null;
+                $this->arrayParam['RESOURCEID']=$this->unitTestParamVm->GetString("ParamValue");
+
+                if (array_key_exists("RESOURCEID", $this->arrayParam))
+                {
+                    $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+                }
+
+                $response = $this->featureSrvc->TestConnection($repId);
+            }
+            else
+            {
+                $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PROVIDER\"");
+                $this->arrayParam["PROVIDER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+                $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CONNECTIONSTRING\"");
+                $this->arrayParam["CONNECTIONSTRING"]=$this->unitTestParamVm->GetString("ParamValue");
+
+                $response = $this->featureSrvc->TestConnection($this->arrayParam["PROVIDER"], $this->arrayParam["CONNECTIONSTRING"]);
+            }
+            $result = new Result(Utils::BooleanToString($response), "text/plain");
+
+            return $result;
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function GetSchemas($paramSet)
+    {
+        try
+        {
+            $repId = null;
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam['RESOURCEID']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $schemas = $this->featureSrvc->GetSchemas($repId);
+
+            return new Result(Utils::MgStringCollectionToString($schemas), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function GetClasses($paramSet)
+    {
+        try
+        {
+            $repId = null;
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $this->arrayParam['RESOURCEID']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SCHEMANAME\"");
+            $this->arrayParam['SCHEMANAME']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            if (array_key_exists("RESOURCEID", $this->arrayParam))
+            {
+                $repId = new MgResourceIdentifier($this->arrayParam["RESOURCEID"]);
+            }
+
+            $classes = $this->featureSrvc->GetClasses($repId, $this->arrayParam['SCHEMANAME']);
+
+            return new Result(Utils::MgStringCollectionToString($classes), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/FeatureServiceHttpRequests.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/FeatureServiceHttpRequests.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/FeatureServiceHttpRequests.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,296 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+
+require_once("HttpRequest.php");
+require_once("Utils.php");
+
+//This file defines the operations that are currently supported by the unit test infrastructure for Feature Service methods exposed through Htpp.
+//There is one function per operation. Each function tries to retrieve the parameters that are required for its corresponding
+//operation and send the Http request
+//Edit that file to add more operations from Feature Service that are exposed through Http
+
+class FeatureServiceHttpRequests
+{
+    private $unitTestParamVm;
+    private $URL;
+    private $db;
+
+
+    function __construct($db, $newURL)
+    {
+        $this->db=$db;
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->httpRequest = new HttpRequest();
+        $this->URL = $newURL;
+    }
+
+
+    function __destruct()
+    {
+        $this->unitTestParamVm->SqlFinalize();
+        unset($this->httpRequest);
+    }
+
+
+    function GetFeatureProviders($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function GetProviderCapabilities($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PROVIDER\"");
+            $arrayParam["PROVIDER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function GetConnectionPropertyValues($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PROVIDER\"");
+            $arrayParam["PROVIDER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PROPERTY\"");
+            $arrayParam["PROPERTY"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CONNECTIONSTRING\"");
+            $arrayParam["CONNECTIONSTRING"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function DescribeFeatureSchema($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SCHEMA\"");
+            $arrayParam["SCHEMA"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function SelectFeatures($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CLASSNAME\"");
+            $arrayParam["CLASSNAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PROPERTIES\"");
+            $arrayParam["PROPERTIES"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"FILTER\"");
+            $arrayParam["FILTER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function SelectAggregates($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CLASSNAME\"");
+            $arrayParam["CLASSNAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GEOMPROPERTY\"");
+            $arrayParam["GEOMPROPERTY"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PROPERTIES\"");
+            $arrayParam["PROPERTIES"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"FILTER\"");
+            $arrayParam["FILTER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SPATIALOP\"");
+            $arrayParam["SPATIALOP"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GEOMETRY\"");
+            $arrayParam["GEOMETRY"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function ExecuteSqlQuery($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SQL\"");
+            $arrayParam["SQL"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    //TODO:How to represent the check box in the request
+    function GetSpatialContexts($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"ACTIVEONLY\"");
+            $arrayParam["ACTIVEONLY"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    //TODO:How to represent the check box in the request
+    function GetLongTransactions($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"ACTIVEONLY\"");
+            $this->arrayParam['ACTIVEONLY']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function TestConnection($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            if ($this->unitTestParamVm->GetString("ParamValue") != "")
+            {
+                $arrayParam['RESOURCEID']=$this->unitTestParamVm->GetString("ParamValue");
+            }
+            else
+            {
+                $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PROVIDER\"");
+                $arrayParam["PROVIDER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+                $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CONNECTIONSTRING\"");
+                $arrayParam["CONNECTIONSTRING"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HtmlPrinter.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HtmlPrinter.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HtmlPrinter.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,167 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("Utils.php");
+
+//This is a helper class that contains methods that output data in an Html format.
+
+class HtmlPrinter
+{
+    public static function PrintHtmlHeader($title)
+    {
+        print ("<html>\n");
+        print ("<head>\n");
+        print ("<title>$title</title>\n");
+        print ("</head>\n");
+        print ("<body>\n");
+        print("<h2>$title</h2>\n");
+    }
+
+    public static function PrintTestCases($testSuite, $dumpFilePath, $testType)
+    {
+        try
+        {
+            self::PrintTestTableHeader($testSuite, "Test Case" , "Description", "Prerequisite");
+
+            $dbPath = Utils::GetDbPath($dumpFilePath);
+            $dbName = Utils::GetPath($dbPath);
+            if (file_exists($dbName))
+            {
+                $db = new SqliteDB();
+                $db->Open($dbName);
+                $vm = new SqliteVM($db, true);
+
+
+                $status = $vm->Execute("Select * from TestCase WHERE TestType=\"$testType\" Order by ExecuteSequence;");
+                while ($status == SQLITE_ROW)
+                {
+                    $testName = $vm->GetString("TestName");
+                    $description = $vm->GetString("Description");
+                    $prerequisite = $vm->GetString("Prerequisite");
+                    print("<tr>\n");
+                    print("<td><input type=\"radio\" name=\"testName\" value=\"$testName\"></td>\n");
+                    printf("<td>%s</td>\n<td>%s</td><td>%s</td>\n", $testName, $description, $prerequisite);
+                    print("<td><input type=\"hidden\" name=\"$testName:dbPath\" value=\"$dbPath\"></td>\n");
+                    print("</tr>\n");
+                    $status = $vm->NextRow();
+                }
+                unset($vm);
+                unset($db);
+            }
+        }
+        catch (SqliteException $s)
+        {
+            print $s->GetExceptionMessage();
+        }
+        self::printTableEnd();
+    }
+
+    public static function PrintTestTableHeader($caption="", $c1="", $c2="", $c3="")
+    {
+        print("<table border=\"1\" cellpadding=\"5\">\n");
+        print("<h3><b>$caption</b></h3>");
+        print("<tr>\n");
+        printf("<td>&nbsp</td>\n<th>%s</th>\n<th>%s</th><th>%s</th><th> </th>\n", $c1, $c2, $c3);
+        print("</tr>\n");
+    }
+
+    public static function printTableEnd()
+    {
+        print("</table><br />\n");
+    }
+
+    public static function PrintSelectTestMode()
+    {
+        print("<h3>Select Test Mode</h3>\n");
+        print("<p>\n");
+        print("<select name=\"testExecutionMode\">\n");
+        print("<option value=\"validate\">Validate\n");
+        print("<option value=\"dump\">Dump\n");
+        print("</select>\n");
+        print("</p>\n");
+    }
+
+    public static function PrintSelectOutput()
+    {
+        print("<h3>Select Output</h3>\n");
+        print("<p>\n");
+        print("<select name=\"output\">\n");
+        print("<option value=\"html\">Html\n");
+        print("<option value=\"text\">Text\n");
+        print("</select>\n");
+        print("</p>\n");
+    }
+
+    public static function PrintHtmlFooter()
+    {
+        print ("</body>\n");
+        print ("</html>\n");
+    }
+
+    public static function PrintFormHeader($target)
+    {
+        print ("<form name=\"input\" action=\"$target\" method=\"POST\" id=\"myform\">\n");
+    }
+
+    public static function PrintFormFooter($requestType, $action)
+    {
+        print("<td><input type=\"hidden\" name=\"requestType\" value=\"$requestType\"></td>\n");
+        print ("<input type=\"submit\" value=\"$action\" ID=\"$action\" NAME=\"$action\"> <input type=\"reset\" ID=\"Reset1\" NAME=\"Reset1\">\n");
+        print ("</form>\n");
+    }
+
+    public static function AddResultRow($operation, $outcome, $paramSet, $actualResult="", $expectedResult="")
+    {
+        $checkBox="<input type=\"checkbox\" name=$paramSet>";
+        if ( $outcome=="fail")
+        {
+            print("<tr bgcolor=\"tomato\">\n");
+        }
+        else
+        {
+            print ("<tr bgcolor=\"lightgreen\">\n");
+        }
+        printf("<td>%s</td><td>%s</td><td>%s</td>\n<td>%s</td>\n<td>%s</td>\n<td>%s</td>\n", $checkBox, $paramSet, $operation, $outcome, $actualResult, $expectedResult);
+        print("</tr>");
+    }
+
+    public static function PrintResultTableHeader($caption="")
+    {
+        print("<table scroll=\"no\" border=\"1\" cellpadding=\"5\">\n");
+        print("<h3><b>$caption</b></h3>");
+        print("<col width=10><col width=20><col width=40><col width=20><col width=180><col width=180><col>");
+        print("<tr>\n");
+        print("<td>&nbsp</td><th>Param Set</th><th>Operation</th>\n<th>Outcome</th>\n<th>Actual Result</th>\n<th>Expected Result</th>\n");
+        print("</tr>\n");
+    }
+
+    public static function PrintGenerateFormHiddenFields()
+    {
+        $output = $_POST['output'];
+        $testName = $_POST['testName'];
+        $dbPath =$_POST[$testName.":dbPath"];
+
+        print("<td><input type=\"hidden\" name=\"output\" value=\"$output\"></td>\n");
+        print("<td><input type=\"hidden\" name=\"testExecutionMode\" value=\"generate\"></td>\n");
+        print("<td><input type=\"hidden\" name=\"testName\" value=\"$testName\"></td>\n");
+        print("<td><input type=\"hidden\" name=\"$testName:dbPath\" value=\"$dbPath\"></td>\n");
+    }
+}
+
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HttpEntUnitTests.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HttpEntUnitTests.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HttpEntUnitTests.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,49 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+//This script generates the HTML form for Api tests
+
+require_once("HtmlPrinter.php");
+
+if (!defined('HttpUnitTests_MAIN_METHOD'))
+{
+
+    define('HttpUnitTests_MAIN_METHOD', 'HttpUnitTests::main');
+    $test = new HttpUnitTests();
+    $test->main();
+}
+
+class HttpUnitTests
+{
+    public function main()
+    {
+        HtmlPrinter::PrintHtmlHeader("HttpEnt Tests");
+        HtmlPrinter::PrintFormHeader("Run.php");
+
+        //Add your tests here
+        //HtmlPrinter::PrintTestCases("YourTestName", "Path to database file where your data is stored", "Http")
+        HtmlPrinter::PrintTestCases("FeatureService", "../../TestData/FeatureService/FeatureServiceTest.dump", "HttpEnt");
+
+        HtmlPrinter::PrintSelectTestMode();
+        HtmlPrinter::PrintSelectOutput();
+        HtmlPrinter::PrintFormFooter("HttpEnt", "RunTests");
+        HtmlPrinter::PrintHtmlFooter();
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HttpRequest.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HttpRequest.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HttpRequest.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,148 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+//This class sends Http requests using curl. For more information on curl see www.php.net
+
+require_once("Result.php");
+
+class HttpRequest
+{
+    private $ch;
+    public $cookieJar = "UnitTestCookieJar";
+
+    public function __construct()
+    {
+        $this->ch = curl_init();
+    }
+
+    public function __destruct()
+    {
+        curl_close($this->ch );
+    }
+
+    public function SendRequest($httpRequest, $arrayParam=null, $requestType="GET")
+    {
+        curl_setopt($this->ch, CURLOPT_HTTPGET, true);
+        if ("POST" == $requestType)
+        {
+            curl_setopt($this->ch, CURLOPT_POST,1);
+            curl_setopt($this->ch, CURLOPT_POSTFIELDS, $arrayParam);
+        }
+        else
+        {
+            //form the http request for GET requests
+            //encode all non-alphanumeric characters except -_
+            if (isset($arrayParam))
+            {
+                $httpRequest .= "?";
+                foreach ($arrayParam as $param => $value)
+                {
+                    $httpRequest .= $param."=".urlencode($value)."&";
+                }
+            }
+        }
+
+        curl_setopt($this->ch, CURLOPT_URL, $httpRequest);
+        curl_setopt($this->ch, CURLOPT_HEADER, true);
+        curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($this->ch, CURLOPT_USERPWD, "Administrator:admin");
+        //Uncomment for fiddler tracing
+        //curl_setopt($this->ch, CURLOPT_PROXY, '127.0.0.1:8888');
+        
+        //curl_setopt($this->ch, CURLOPT_COOKIEFILE, $this->cookieJar);
+        //curl_setopt($this->ch, CURLOPT_COOKIEJAR, $this->cookieJar);
+
+        //Execute
+        $response = curl_exec($this->ch);
+
+        if (curl_errno($this->ch))
+        {
+            $response = "Curl error: ".curl_error($this->ch)." Code: ".curl_errno($this->ch);
+            $result = new Result($response, "text/plain", 559);
+        }
+        else if (self::GetHttpStatusCode()==559)
+        {
+            $response = self::GetHttpErrorMessage($response);
+            $result = new Result($response, "text/plain", 559);
+        }
+        //If there was an 100 response we need to remove 2 headers
+        else if (strpos($response, "100 Continue"))
+        {
+            $response = self::RemoveHeader($response);
+            $response = self::RemoveHeader($response);
+            $result = new Result($response, self::GetContentType(), self::GetHttpStatusCode());
+        }
+        else
+        {
+            $response = self::RemoveHeader($response);
+            $result = new Result($response, self::GetContentType(), self::GetHttpStatusCode());
+        }
+
+        return $result;
+    }
+
+    function GetContentType()
+    {
+        return curl_getinfo($this->ch, CURLINFO_CONTENT_TYPE);
+    }
+
+    function GetHttpStatusCode()
+    {
+        return curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
+    }
+
+    //Find the separator between the header and the body and remove the header
+    function RemoveHeader($response)
+    {
+        if (strpos($response, "\r\n\r\n"))
+        {
+            $response = substr($response, strpos($response, "\r\n\r\n")+4);
+        }
+        else
+        {
+            $response = "";
+        }
+        return $response;
+    }
+
+    //Extract the error message after the HTTP status code
+    function GetHttpErrorMessage($response)
+    {
+        $start = strpos($response, "HTTP/1.1 559 ")+strlen("HTTP/1.1 559 ");
+        $end   = strpos($response, "Exception\r\n")+strlen("Exception\r\n");
+        return substr($response, $start, $end - $start);
+    }
+
+    function GetCookie()
+    {
+        $cookie = "";
+        if (file_exists($this->cookieJar))
+        {
+            $cookies = file_get_contents($this->cookieJar);
+            if (strstr($cookies, "MGSESSION") != false)
+            {
+                //TODO: Determine file format and parse out the cookie
+                $cookie = $cookies;
+            }
+        }
+        return $cookie;
+    }
+}
+
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HttpUnitTests.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HttpUnitTests.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/HttpUnitTests.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,55 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+//This script generates the HTML form for Api tests
+
+require_once("HtmlPrinter.php");
+
+if (!defined('HttpUnitTests_MAIN_METHOD'))
+{
+
+    define('HttpUnitTests_MAIN_METHOD', 'HttpUnitTests::main');
+    $test = new HttpUnitTests();
+    $test->main();
+}
+
+class HttpUnitTests
+{
+    public function main()
+    {
+        HtmlPrinter::PrintHtmlHeader("Http Tests");
+        HtmlPrinter::PrintFormHeader("Run.php");
+
+        //Add your tests here
+        //HtmlPrinter::PrintTestCases("YourTestName", "Path to database file where your data is stored", "Api")
+        HtmlPrinter::PrintTestCases("ResourceService", "../../TestData/ResourceService/ResourceServiceTest.dump", "Http");
+        HtmlPrinter::PrintTestCases("DrawingService", "../../TestData/DrawingService/DrawingServiceTest.dump", "Http");
+        HtmlPrinter::PrintTestCases("FeatureService", "../../TestData/FeatureService/FeatureServiceTest.dump", "Http");
+        HtmlPrinter::PrintTestCases("MappingService", "../../TestData/MappingService/MappingServiceTest.dump", "Http");
+        HtmlPrinter::PrintTestCases("SiteServiceTests", "../../TestData/SiteService/SiteServiceTest.dump", "Http");
+        HtmlPrinter::PrintTestCases("WfsTests", "../../TestData/Wfs/WfsTest.dump", "Http");
+        HtmlPrinter::PrintTestCases("WmsTests", "../../TestData/Wms/WmsTest.dump", "Http");
+
+        HtmlPrinter::PrintSelectTestMode();
+        HtmlPrinter::PrintSelectOutput();
+        HtmlPrinter::PrintFormFooter("Http", "RunTests");
+        HtmlPrinter::PrintHtmlFooter();
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/LocaleTest.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/LocaleTest.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/LocaleTest.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,62 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+// get current path
+$curPath = __FILE__;
+$pos = strrpos($curPath, '\\');
+if ($pos == false)
+{
+    $pos = strrpos($curPath, '/');
+}
+$curPath = substr_replace($curPath, "", $pos+1);
+
+//Note: current path might already be the script!!
+MgInitializeWebTier(WEBCONFIGINI);
+
+$locale = $_GET['LOCALE'];
+
+try
+{
+$site = new MgSiteConnection();
+$cred = new MgUserInformation();
+$cred->SetMgUsernamePassword("Administrator","admin");
+$cred->SetLocale($locale);
+$site->Open($cred);
+$svc = $site->CreateService(5);
+}
+catch (MgException $exc)
+{
+    header("HTTP/1.1 559".$exc->GetExceptionMessage());
+    $hdr = "Status: 559 ".$exc->GetExceptionMessage();
+    header($hdr);
+    echo "<html>\n";
+    echo "<body>\n";
+    echo $hdr."\n";
+    echo $exc->GetExceptionMessage()."\n";
+    echo $exc->GetDetails()."\n";
+    echo "</body>\n";
+    echo "</html>\n";
+    return;
+}
+echo "<html>\n";
+echo "<body>\n";
+echo "Done.\n";
+echo "</body>\n";
+echo "</html>\n";
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MapAgentShim/index.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MapAgentShim/index.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MapAgentShim/index.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,293 @@
+<?php
+
+/*
+ index.php
+ 
+ This is a php-based shim to the mapagent. It uses the MgHttpRequest/MgHttpResponse APIs to replicate
+ most of the functionality of the mapagent. The purpose of this script is to provide a simple mapagent
+ http endpoint (in conjunction with PHP's built-in web server) in which to run the PHP-based mapagent
+ test suite against
+ 
+ Usage:
+ 
+ php <various options> -S servername:port -t path/to/this/file.php
+ 
+ The mapagent can then be accessed via:
+ 
+ http://servername:port/mapguide/mapagent/mapagent.fcgi
+
+ */
+
+require_once dirname(__FILE__)."/../../../../Web/src/mapadmin/constants.php";
+
+function EscapeXmlChars($str) {
+    $newStr = "";
+    $len = strlen($str);
+
+    for($i = 0; $i < $len; $i++)
+    {
+        switch($str[$i])
+        {
+            case '&' :
+            {
+                $newStr .= "&";
+                break;
+            }
+            case '\'' :
+            {
+                $newStr .= "'";
+                break;
+            }
+            case '>' :
+            {
+                $newStr .= ">";
+                break;
+            }
+            case '<' :
+            {
+                $newStr .= "<";
+                break;
+            }
+            case '"' :
+            {
+                $newStr .= """;
+                break;
+            }
+            default :
+                $newStr .= $str[$i];
+        }
+    }
+    return $newStr;
+}
+
+function Unauthorized() {
+    //Send back 401
+    header('WWW-Authenticate: Basic realm="MapGuide"');
+    $e = new Exception();
+    echo "You must enter a valid login ID and password to access this site\r\n";
+    http_response_code(401);
+    die;
+}
+
+function CheckParams($param) {
+    $bValid = $param->ContainsParameter("SESSION");
+    if (!$bValid)
+        $bValid = $param->ContainsParameter("USERNAME");
+    
+    $op = $param->GetParameterValue("OPERATION");
+    if ($op == "GETSITESTATUS")
+        $bValid = true;
+
+    //Invalid service parameter needs to trigger unauthorized
+    if ($bValid) {
+        if ($param->ContainsParameter("SERVICE")) {
+            $svc = $param->GetParameterValue("SERVICE");
+            $bValid = ($svc == "WFS" || $svc == "WMS");
+        }
+    }
+
+    if (!$bValid)
+        Unauthorized();
+}
+
+function OutputException($statusMessage, $errorMessage, $details, $phpTrace, $status = 500, $mimeType = MgMimeType::Html) {
+    $errResponse = "";
+    $errResponse = sprintf(
+            "\r\n<html>\n<head>\n<title>%s</title>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n</head>\n<body>\n<h2>%s</h2>\n%s\n</body>\n</html>\n",
+            $statusMessage,
+            $errorMessage,
+            $details,
+            $phpTrace);
+    header($_SERVER["SERVER_PROTOCOL"]." 559 $statusMessage"); 
+    header("Status: 559 $statusMessage");
+    header($statusMessage, true, 559);
+    echo $errResponse;
+}
+
+function OutputError($result, $mimeType = MgMimeType::Html) {
+    $statusMessage = $result->GetHttpStatusMessage();
+    $e = new Exception();
+    if ($statusMessage === "MgAuthenticationFailedException" || $statusMessage === "MgUnauthorizedAccessException" || $statusMessage == "MgPermissionDeniedException") {
+        Unauthorized();
+    } else {
+        header("Content-Type: $mimeType");
+        OutputException($statusMessage, $result->GetErrorMessage(), $result->GetDetailedErrorMessage(), $e->getTraceAsString(), $result->GetStatusCode(), $mimeType);
+    }
+}
+
+function OutputMgStringCollection($strCol, $mimeType = MgMimeType::Xml) {
+    $content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><StringCollection />";
+    if ($strCol != null) {
+        // MgStringCollection::ToXml() doesn't seem to be reliable in PHP (bug?), so do this manually
+        $count = $strCol->GetCount();
+        $content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><StringCollection>";
+        for ($i = 0; $i < $count; $i++) {
+            $value = EscapeXmlChars($strCol->GetItem($i));
+            $content .= "<Item>$value</Item>";
+        }
+        $content .= "</StringCollection>";
+    }
+    header("Content-Type: $mimeType");
+    echo $content;
+}
+
+function OutputByteReader($byteReader, $chunkResult = false, $bPrependXmlProlog = false) {
+    $mimeType = $byteReader->GetMimeType();
+    header("Content-Type: $mimeType");
+    $rdrLen = $byteReader->GetLength();
+    if ($mimeType == MgMimeType::Xml && $bPrependXmlProlog) {
+        echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
+    }
+    do
+    {
+        $data = str_pad("\0", 50000, "\0");
+        $len = $byteReader->Read($data, 50000);
+        if ($len > 0)
+        {
+            $str = substr($data, 0, $len);
+            echo $str;
+        }
+    } while ($len > 0);
+}
+
+function TryFillCredentials($param) {
+    if (array_key_exists("SESSION", $_REQUEST)) {
+        $param->AddParameter("SESSION", $_REQUEST["SESSION"]);
+    } else {
+        $username = null;
+        $password = "";
+        // Username/password extraction logic ripped from PHP implementation of the MapGuide AJAX viewer
+        
+        // No session, no credentials explicitely passed. Check for HTTP Auth user/passwd.  Under Apache CGI, the
+        // PHP_AUTH_USER and PHP_AUTH_PW are not set.  However, the Apache admin may
+        // have rewritten the authentication information to REMOTE_USER.  This is a
+        // suggested approach from the Php.net website.
+
+        // Has REMOTE_USER been rewritten?
+        if (!isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['REMOTE_USER']) &&
+        preg_match('/Basic +(.*)$/i', $_SERVER['REMOTE_USER'], $matches))
+        {
+            list($name, $password) = explode(':', base64_decode($matches[1]));
+            $_SERVER['PHP_AUTH_USER'] = strip_tags($name);
+            $_SERVER['PHP_AUTH_PW']    = strip_tags($password);
+        }
+
+
+        // REMOTE_USER may also appear as REDIRECT_REMOTE_USER depending on CGI setup.
+        //  Check for this as well.
+        if (!isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['REDIRECT_REMOTE_USER']) &&
+        preg_match('/Basic (.*)$/i', $_SERVER['REDIRECT_REMOTE_USER'], $matches))
+        {
+            list($name, $password) = explode(':', base64_decode($matches[1]));
+            $_SERVER['PHP_AUTH_USER'] = strip_tags($name);
+            $_SERVER['PHP_AUTH_PW'] = strip_tags($password);
+        }
+
+        // Finally, PHP_AUTH_USER may actually be defined correctly.  If it is set, or
+        // has been pulled from REMOTE_USER rewriting then set our USERNAME and PASSWORD
+        // parameters.
+        if (isset($_SERVER['PHP_AUTH_USER']) && strlen($_SERVER['PHP_AUTH_USER']) > 0)
+        {
+            $username = $_SERVER['PHP_AUTH_USER'];
+            if (isset($_SERVER['PHP_AUTH_PW']) && strlen($_SERVER['PHP_AUTH_PW']) > 0)
+                $password = $_SERVER['PHP_AUTH_PW'];
+        }
+
+        //If we have everything we need, put it into the MgHttpRequestParam
+        if ($username != null) {
+            $param->AddParameter("USERNAME", $username);
+            if ($password !== "") {
+                $param->AddParameter("PASSWORD", $password);
+            }
+        } else {
+            Unauthorized();
+        }
+    }
+}
+
+$iniFile = dirname(__FILE__)."/../../../webconfig.ini";
+try {
+    if (array_key_exists("WEBCONFIGINI", $_SERVER)) {
+        $iniFile = $_SERVER["WEBCONFIGINI"];
+    }
+    if (array_key_exists("WEBCONFIGINI", $_ENV)) {
+        $iniFile = $_ENV["WEBCONFIGINI"];
+    }
+    MgInitializeWebTier($iniFile);
+    $method = $_SERVER["REQUEST_METHOD"];
+    if (strpos($_SERVER["REQUEST_URI"], '/mapguide/mapagent/mapagent.fcgi') !== FALSE) {
+        if ($method == "GET" || $method == "POST") {
+            $uri = "http://".$_SERVER["SERVER_NAME"].$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
+            $req = new MgHttpRequest($uri);
+            $param = $req->GetRequestParam();
+            
+            TryFillCredentials($param);
+            
+            if ($method == "GET") {
+                foreach ($_GET as $key => $value) {
+                    $param->AddParameter($key, $value);
+                }
+            } else { //POST
+                foreach ($_POST as $key => $value) {
+                    $param->AddParameter($key, $value);
+                }
+                foreach ($_FILES as $key => $value) {
+                    if (array_key_exists("error", $value) && $value["error"] == 1) {
+                        echo "Error: The file ".$value["name"]." for field $key exceeds the maximum file upload size of ".ini_get("upload_max_filesize");
+                        die;
+                    }
+                    $param->AddParameter($key, $value["tmp_name"]);
+                    $param->SetParameterType($key, "tempfile");
+                }
+            }
+            
+            CheckParams($param);
+            
+            $response = $req->Execute();
+            $result = $response->GetResult();
+
+            $status = $result->GetStatusCode();
+            if ($status == 200) {
+                $resultObj = $result->GetResultObject();
+                if ($resultObj != null) {
+                    header("Content-Type: ".$result->GetResultContentType());
+                    if ($resultObj instanceof MgByteReader) {
+                        OutputByteReader($resultObj);
+                    } else if ($resultObj instanceof MgStringCollection) {
+                        //OutputMgStringCollection($resultObj, $param->GetParameterValue("FORMAT"));
+                        $byteReader = $resultObj->ToXml();
+                        OutputByteReader($byteReader);
+                    } else if ($resultObj instanceof MgHttpPrimitiveValue) {
+                        echo $resultObj->ToString();
+                    } else if (method_exists($resultObj, "ToXml")) {
+                        $byteReader = $resultObj->ToXml();
+                        OutputByteReader($byteReader);
+                    } else {
+                        throw new Exception("Could not determine how to output: ".$resultObj->ToString()); //TODO: Localize
+                    }
+                }
+            } else {
+                $format = $param->GetParameterValue("FORMAT");
+                if ($format != "") {
+                    OutputError($result, $format);
+                } else {
+                    OutputError($result);
+                }
+                //throw new Exception("Error executing operation: ".$param->GetParameterValue("OPERATION").". The status code is: $status"); //TODO: Localize
+            }
+            http_response_code($status);
+            
+        } else {
+            echo "Unsupported method";
+            http_response_code(405);
+        }
+    } else {
+        return false;
+    }
+}
+catch (MgException $ex) {
+    OutputException(get_class($ex), $ex->GetExceptionMessage(), $ex->GetDetails(), $e->getTraceAsString(), 559);
+    die;
+}
+
+?>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MapLayerAPI.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MapLayerAPI.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MapLayerAPI.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,510 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("master.php");
+require_once("Result.php");
+require_once("Utils.php");
+
+class MapLayerAPI
+{
+    private $cred;
+    private $resSrvc;
+    private $unitTestParamVm;
+    private $arrayParam;
+    private $site;
+    private $map;
+
+    function __construct($db)
+    {
+        $map = null;
+
+        Utils::MapAgentInit(WEBCONFIGINI);
+
+        $this->cred = new MgUserInformation();
+        $this->cred->SetMgUsernamePassword("Administrator","admin");
+        $this->cred->SetLocale("en");
+
+        $this->site = new MgSiteConnection();
+        $this->site->Open($this->cred);
+        $this->resSrvc = $this->site->CreateService(MgServiceType::ResourceService);
+
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->arrayParam = array();
+    }
+
+    function __destruct()
+    {
+        unset($this->map);
+        unset($this->resSrvc);
+        unset($this->site);
+        unset($this->cred);
+        unset($this->unitTestParamVm);
+        unset($this->arrayParam);
+    }
+
+    function GetMapExtent($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $extent = $this->map->GetMapExtent();
+            return new Result(Utils::MgEnvelopeToString($extent), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetCoordinateSystem($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            return new Result($this->map->GetMapSRS(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetName($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            return new Result($this->map->GetName(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetLayers($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $layers = $this->map->GetLayers();
+            $layerNames = "";
+            for($i = 0; $i < $layers->GetCount(); $i ++)
+            {
+                if($i > 0)
+                    $layerNames .= "/";
+                $layerNames .= $layers->GetItem($i)->GetName();
+            }
+            return new Result($layerNames, "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetLayerGroups($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $groups = $this->map->GetLayerGroups();
+            $groupNames = "";
+            for($i = 0; $i < $groups->GetCount(); $i ++)
+            {
+                if($i > 0)
+                    $groupNames .= "/";
+                $groupNames .= $groups->GetItem($i)->GetName();
+            }
+            return new Result($groupNames, "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetLayerVisibility($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LAYERNAME\"");
+            $layerName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $layer = $this->FindLayer($layerName);
+            if($layer == null)
+                return new Result("***Unknown Layer***", "text/plain");
+            return new Result(Utils::BooleanToString($layer->GetVisible()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetDisplayInLegend($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LAYERNAME\"");
+            $layerName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $layer = $this->FindLayer($layerName);
+            if($layer == null)
+                return new Result("***Unknown Layer***", "text/plain");
+            return new Result(Utils::BooleanToString($layer->GetDisplayInLegend()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetLegendLabel($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LAYERNAME\"");
+            $layerName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $layer = $this->FindLayer($layerName);
+            if($layer == null)
+                return new Result("***Unknown Layer***", "text/plain");
+            return new Result($layer->GetLegendLabel(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetLayerDefinition($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LAYERNAME\"");
+            $layerName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $layer = $this->FindLayer($layerName);
+            if($layer == null)
+                return new Result("***Unknown Layer***", "text/plain");
+            return new Result($layer->GetLayerDefinition()->ToString(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function IsLayerVisible($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LAYERNAME\"");
+            $layerName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $layer = $this->FindLayer($layerName);
+            if($layer == null)
+                return new Result("***Unknown Layer***", "text/plain");
+            return new Result(Utils::BooleanToString($layer->IsVisible()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function AddGroup($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUPNAME\"");
+            $groupName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PARENTGROUPNAME\"");
+            $parentGroupName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $groups = $this->map->GetLayerGroups();
+            $group = new MgLayerGroup($groupName);
+            if($parentGroupName != "")
+            {
+                $parentGroup = $group->SetGroup($groups->GetItem($parentGroupName));
+                $group->SetGroup($parentGroup);
+            }
+            $groups->Add($group);
+
+            return new Result($groups->GetCount(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function AddLayer($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LAYERDEFINITION\"");
+            $layerDef = $this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUPNAME\"");
+            $groupName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $layer = new MgLayer(new MgResourceIdentifier($layerDef), $this->resSrvc);
+            if($groupName != "")
+            {
+                $groups = $this->map->GetLayerGroups();
+                $layer->SetGroup($groups->GetItem($groupName));
+            }
+
+            $layers = $this->map->GetLayers();
+            $layers->Add($layer);
+
+            return new Result($layers->GetCount(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function HideGroup($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUPNAME\"");
+            $groupName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $groups = $this->map->GetLayerGroups();
+            $group = $groups->GetItem($groupName);
+
+            $group->SetVisible(false);
+
+            return new Result(Utils::BooleanToString($group->GetVisible()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function ShowGroup($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUPNAME\"");
+            $groupName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $groups = $this->map->GetLayerGroups();
+            $group = $groups->GetItem($groupName);
+
+            $group->SetVisible(true);
+
+            return new Result(Utils::BooleanToString($group->GetVisible()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetViewCenter($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            return new Result(Utils::MgPointToString($this->map->GetViewCenter()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetViewScale($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            return new Result($this->map->GetViewScale(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetDataExtent($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $extent = $this->map->GetDataExtent();
+            return new Result(Utils::MgEnvelopeToString($extent), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function RemoveGroup($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUPNAME\"");
+            $groupName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $groups = $this->map->GetLayerGroups();
+            $group = $groups->GetItem($groupName);
+
+            $groups->Remove($group);
+
+            return new Result("", "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function LayerExists($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LAYERNAME\"");
+            $layerName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $exists = false;
+            $layers = $this->map->GetLayers();
+            for($i = 0; $i < $layers->GetCount(); $i++)
+            {
+                $layer = $layers->GetItem($i);
+                if($layer->GetName() == $layerName)
+                {
+                    $exists = true;
+                    break;
+                }
+            }
+
+            return new Result(Utils::BooleanToString($exists, "text/plain"));
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetFeatureSourceId($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LAYERNAME\"");
+            $layerName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $layer = $this->FindLayer($layerName);
+            if($layer == null)
+                return new Result("***Unknown Layer***", "text/plain");
+            return new Result($layer->GetFeatureSourceId(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetFeatureClassName($paramSet)
+    {
+        try
+        {
+            $this->CreateMapFromResource($paramSet);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LAYERNAME\"");
+            $layerName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $layer = $this->FindLayer($layerName);
+            return new Result($layer->GetFeatureClassName(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function FindLayer($name)
+    {
+        $layers = $this->map->GetLayers();
+        for($i = 0; $i < $layers->GetCount(); $i++)
+        {
+            $layer = $layers->GetItem($i);
+            if($layer->GetName() == $name)
+                return $layer;
+        }
+        return null;
+    }
+
+    function CreateMapFromResource($paramSet)
+    {
+        if($this->map != null)
+            return;
+
+        $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"MAPDEFINITION\"");
+        $mapName = $this->unitTestParamVm->GetString("ParamValue");
+        if ($mapName == null || strlen($mapName) < 1)
+        {
+            $mapName = "Library://maplayertest/World.MapDefinition";
+        }
+
+        $mapDefId = new MgResourceIdentifier($mapName);
+        $this->map = new MgMap($this->site);
+        $this->map->Create($mapDefId, $mapDefId->GetName());
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MappingServiceAPI.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MappingServiceAPI.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MappingServiceAPI.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,85 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("master.php");
+require_once("Result.php");
+require_once("Utils.php");
+
+//This file defines the operations that are currently supported for Mapping Service API by the unit test infrastructure.
+//There is one function per operation. Each function tries to retrieve the parameters that are required for its corresponding
+//operation and execute the operation
+//Edit that file to add more operations for Drawing Service API
+
+class MappingServiceAPI
+{
+    private $unitTestParamVm;
+    private $cred;
+    private $site;
+    private $arrayParam;
+    private $mappingSrvc;
+    private $mimeType;
+
+    function __construct($db)
+    {
+        $this->mimeType="text/xml";
+        Utils::MapAgentInit(WEBCONFIGINI);
+
+        $this->cred = new MgUserInformation();
+        $this->cred->SetMgUsernamePassword("Administrator","admin");
+        $this->cred->SetLocale("en");
+
+        $this->site = new MgSiteConnection();
+        $this->site->Open($this->cred);
+        $this->mappingSrvc = $this->site->CreateService(MgServiceType::MappingService);
+
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->arrayParam = array();
+    }
+
+    function __destruct()
+    {
+        unset($this->site);
+        unset($this->cred);
+        unset($this->unitTestParamVm);
+        unset($this->arrayParam);
+        unset($this->mappingSrvc);
+    }
+
+    function GetParam($arrayParam, $paramSet, $paramName)
+    {
+        $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=$paramName");
+        $paramValue = $this->unitTestParamVm->GetString("ParamValue");
+        if (isset($paramValue))
+        {
+            $arrayParam[$paramName] = $paramValue;
+        }
+    }
+
+    //TODO: Not completed yet. Converts $xmlString into a StringCollection object
+    function ToMgStringCollection($xmlString)
+    {
+        $stringCollection = new MgStringCollection();
+        while ( (substr_count($xmlString, "<item>")) > 0)
+        {
+            $start = strpos($xmlString, "<item>");
+            $end = strpos($xmlString, "</item>");
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MappingServiceHttpRequests.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MappingServiceHttpRequests.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/MappingServiceHttpRequests.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,98 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("master.php");
+require_once("HttpRequest.php");
+require_once("Utils.php");
+
+//This file defines the operations that are currently supported by the unit test infrastructure for Drawing Service methods exposed through Htpp.
+//There is one function per operation. Each function tries to retrieve the parameters that are required for its corresponding
+//operation and send the Http request
+//Edit that file to add more operations from Drawing Service that are exposed through Http
+
+class MappingServiceHttpRequests
+{
+    private $db;
+    private $unitTestParamVm;
+    private $URL;
+
+    function __construct($db, $newURL)
+    {
+        $this->db = $db;
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->httpRequest = new HttpRequest();
+        $this->URL = $newURL;
+
+        // get rid of the cookie jar...
+        if (file_exists($this->httpRequest->cookieJar))
+        {
+            unlink($this->httpRequest->cookieJar);
+        }
+    }
+
+    function __destruct()
+    {
+        unset($this->unitTestParamVm);
+        unset($this->httpRequest);
+        unset($this->db);
+    }
+
+    function GetParam(&$arrayParam, $paramSet, $paramName)
+    {
+        $select = "Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"$paramName\"";
+        $this->unitTestParamVm->Execute($select);
+        $paramValue = $this->unitTestParamVm->GetString("ParamValue");
+        if (isset($paramValue) && strlen($paramValue) > 0)
+        {
+            $arrayParam[$paramName] = $paramValue;
+        }
+    }
+
+    function GetMap($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->GetParam($arrayParam, $paramSet, "MAPDEFINITION");
+            $this->GetParam($arrayParam, $paramSet, "DWFVERSION");
+            $this->GetParam($arrayParam, $paramSet, "EMAPVERSION");
+            $this->GetParam($arrayParam, $paramSet, "SESSION");
+
+            $result = $this->httpRequest->SendRequest($this->URL, $arrayParam);
+
+            if ($result->GetHttpStatusCode() != 559)
+            {
+                //$cookie = $this->httpRequest->GetCookie();
+                //if (strlen($cookie) <= 0)
+                //{
+                //    $result = new Result("No cookie created in GetMap", "text/plain", 559);
+                //}
+            }
+
+            return $result;
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage().$this->unitTestParamVm->GetErrMsg(), "text/plain");
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Menu.html
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Menu.html	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Menu.html	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<ul>
+<li><a href="ApiUnitTests.php" target="showframe">Api Unit Tests</a></li>
+<li><a href="HttpUnitTests.php" target="showframe">Http Unit Tests</a></li>
+</ul>
+</html>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/NewSdf.MapDefinition
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/NewSdf.MapDefinition	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/NewSdf.MapDefinition	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+  <MapDefinition version="1.0.0">
+    <Name>New Map</Name>
+    <CoordinateSystem>LOCALCS["Non-Earth (Meter)",LOCAL_DATUM["Local Datum",0],UNIT["Meter", 1],AXIS["X",EAST],AXIS["Y",NORTH]]</CoordinateSystem>
+    <Extents>
+      <MinX>0</MinX>
+      <MaxX>0</MaxX>
+      <MinY>0</MinY>
+      <MaxY>0</MaxY>
+    </Extents>
+    <BackgroundColor>FFFFFFFF</BackgroundColor>
+    <Metadata><MapDescription>Test map</MapDescription></Metadata>
+    <MapLayer>
+      <Name>NewSdfInt</Name>
+      <ResourceId>Library://TrevorWekel/NewSdfInt.LayerDefinition</ResourceId>
+      <Group></Group>
+      <Visible>true</Visible>
+      <Selectable>true</Selectable>
+      <ShowInLegend>true</ShowInLegend>
+      <LegendLabel>NewSdfInt</LegendLabel>
+      <ExpandInLegend>true</ExpandInLegend>
+    </MapLayer>
+  </MapDefinition>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/NewSdfInt.LayerDefinition
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/NewSdfInt.LayerDefinition	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/NewSdfInt.LayerDefinition	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LayerDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="LayerDefinition-1.0.0.xsd" version="1.0.0">
+  <VectorLayerDefinition>
+    <ResourceId>Library://TrevorWekel/NewSdf.FeatureSource</ResourceId>
+    <FeatureName>IntKey</FeatureName>
+    <FeatureNameType>FeatureClass</FeatureNameType>
+    <Geometry>GEOM</Geometry>
+    <VectorScaleRange>
+      <AreaTypeStyle>
+        <AreaRule>
+          <LegendLabel></LegendLabel>
+          <AreaSymbolization2D>
+            <Fill>
+              <FillPattern>Solid</FillPattern>
+              <ForegroundColor>ffffffff</ForegroundColor>
+              <BackgroundColor>ff000000</BackgroundColor>
+            </Fill>
+            <Stroke>
+              <LineStyle>Solid</LineStyle>
+              <Thickness>0</Thickness>
+              <Color>ff000000</Color>
+              <Unit>Inches</Unit>
+            </Stroke>
+          </AreaSymbolization2D>
+        </AreaRule>
+      </AreaTypeStyle>
+    </VectorScaleRange>
+  </VectorLayerDefinition>
+</LayerDefinition>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/NewSdfString.LayerDefinition
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/NewSdfString.LayerDefinition	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/NewSdfString.LayerDefinition	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LayerDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="LayerDefinition-1.0.0.xsd" version="1.0.0">
+  <VectorLayerDefinition>
+    <ResourceId>Library://TrevorWekel/NewSdf.FeatureSource</ResourceId>
+    <FeatureName>StringKey</FeatureName>
+    <FeatureNameType>FeatureClass</FeatureNameType>
+    <Geometry>GEOM</Geometry>
+    <VectorScaleRange>
+      <AreaTypeStyle>
+        <AreaRule>
+          <LegendLabel></LegendLabel>
+          <AreaSymbolization2D>
+            <Fill>
+              <FillPattern>Solid</FillPattern>
+              <ForegroundColor>ffffffff</ForegroundColor>
+              <BackgroundColor>ff000000</BackgroundColor>
+            </Fill>
+            <Stroke>
+              <LineStyle>Solid</LineStyle>
+              <Thickness>0</Thickness>
+              <Color>ff000000</Color>
+              <Unit>Inches</Unit>
+            </Stroke>
+          </AreaSymbolization2D>
+        </AreaRule>
+      </AreaTypeStyle>
+    </VectorScaleRange>
+  </VectorLayerDefinition>
+</LayerDefinition>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/PhpUnitTests.zpj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/PhpUnitTests.zpj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/PhpUnitTests.zpj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,72 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<project>
+    <project_entity ID="debugger">
+        <connectionInfo use_defaults="true" tempFilesLocation="C:\Documents and Settings\wekeltr" debug_mode="local" serverHost="http://localhost/php/phptest.php" port="10000"/>
+        <breakpoints/>
+        <watches/>
+    </project_entity>
+    <project_entity ID="project_roots">
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\ApiUnitTests.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\DrawingServiceAPI.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\DrawingServiceHttpRequests.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\ExecuteOperation.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\FeatureServiceAPI.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\FeatureServiceHttpRequests.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\HtmlPrinter.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\HttpRequest.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\HttpUnitTests.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\index.html"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\LocaleTest.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\MapLayerAPI.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\MappingServiceAPI.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\MappingServiceHttpRequests.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\Menu.html"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\ResourceServiceAPI.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\ResourceServiceHttpRequests.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\Result.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\Run.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\RunTests.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\ServerAdminAPI.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\setResourceData.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\SiteServiceAPI.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\SiteServiceHttpRequests.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\utf8test.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\Utils.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\Validate.php"/>
+        <vfile name="C:\MgDev\UnitTest\WebTier\Php\ValidateUtils.php"/>
+    </project_entity>
+    <project_entity ID="bookmarks"/>
+    <project_entity ID="editor_component" selectedEditor="2">
+        <openEditors>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\utf8test.php" caretPosition="152" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\ResourceServiceHttpRequests.php" caretPosition="2470" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\HttpRequest.php" caretPosition="0" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\RunTests.php" caretPosition="319" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\Run.php" caretPosition="5365" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\ApiUnitTests.php" caretPosition="299" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\HttpUnitTests.php" caretPosition="306" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\Web\src\PHPMapAgent\helper.php" caretPosition="0" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\Validate.php" caretPosition="0" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\HtmlPrinter.php" caretPosition="0" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\Utils.php" caretPosition="6677" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\DrawingServiceAPI.php" caretPosition="786" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\Web\src\PHPMapAgent\MapAgent.php" caretPosition="0" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\MappingServiceAPI.php" caretPosition="0" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\MapLayerAPI.php" caretPosition="12330" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\Web\src\MapGuidePhpApi\MgConstants.php" caretPosition="846" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\FeatureServiceAPI.php" caretPosition="0" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\SiteServiceAPI.php" caretPosition="2296" clone="false" encoding="UTF-8"/>
+            <editor fileName="C:\MgDev\UnitTest\WebTier\Php\ServerAdminAPI.php" caretPosition="1464" clone="false" encoding="UTF-8"/>
+        </openEditors>
+    </project_entity>
+    <project_entity ID="CVS">
+        <lastArguments>
+            <CVSCommand commandType="update"/>
+            <CVSCommand commandType="commit"/>
+            <CVSCommand commandType="add"/>
+            <CVSCommand commandType="status"/>
+            <CVSCommand commandType="diff"/>
+        </lastArguments>
+    </project_entity>
+</project>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ResourceServiceAPI.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ResourceServiceAPI.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ResourceServiceAPI.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,873 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+//This class defines the operations that are currently supported by the unit test infrastructure for Resource Service API .
+//There is one function per operation. Each function tries to retrieve the parameters that are required for its corresponding
+//operation and executes the operation
+//Edit that file to add more operations for Resource Service API
+
+require_once("master.php");
+require_once("Utils.php");
+require_once("Result.php");
+
+class ResourceServiceAPI
+{
+    private $unitTestParamVm;
+    private $cred;
+    private $site;
+    private $resourceSrvc;
+    private $mimeType;
+
+    function __construct($db)
+    {
+        $this->mimeType="text/xml";
+        Utils::MapAgentInit(WEBCONFIGINI);
+
+        $this->cred = new MgUserInformation();
+        $this->cred->SetMgUsernamePassword("Administrator","admin");
+        $this->cred->SetLocale("en");
+
+        $this->site = new MgSiteConnection();
+        $this->site->Open($this->cred);
+        $this->resourceSrvc = $this->site->CreateService(MgServiceType::ResourceService);
+
+        $this->unitTestParamVm = new SqliteVM($db, true);
+    }
+
+    function __destruct()
+    {
+        unset($this->site);
+        unset($this->cred);
+        unset($this->unitTestParamVm);
+    }
+
+    function EnumerateResources ($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"TYPE\"");
+            $arrayParam["TYPE"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DEPTH\"");
+            $arrayParam["DEPTH"]=$this->unitTestParamVm->GetString("ParamValue");
+            if (is_numeric($arrayParam["DEPTH"]))
+            {
+                $arrayParam["DEPTH"] = intval($arrayParam["DEPTH"]);
+            }
+            else
+            {
+                $arrayParam["DEPTH"] = -1;
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->resourceSrvc->EnumerateResources( $repId, $arrayParam["DEPTH"], $arrayParam["TYPE"]);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function SetResource($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+            $contentReader=null;
+            $headerReader=null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CONTENT\"");
+            if ($this->unitTestParamVm->GetString("ParamValue") != "")
+            {
+                $arrayParam['CONTENT']=$this->unitTestParamVm->GetString("ParamValue");
+                $arrayParam['CONTENT']=Utils::GetPath($arrayParam['CONTENT']);
+                if (file_exists($arrayParam['CONTENT']))
+                {
+                    //TODO add mime type
+                    $contentSource = new MgByteSource($arrayParam['CONTENT']);
+                    $contentReader = $contentSource->GetReader();
+                }
+            }
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"HEADER\"");
+            if ($this->unitTestParamVm->GetString("ParamValue") != "")
+            {
+                $arrayParam['HEADER']=$this->unitTestParamVm->GetString("ParamValue");
+                $arrayParam['HEADER']=Utils::GetPath($arrayParam['HEADER']);
+                if (file_exists($arrayParam['HEADER']))
+                {
+                    $headerSource = new MgByteSource($arrayParam['HEADER']);
+                    $headerReader = $headerSource->GetReader();
+                }
+            }
+
+            $byteReader = $this->resourceSrvc->SetResource( $repId, $contentReader, $headerReader);
+
+            $result = Utils::GetResponse($byteReader);
+
+            return $result;
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function DeleteResource($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->resourceSrvc->DeleteResource($repId);
+
+            $result = Utils::GetResponse($byteReader);
+
+            return $result;
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetResourceContent($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PREPROCESS\"");
+            $arrayParam["PREPROCESS"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->resourceSrvc->GetResourceContent($repId);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetResourceHeader($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->resourceSrvc->GetResourceHeader($repId);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateResourceData($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->resourceSrvc->EnumerateResourceData($repId);
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+
+    }
+
+    function GetResourceData($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DATANAME\"");
+            $arrayParam["DATANAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->resourceSrvc->GetResourceData($repId, $arrayParam["DATANAME"]);
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function SetResourceData($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DATANAME\"");
+            $arrayParam["DATANAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DATATYPE\"");
+            $arrayParam["DATATYPE"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DATA\"");
+            $arrayParam["DATA"]=$this->unitTestParamVm->GetString("ParamValue");
+            $arrayParam["DATA"]=Utils::GetPath($arrayParam["DATA"]);
+
+            $extension = self::GetExtension($arrayParam["DATANAME"]);
+            $mimeType = self::GetMimeType($extension);
+            $dataSource = new MgByteSource($arrayParam["DATA"]);
+            $dataSource->SetMimeType($mimeType);
+            $dataReader = $dataSource->GetReader();
+
+            $byteReader = $this->resourceSrvc->SetResourceData($repId, $arrayParam["DATANAME"], $arrayParam["DATATYPE"], $dataReader);
+
+            $result = Utils::GetResponse($byteReader);
+
+            return $result;
+        }
+        catch (MgException $e)
+        {
+            //HACK/FIXME: This SQLite-based test specification doesn't support platform-specific
+            //expectations to my knowledge, because on some of the test cases for this operation
+            //it is being fed paths with improper case (which obviously has no effect on Windows
+            //due to their paths being case-insensitive, but on Linux it's a different matter)
+            //
+            //So the workaround for now, is to catch such situations and shape the result to what
+            //is expected on Windows: an empty result
+            if (($e instanceof MgFileNotFoundException) && !Utils::IsWindows()) {
+                return new Result("", "text/plain");
+            } else {
+                return new Result(get_class($e), "text/plain");
+            }
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function RenameResourceData($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"OLDDATANAME\"");
+            $arrayParam["OLDDATANAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NEWDATANAME\"");
+            $arrayParam["NEWDATANAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->resourceSrvc->RenameResourceData($repId, $arrayParam["OLDDATANAME"], $arrayParam["NEWDATANAME"], false);
+
+            $result = Utils::GetResponse($byteReader);
+
+            return $result;
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function DeleteResourceData($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DATANAME\"");
+            $arrayParam["DATANAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->resourceSrvc->DeleteResourceData($repId, $arrayParam["DATANAME"]);
+
+            $result = Utils::GetResponse($byteReader);
+
+            return $result;
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetRepositoryContent($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->resourceSrvc->GetRepositoryContent($repId);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetRepositoryHeader($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->resourceSrvc->GetRepositoryHeader($repId);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function UpdateRepository($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+            $contentReader = null;
+            $headerReader = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CONTENT\"");
+            if ($this->unitTestParamVm->GetString("ParamValue") != "")
+            {
+                $arrayParam['CONTENT']=$this->unitTestParamVm->GetString("ParamValue");
+                $arrayParam['CONTENT']=Utils::GetPath($arrayParam['CONTENT']);
+                if (file_exists($arrayParam['CONTENT']))
+                {
+                    //TODO add mime type
+                    $contentSource = new MgByteSource($arrayParam['CONTENT']);
+                    $contentReader = $contentSource->GetReader();
+                }
+            }
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"HEADER\"");
+            if ($this->unitTestParamVm->GetString("ParamValue") != "")
+            {
+                $arrayParam['HEADER']=$this->unitTestParamVm->GetString("ParamValue");
+                $arrayParam['HEADER']=Utils::GetPath($arrayParam['HEADER']);
+                if (file_exists($arrayParam['HEADER']))
+                {
+                    $headerSource = new MgByteSource($arrayParam['HEADER']);
+                    $headerReader = $headerSource->GetReader();
+                }
+            }
+            $byteReader = $this->resourceSrvc->UpdateRepository( $repId, $contentReader, $headerReader);
+
+            $result = Utils::GetResponse($byteReader);
+
+            return $result;
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateResourceReferences($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->resourceSrvc->EnumerateReferences( $repId );
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function MoveResource($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $sourceId = null;
+            $destId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SOURCE\""))
+            {
+                $arrayParam["SOURCE"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DESTINATION\""))
+            {
+                $arrayParam["DESTINATION"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("SOURCE", $arrayParam))
+            {
+                $sourceId = new MgResourceIdentifier($arrayParam["SOURCE"]);
+            }
+
+            if (array_key_exists("DESTINATION", $arrayParam))
+            {
+                $destId = new MgResourceIdentifier($arrayParam["DESTINATION"]);
+            }
+
+            $byteReader = $this->resourceSrvc->MoveResource($sourceId, $destId, false);
+
+            $result = Utils::GetResponse($byteReader);
+
+            return $result;
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function CopyResource($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $sourceId = null;
+            $destId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SOURCE\""))
+            {
+                $arrayParam["SOURCE"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DESTINATION\""))
+            {
+                $arrayParam["DESTINATION"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("SOURCE", $arrayParam))
+            {
+                $sourceId = new MgResourceIdentifier($arrayParam["SOURCE"]);
+            }
+
+            if (array_key_exists("DESTINATION", $arrayParam))
+            {
+                $destId = new MgResourceIdentifier($arrayParam["DESTINATION"]);
+            }
+
+            $byteReader = $this->resourceSrvc->CopyResource($sourceId, $destId, false);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function ChangeResourceOwner($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"OWNER\"");
+            $arrayParam["OWNER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"INCLUDEDESCENDANTS\"");
+            $arrayParam["INCLUDEDESCENDANTS"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->resourceSrvc->ChangeResourceOwner($repId, $arrayParam["OWNER"], $arrayParam["INCLUDEDESCENDANTS"]);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function InheritPermissionsFrom($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $repId = null;
+
+            if (SQLITE_ROW == $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\""))
+            {
+                $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+            }
+
+            if (array_key_exists("RESOURCEID", $arrayParam))
+            {
+                $repId = new MgResourceIdentifier($arrayParam["RESOURCEID"]);
+            }
+
+            $byteReader = $this->resourceSrvc->InheritPermissionsFrom($repId);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function ApplyResourcePackage($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $package = null;
+            $packageSource = null;
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PACKAGE\"");
+            $arrayParam["PACKAGE"]=Utils::GetPath($this->unitTestParamVm->GetString("ParamValue"));
+
+            if (Utils::IsWindows()) {
+                $packageSource = new MgByteSource($arrayParam["PACKAGE"]);
+                $package = $packageSource->GetReader();
+
+                $byteReader = $this->resourceSrvc->ApplyResourcePackage($package);
+
+                return Utils::GetResponse($byteReader);
+            } else {
+                return new Result("FIXME: ApplyResourcePackage will kill the mgserver on invalid package files", "text/plain");
+            }
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    public function GetExtension($name)
+    {
+        $extension = "";
+        $pos = strrpos($name, ".");
+
+        if ($pos === false) // note: three equal signs
+        {
+            if ("MG_USER_CREDENTIALS" == $name)
+            {
+                $extension = "txt";
+            }
+            else
+            {
+                $extension = "bin";
+            }
+        }
+        else
+        {
+            $extension = substr($name, $pos - strlen($name) + 1);
+        }
+
+        return $extension;
+    }
+
+    public function GetMimeType($extension)
+    {
+        $mimeType = "application/octet-stream";
+        $extension = strtolower($extension);
+
+        if ($extension == "agf")
+        {
+            $mimeType = "application/agf";
+        }
+        elseif ($extension == "bin")
+        {
+            $mimeType = "application/octet-stream";
+        }
+        elseif ($extension == "dwf")
+        {
+            $mimeType = "model/vnd.dwf";
+        }
+        elseif ($extension == "jpg" || $extension == "jpeg")
+        {
+            $mimeType = "image/jpeg";
+        }
+        elseif ($extension == "png")
+        {
+            $mimeType = "image/png";
+        }
+        elseif ($extension == "tif" || $extension == "tiff")
+        {
+            $mimeType = "image/tiff";
+        }
+        elseif ($extension == "html")
+        {
+            $mimeType = "text/html";
+        }
+        elseif ($extension == "txt")
+        {
+            $mimeType = "text/plain";
+        }
+        elseif ($extension == "xml")
+        {
+            $mimeType = "text/xml";
+        }
+
+        return $mimeType;
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ResourceServiceHttpRequests.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ResourceServiceHttpRequests.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ResourceServiceHttpRequests.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,482 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+
+//This class defines the operations that are currently supported by the unit test infrastructure for Resource Service methods exposed through Htpp.
+//There is one function per operation. Each function tries to retrieve the parameters that are required for its corresponding
+//operation and send the Http request
+//Edit that file to add more operations from Resource Service that are exposed through Http
+
+require_once("HttpRequest.php");
+require_once("Utils.php");
+require_once("Result.php");
+
+class ResourceServiceHttpRequests
+{
+    private $unitTestParamVm;
+    private $httpRequest;
+    private $URL;
+    private $db;
+
+    function __construct($db, $newURL)
+    {
+        $this->db = $db;
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->httpRequest = new HttpRequest();
+        $this->URL = $newURL;
+    }
+
+    function __destruct()
+    {
+        $this->unitTestParamVm->SqlFinalize();
+        unset($this->httpRequest);
+        unset($this->db);
+    }
+
+    function EnumerateResources ($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"TYPE\"");
+            $arrayParam["TYPE"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DEPTH\"");
+            $arrayParam["DEPTH"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function SetResource($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam['RESOURCEID']=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CONTENT\"");
+            if ($this->unitTestParamVm->GetString("ParamValue") != "")
+            {
+                $arrayParam['CONTENT']=$this->unitTestParamVm->GetString("ParamValue");
+                $arrayParam['CONTENT']=new CURLFile(Utils::GetPath($arrayParam['CONTENT']));
+            }
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"HEADER\"");
+            if ($this->unitTestParamVm->GetString("ParamValue") != "")
+            {
+                $arrayParam['HEADER']=$this->unitTestParamVm->GetString("ParamValue");
+                $arrayParam['HEADER']=new CURLFile(Utils::GetPath($arrayParam['HEADER']));
+
+            }
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam, "POST");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function DeleteResource($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetResourceContent($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PREPROCESS\"");
+            $arrayParam["PREPROCESS"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetResourceHeader($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateResourceData($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetResourceData($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DATANAME\"");
+            $arrayParam["DATANAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function SetResourceData($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DATANAME\"");
+            $arrayParam["DATANAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DATATYPE\"");
+            $arrayParam["DATATYPE"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DATALENGTH\"");
+            $arrayParam["DATALENGTH"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DATA\"");
+            $arrayParam["DATA"]=$this->unitTestParamVm->GetString("ParamValue");
+            $path = Utils::GetPath($arrayParam["DATA"]);
+            //HACK/FIXME: This SQLite-based test specification doesn't support platform-specific
+            //expectations to my knowledge, because on some of the test cases for this operation
+            //it is being fed paths with improper case (which obviously has no effect on Windows
+            //due to their paths being case-insensitive, but on Linux it's a different matter)
+            //
+            //So the workaround for now, is to abort the call and return the expected (Windows)
+            //result: an empty result
+            if (!Utils::IsWindows() && !is_file($path)) {
+                return new Result("", "text/plain");
+            }
+            $arrayParam["DATA"]=new CURLFile($path);
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam, "POST");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function RenameResourceData($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"OLDDATANAME\"");
+            $arrayParam["OLDDATANAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NEWDATANAME\"");
+            $arrayParam["NEWDATANAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function DeleteResourceData($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DATANAME\"");
+            $arrayParam["DATANAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function MoveResource($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SOURCE\"");
+            $arrayParam["SOURCE"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DESTINATION\"");
+            $arrayParam["DESTINATION"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function ApplyResourcePackage($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PACKAGE\"");
+
+            if ($this->unitTestParamVm->GetString("ParamValue") != "")
+            {
+                $arrayParam['PACKAGE']=$this->unitTestParamVm->GetString("ParamValue");
+                $arrayParam['PACKAGE']=new CURLFile(Utils::GetPath($arrayParam['PACKAGE']));
+            }
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam, "POST");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function UpdateRepository($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CONTENT\"");
+            if ($this->unitTestParamVm->GetString("ParamValue") != "")
+            {
+                $arrayParam['CONTENT']=$this->unitTestParamVm->GetString("ParamValue");
+                $arrayParam['CONTENT']=new CURLFile(Utils::GetPath($arrayParam['CONTENT']));
+            }
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"HEADER\"");
+            if ($this->unitTestParamVm->GetString("ParamValue") != "")
+            {
+                $arrayParam['HEADER']=$this->unitTestParamVm->GetString("ParamValue");
+                $arrayParam['HEADER']=new CURLFile(Utils::GetPath($arrayParam['HEADER']));
+            }
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam, "POST");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetRepositoryContent($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetRepositoryHeader($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateResourceReferences($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function CopyResource($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SOURCE\"");
+            $arrayParam["SOURCE"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DESTINATION\"");
+            $arrayParam["DESTINATION"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function ChangeResourceOwner($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"OWNER\"");
+            $arrayParam["OWNER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function InheritPermissionsFrom($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"RESOURCEID\"");
+            $arrayParam["RESOURCEID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Result.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Result.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Result.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,66 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+
+//This class defines a Result object. Each Result object contains the data returned from an operation,
+//content/mime type, and the httpStatusCode(http requests only)
+class Result
+{
+    private $resultData;
+    private $contentType;
+    private $httpStatusCode;
+
+    public function __construct($resultData="", $contentType="", $httpStatusCode="")
+    {
+        $this->resultData=$resultData;
+        $this->contentType=$contentType;
+        $this->httpStatusCode=$httpStatusCode;
+    }
+
+    public function SetResultData($newData)
+    {
+        $this->resultData=$newData;
+    }
+
+    public function SetContentType($newContentType)
+    {
+        $this->contentType=$newContentType;
+    }
+
+    public function SetHttpStatusCode($newHttpStatusCode)
+    {
+        $this->httpStatusCode=$newHttpStatusCode;
+    }
+
+    public function GetResultData()
+    {
+        return $this->resultData;
+    }
+
+    public function GetContentType()
+    {
+        return $this->contentType;
+    }
+
+    public function GetHttpStatusCode()
+    {
+        return $this->httpStatusCode;
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Run.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Run.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Run.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,250 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+
+//This class executes a test case based on input received from the HTML forms
+
+require_once("ExecuteOperation.php");
+require_once("Validate.php");
+require_once("Result.php");
+require_once("HtmlPrinter.php");
+require_once("Utils.php");
+
+if (!defined('Run_MAIN_METHOD'))
+{
+    define('Run_MAIN_METHOD', 'Run::main');
+    $test = new Run();
+    $testsRun = 0;
+    $test->main($testsRun);
+}
+
+class Run
+{
+    //Define any private data members here
+    private $unitTestVm;
+    private $unitTestResultVm;
+    private $apiCall;
+    private $unitTestDb;
+
+    private $validate;
+    private $operation;
+
+    public function main(&$testsRun, $file=null)
+    {
+        $status=-1;
+        //Print HTML tags for the result page
+        if ($_POST['output']=="html")
+        {
+            header('Content-type: text/html; charset=UTF-8');
+            HtmlPrinter::PrintHtmlHeader("Test Results");
+            HtmlPrinter::PrintFormHeader("Run.php");
+        }
+
+        //Open the file here if the file resource is null.
+        //This is in the case a test is executed from HTML form.
+        if (null == $file)
+        {
+            $file=fopen("UnitTests.log", "w");
+        }
+        //If initialization is successful then run the test and clean up
+        if (self::Initialize())
+        {
+            $status = self::RunTest($testsRun, $file);
+            self::CleanUp();
+        }
+
+        //Close HTML tags for the output
+        if ($_POST['output']=="html")
+        {
+            HtmlPrinter::PrintHtmlFooter();
+        }
+        return $status;
+    }
+
+
+    function Initialize()
+    {
+        try
+        {
+            //Retrieves data from the $_POST array.
+            //Initializes objects that will required during the test execution
+            if (!isset($_SERVER['SERVER_ADDR']))
+            {
+                $url="http://localhost/mapguide/mapagent/mapagent.fcgi";
+            }
+            else
+            {
+                $url="http://".$_SERVER['SERVER_ADDR'].":".$_SERVER['SERVER_PORT']."/mapguide/mapagent/mapagent.fcgi";
+            }
+            //print("Using mapagent url: $url\n");
+            $testName=$_POST['testName'];
+            $dbName = Utils::GetPath($_POST[$testName.":dbPath"]);
+            if (file_exists($dbName))
+            {
+                $status = true;
+
+                $this->unitTestDb = new SqliteDB();
+                $this->unitTestDb->Open("$dbName");
+
+                $this->unitTestVm = new SqliteVM($this->unitTestDb);
+                $this->unitTestResultVm = new SqliteVM($this->unitTestDb);
+
+                $this->operation = new ExecuteOperation($_POST['requestType'], $this->unitTestDb, $url);
+                $this->validate = new Validate($this->unitTestDb);
+            }
+            else
+            {
+                //TODO: What to do when the database is missing
+                print("Cannot find database. Where did you put my database: ".$dbName."?");
+                $status=false;
+            }
+            return $status;
+        }
+        catch (MgException $e)
+        {
+            print $e->GetDetails();
+            return false;
+        }
+        catch (SqliteException $s)
+        {
+            print $s->GetExceptionMessage();
+            return false;
+        }
+    }
+
+    //Deletes all objects after the test execution
+    function CleanUp()
+    {
+        try
+        {
+            unset($this->operation);
+            unset($this->validate);
+            $this->unitTestResultVm = null;
+            $this->unitTestVm = null;
+            $this->unitTestDb = null;
+        }
+        catch (MgException $e)
+        {
+            print $e->GetExceptionMessage('en');
+        }
+        catch (SqliteException $s)
+        {
+            print $s->GetExceptionMessage();
+        }
+    }
+
+    //Runs a test
+    function RunTest(&$testsRun, $file)
+    {
+        try
+        {
+            $actualResult=new Result();
+            $exitStatus=0;
+            $testName=$_POST['testName'];
+
+            //Add the test in the log file
+            $entry="\n\n************************************************************\n";
+            $entry .= sprintf("Executing %s\n", $testName);
+            $entry.="************************************************************\n\n";
+
+            fwrite($file, $entry);
+
+            //Get the list of operations to be executed for this test. This list should comma separated, no spaces.
+            $this->unitTestVm->Execute("Select ParamSets from TestCase where TestName=\"$testName\"");
+            $sets = $this->unitTestVm->GetString("ParamSets");
+
+            $paramSet = strtok($sets, ",");
+
+
+            if ($_POST['output']=="html")
+            {
+                //Start the HTML table for the results
+                HtmlPrinter::PrintResultTableHeader($_POST['requestType']." Test ".$testName);
+            }
+
+            //For each operation in the list
+            //Execute the operation and validate the result
+            while ($paramSet != "")
+            {
+                $this->unitTestVm->Execute("Select ParamValue from Params where ParamSet=$paramSet AND ParamName=\"OPERATION\";");
+                $paramValue = $this->unitTestVm->GetString("ParamValue");
+
+                $testsRun++;
+
+                //Add the operation to the log file
+                $this->AddLogFileEntry($file, $paramValue, $paramSet, $this->unitTestResultVm);
+
+                $actualResult = $this->operation->Execute($paramSet, $paramValue);
+
+                if (substr_count($_POST['requestType'],"Http"))
+                {
+                    $status = $this->validate->ValidateHttpRequest($_POST['testName'], $paramSet, $paramValue, $actualResult, $file);    
+                }
+                else
+                {
+                    $status = $this->validate->ValidateApiRequest($_POST['testName'], $paramSet, $paramValue, $actualResult, $file);
+                }
+
+                $exitStatus += $status;
+
+                $paramSet = strtok(",");
+            }
+
+            //Closes the Html table. Also outputs hidden fields that are needed for Generation mode
+            if ($_POST['output']=="html")
+            {
+                HtmlPrinter::printTableEnd();
+                HtmlPrinter::PrintGenerateFormHiddenFields();
+                HtmlPrinter::PrintFormFooter($_POST['requestType'], "Generate");
+            }
+
+            if ($_POST['testExecutionMode'] == "generate")
+            {
+                Utils::CreateDumpFile($this->unitTestDb);
+            }
+
+            return $exitStatus;
+        }
+        catch (MgException $e)
+        {
+            print $e->GetExceptionMessage("en")."\n";
+        }
+        catch (SqliteException $s)
+        {
+            print $s->GetExceptionMessage()."\n";
+        }
+    }
+
+    function AddLogFileEntry($file, $operation, $paramSet, $vm)
+    {
+        fwrite($file, "\nParamSet: ".$paramSet."\n");
+
+        $status = $vm->Execute("Select * from Params where ParamSet=$paramSet");
+
+        while ($status==SQLITE_ROW)
+        {
+            $paramName = $vm->GetString("ParamName");
+            $paramValue = $vm->GetString("ParamValue");
+            $entry = sprintf("%s: %s\n", $paramName, $paramValue);
+            fwrite($file, $entry);
+            $status = $vm->NextRow();
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/RunTests.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/RunTests.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/RunTests.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,185 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+//Command line script that executes all tests in the given database files
+
+define('Run_MAIN_METHOD', true);
+require_once("sqlite_constants.php");
+require_once("Run.php");
+main();
+
+function main()
+{
+    SetGlobals();
+    $testsFailed = 0;
+    $testsRun = 0;
+    $isEnterprise = false;
+    $runApiTestsOnly = false;
+    $file = fopen("UnitTests.log", "w");
+
+    date_default_timezone_set("GMT-0");
+    $str = sprintf("Run started: %s\n\n",date("D M j G:i:s T Y"));
+
+    echo $str;
+    fwrite($file, $str);
+
+    if (($_SERVER['argc'] >1) && ($_SERVER['argv'][1] == "-help"))
+    {
+        ShowUsage();
+    }
+
+    if (($_SERVER['argc']>1) && ($_SERVER['argv'][1]=="/enterprise"))
+    {
+        $isEnterprise = true;
+    }
+    
+    if (($_SERVER['argc']>1))
+    {
+        for ($i = 0; $i < count($_SERVER['argv']); $i++)
+        {
+            if ($_SERVER['argv'][$i] == "-apionly")
+            {
+                $runApiTestsOnly = true;
+            }
+            else if ($_SERVER['argv'][$i] == "-config" && $i < count($_SERVER['argv']) - 1)
+            {
+                define("WEBCONFIGINI", $_SERVER['argv'][$i+1]);
+            }
+        }
+    }
+
+    if (!$file)
+    {
+        echo "Unable to create log file UnitTests.log";
+    }
+
+    $testsFailed += ExecuteTest("Api", "../../TestData/ResourceService/ResourceServiceTest.dump", $testsRun, $file, $isEnterprise);
+    if (!$runApiTestsOnly)
+        $testsFailed += ExecuteTest("Http", "../../TestData/ResourceService/ResourceServiceTest.dump", $testsRun, $file, $isEnterprise);
+
+    $testsFailed += ExecuteTest("Api", "../../TestData/DrawingService/DrawingServiceTest.dump", $testsRun, $file, $isEnterprise);
+    if (!$runApiTestsOnly)
+        $testsFailed += ExecuteTest("Http", "../../TestData/DrawingService/DrawingServiceTest.dump", $testsRun, $file, $isEnterprise);
+
+    $testsFailed += ExecuteTest("Api", "../../TestData/FeatureService/FeatureServiceTest.dump", $testsRun, $file, $isEnterprise);
+    if (!$runApiTestsOnly)
+        $testsFailed += ExecuteTest("Http", "../../TestData/FeatureService/FeatureServiceTest.dump", $testsRun, $file, $isEnterprise);
+
+    $testsFailed += ExecuteTest("Api", "../../TestData/SiteService/SiteServiceTest.dump", $testsRun, $file, $isEnterprise);
+    if (!$runApiTestsOnly)
+        $testsFailed += ExecuteTest("Http", "../../TestData/SiteService/SiteServiceTest.dump", $testsRun, $file, $isEnterprise);
+
+    $testsFailed += ExecuteTest("Api", "../../TestData/MappingService/MappingServiceTest.dump", $testsRun, $file, $isEnterprise);
+    if (!$runApiTestsOnly)
+        $testsFailed += ExecuteTest("Http", "../../TestData/MappingService/MappingServiceTest.dump", $testsRun, $file, $isEnterprise);
+
+    $testsFailed += ExecuteTest("Api", "../../TestData/ServerAdmin/ServerAdminTest.dump", $testsRun, $file, $isEnterprise);
+
+    $testsFailed += ExecuteTest("Api", "../../TestData/MapLayer/MapLayerTest.dump", $testsRun, $file, $isEnterprise);
+
+    $testsFailed += ExecuteTest("Api", "../../TestData/WebLayout/WebLayoutTest.dump", $testsRun, $file, $isEnterprise);
+
+    if (!$runApiTestsOnly)
+    {
+        $testsFailed += ExecuteTest("Http", "../../TestData/Wfs/WfsTest.dump", $testsRun, $file, $isEnterprise);
+        $testsFailed += ExecuteTest("Http", "../../TestData/Wms/WmsTest.dump", $testsRun, $file, $isEnterprise);
+    }
+    $testsFailed += ExecuteTest("Api", "../../TestData/Unicode/UnicodeTest.dump", $testsRun, $file, $isEnterprise);
+
+    $str = sprintf("\n\nTests failed/run: %d/%d\n", $testsFailed, $testsRun);
+    $str .=sprintf("Run ended: %s\n\n",date("D M j G:i:s T Y"));
+    echo $str;
+    fwrite($file, $str);
+
+    if ($file)
+    {
+        fclose($file);
+    }
+
+    exit($testsFailed);
+}
+
+function ExecuteTest($requestType, $dumpFileName, &$testsRun, $file, $ent)
+{
+    try
+    {
+        $exitStatus=0;
+        //Create database objects
+        $dbPath = Utils::GetDbPath($dumpFileName);
+        $dbName = Utils::GetPath($dbPath);
+
+        if (file_exists($dbName))
+        {
+            $db = new SqliteDB();
+            $db->Open($dbName);
+            $vm = new SqliteVM($db);
+            //Select all tests from the specified request type. Order tests using ExecuteSequence field
+            if ($ent)
+            {
+                $rType = $requestType;
+                $status = $vm->Execute("Select TestName, TestType from TestCase where TestType=\"$rType\" or TestType=\"$requestType\"order by ExecuteSequence");
+            }
+            else
+            {
+                $status = $vm->Execute("Select TestName, TestType from TestCase where TestType=\"$requestType\" order by ExecuteSequence");
+            }
+
+            while ($status==SQLITE_ROW)
+            {
+                $testName = $vm->GetString("TestName");
+                $requestType = $vm->GetString("TestType");
+
+                printf ("Executing %s test: %s\n", $requestType, $testName);
+                //File in the $_POST array to simulate a GET request that is  normally send by the HTML forms
+                $_POST['testName']=$testName;
+                $_POST['requestType']=$requestType;
+                $_POST[$testName.':dbPath']=$dbPath;
+
+                $result = new Run($testsRun);
+                $exitStatus += $result->main($testsRun, $file);
+                $status = $vm->NextRow();
+            }
+            unset($vm);
+            unset($db);
+            return $exitStatus;
+        }
+    }
+    catch (SqliteException $s)
+    {
+        print $s->GetExceptionMessage();
+        $s->Dispose();
+        return 1;
+    }
+}
+
+function SetGlobals()
+{
+    //Sets global variables
+    $_POST['output']="text";
+    $_POST['testExecutionMode']="validate";
+}
+
+function ShowUsage()
+{
+    printf("Usage: php RunTests.php [-option]\n");
+    printf ("\t-help Displays this help\n");
+    printf ("\t/enterprise Runs unit tests for enterprise server(All feature providers)\n");
+}
+
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/SQLiteEngine.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/SQLiteEngine.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/SQLiteEngine.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,214 @@
+<?php
+
+// SQLiteEngine.php
+//
+// A PHP-based implementation of SqliteDB and SqliteVM class using PDO instead
+// of the internal SWIG-wrapped SQLite classes in Oem
+//
+// This avoids any access violation issues should our test runner happen to
+// be mis-using any classes in the SWIG wrapper.
+
+if (!defined('SQLITE_ROW')) {
+    define('SQLITE_ROW', 100);
+}
+
+if (!extension_loaded("SQLitePhpApi"))
+{
+    //echo "SQLitePhpApi not loaded. Using PHP implementation";
+
+    class SqliteException extends Exception
+    {
+    }
+
+    class CantOpenDbException extends SqliteException
+    {
+    }
+    
+    class SqliteDB
+    {
+        private $dbh;
+        private $dbName;
+
+        public function __construct()
+        {
+            $this->dbh = NULL;
+            $this->dbName = "";
+        }
+        
+        public function __destruct()
+        {
+            $this->Close();
+        }
+
+        public function Close()
+        {
+            if ($this->dbh != NULL)
+            {
+                $this->dbh = NULL;
+            }
+        }
+        
+        public function GetHandle() { return $this->dbh; }
+        
+        public function Open($dbName)
+        {
+            $this->Close();
+            $this->dbh = new PDO("sqlite:$dbName");
+            $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+            $this->dbName = $dbName;
+        }
+        
+        public function GetName() { return $this->dbName; }
+        
+        public function GenerateDatabase($dumpFilePath, $dbPath)
+        {
+            if (php_sapi_name() == 'cli')
+                echo "Generating database from dump: $dumpFilePath\n";
+            else
+                echo "Generating database from dump: $dumpFilePath<br/>";
+            $pdo = new PDO("sqlite:$dbPath");
+            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+            $sql = file_get_contents($dumpFilePath);
+            $pdo->exec($sql);
+            $pdo = NULL;
+        }
+        
+        public function DumpDatabase($path)
+        {
+            throw new Exception("Creating database dumps not implemented. Please run this script with the php_SQLitePhpApi extension loaded");
+        }
+    }
+    
+    class SqliteVM
+    {
+        private $db;
+        private $currentStmt;
+        private $currentRow;
+        private $resultLOB;
+        
+        private $nameIndexMap;
+
+        public function __construct($db)
+        {
+            $this->db = $db->GetHandle();
+            $this->currentStmt = NULL;
+            $this->currentRow = NULL;
+            $this->nameIndexMap = array();
+        }
+        
+        public function Execute($query)
+        {
+            /*
+            if (php_sapi_name() == 'cli')
+                echo "Executing: $query\n";
+            else
+                echo "Executing: $query<br/>";
+            */
+            if ($this->currentStmt != NULL)
+            {
+                $this->currentStmt->closeCursor();
+                $this->currentStmt = NULL;
+            }
+            $this->nameIndexMap = array();
+            $this->currentStmt = $this->db->prepare($query);
+            try
+            {
+                if ($this->currentStmt === FALSE)
+                {
+                    if (php_sapi_name() == 'cli')
+                        echo "ERROR: Failed to prepare query: $query\n";
+                    else
+                        echo "ERROR: Failed to prepare query: $query<br/>";
+                    return -1;
+                }
+                if (!$this->currentStmt->execute())
+                {
+                    if (php_sapi_name() == 'cli')
+                        echo "ERROR: Failed to execute query: $query\n";
+                    else
+                        echo "ERROR: Failed to execute query: $query<br/>";
+                    return -1;
+                }
+            }
+            catch (PDOException $e)
+            {
+                return -1;
+            }
+            //Compile name index map for the purposes of column binding below
+            for ($i = 0; $i < $this->currentStmt->columnCount(); $i++)
+            {
+                try {
+                    $col = $this->currentStmt->getColumnMeta($i);
+                    if ($col === FALSE)
+                        continue;
+                } catch (PDOException $e) {
+                    continue;
+                }
+                if (array_key_exists("name", $col)) 
+                {
+                    $colName = $col["name"];
+                    $this->nameIndexMap[$colName] = $i;
+                }
+            }
+            
+            //Bind columns, paying special attention to bind Result as BLOB if found
+            $this->currentRow = array();
+            foreach ($this->nameIndexMap as $colName => $index)
+            {
+                if ($colName == "Result") {
+                    $this->currentStmt->bindColumn(($index+1), $this->currentRow[$colName], PDO::PARAM_LOB);
+                    //echo "Bound $colName as BLOB<br/>";
+                } else {
+                    $this->currentStmt->bindColumn(($index+1), $this->currentRow[$colName]);
+                }
+            }
+            
+            try
+            {
+                $res = $this->currentStmt->fetch();
+                if ($res === FALSE)
+                    return -1;
+            }
+            catch (PDOException $e)
+            {
+                return -1;
+            }
+            return SQLITE_ROW;
+        }
+        
+        public function GetString($name)
+        {
+            if (array_key_exists($name, $this->currentRow))
+                return $this->currentRow[$name];
+            return "";
+        }
+        
+        public function GetBlob($name)
+        {
+            $str = (string)$this->currentRow[$name];
+            //SqliteBlob in C++ has a MgByteReader-style interface. Since
+            //in PHP if it quacks like a duck ... we'll give them an actual MgByteReader
+            $source = new MgByteSource($str, strlen($str));
+            return $source->GetReader();
+        }
+        
+        public function NextRow()
+        {
+            $this->currentRow = $this->currentStmt->fetch();
+            if ($this->currentRow === FALSE)
+                return -1;
+            return SQLITE_ROW;
+        }
+        
+        public function SqlFinalize()
+        {
+            if ($this->currentStmt != NULL)
+            {
+                $this->currentStmt->closeCursor();
+                $this->currentStmt = NULL;
+            }
+        }
+    }
+}
+
+?>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ServerAdminAPI.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ServerAdminAPI.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ServerAdminAPI.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,327 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("master.php");
+require_once("Result.php");
+require_once("Utils.php");
+
+class ServerAdminAPI
+{
+    private $cred;
+    private $unitTestParamVm;
+    private $arrayParam;
+    private $serverAdmin;
+
+    function __construct($db)
+    {
+        Utils::MapAgentInit(WEBCONFIGINI);
+
+        $this->cred = new MgUserInformation();
+        $this->cred->SetMgUsernamePassword("Administrator","admin");
+        $this->cred->SetLocale("en");
+
+        $this->serverAdmin = new MgServerAdmin();
+        //!not work with localhost?
+        $this->serverAdmin->Open("127.0.0.1", $this->cred);
+
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->arrayParam = array();
+
+    }
+
+    function __destruct()
+    {
+        unset($this->cred);
+        unset($this->unitTestParamVm);
+        unset($this->arrayParam);
+    }
+
+    //Unused for now. Will be used when GetProperties() is implemented in server admin
+    function GetProperties($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SECTION\"");
+            $section = $this->unitTestParamVm->GetString("ParamValue");
+
+            $properties = $this->serverAdmin->GetProperties($section);
+
+            return new Result(Utils::MgStringCollectionToString($properties), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function TakeOffline($paramSet)
+    {
+        try
+        {
+            $this->serverAdmin->TakeOffline();
+            $isOnline = $this->serverAdmin->IsOnline();
+
+            return new Result(Utils::BooleanToString($isOnline), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function BringOnline($paramSet)
+    {
+        try
+        {
+            $this->serverAdmin->BringOnline();
+            $isOnline = $this->serverAdmin->IsOnline();
+
+            return new Result(Utils::BooleanToString($isOnline), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetLog($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LOGTYPE\"");
+            $logtype = $this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NUMENTRIES\"");
+            $numEntries = $this->unitTestParamVm->GetString("ParamValue");
+            if ($numEntries != null && is_numeric($numEntries)) 
+            {
+                $numEntries = intval($numEntries);
+            }
+            if ($numEntries == null)
+            {
+                $byteReader = $this->serverAdmin->GetLog($logtype);
+            }
+            else
+            {
+                $byteReader = $this->serverAdmin->GetLog($logtype, $numEntries);
+            }
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetLogByDate($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LOGTYPE\"");
+            $logtype = $this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"FROMDATE\"");
+            $fromDateString = $this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"TODATE\"");
+            $toDateString = $this->unitTestParamVm->GetString("ParamValue");
+
+            $fromDatePieces = explode(",", $fromDateString);
+            $toDatePieces = explode(",", $toDateString);
+
+            // Build an MgDateTime object based on a comma deliminated string.  If the string does not explode into
+            // the expected number of parts, a NULL value will be assigned
+            $fromDate = NULL;
+            $toDate = NULL;
+
+            if (sizeof($fromDatePieces) == 3)
+            {
+                $fromDate = new MgDateTime(intval($fromDatePieces[0]), intval($fromDatePieces[1]), intval($fromDatePieces[2]));
+            }
+            else if (sizeof($fromDatePieces) == 7)
+            {
+                $fromDate = new MgDateTime(intval($fromDatePieces[0]), intval($fromDatePieces[1]), intval($fromDatePieces[2]), intval($fromDatePieces[3]), intval($fromDatePieces[4]), intval($fromDatePieces[5]), intval($fromDatePieces[6]));
+            }
+
+            if (sizeof($toDatePieces) == 3)
+            {
+                $toDate = new MgDateTime(intval($toDatePieces[0]), intval($toDatePieces[1]), intval($toDatePieces[2]));
+            }
+            else if (sizeof($toDatePieces) == 7)
+            {
+                $toDate = new MgDateTime(intval($toDatePieces[0]), intval($toDatePieces[1]), intval($toDatePieces[2]), intval($toDatePieces[3]), intval($toDatePieces[4]), intval($toDatePieces[5]), intval($toDatePieces[6]));
+            }
+
+            $byteReader = $this->serverAdmin->GetLog($logtype, $fromDate, $toDate);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function ClearLog($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LOGTYPE\"");
+            $logtype = $this->unitTestParamVm->GetString("ParamValue");
+            $cleared = $this->serverAdmin->ClearLog($logtype);
+            return new Result(Utils::BooleanToString($cleared), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function EnumerateLogs($paramSet)
+    {
+        try
+        {
+            $logs = $this->serverAdmin->EnumerateLogs();
+            return new Result(Utils::MgStringCollectionToString($logs), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function DeleteLog($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"FILENAME\"");
+            $fileName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->serverAdmin->DeleteLog($fileName);
+            $result = Utils::GetResponse($byteReader);
+            return $result;
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function RenameLog($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"OLDFILENAME\"");
+            $oldFileName = $this->unitTestParamVm->GetString("ParamValue");
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NEWFILENAME\"");
+            $newFileName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->serverAdmin->RenameLog($oldFileName, $newFileName);
+            $result = Utils::GetResponse($byteReader);
+            return $result;
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function EnumeratePackages($paramSet)
+    {
+        try
+        {
+            $packages = $this->serverAdmin->EnumeratePackages();
+            return new Result(Utils::MgStringCollectionToString($packages), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function DeletePackage($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PACKAGENAME\"");
+            $packageName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->serverAdmin->DeletePackage($packageName);
+            $result = Utils::GetResponse($byteReader);
+            return $result;
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function LoadPackage($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PACKAGENAME\"");
+            $packageName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->serverAdmin->LoadPackage($packageName);
+            $result = Utils::GetResponse($byteReader);
+            return $result;
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetPackageStatus($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PACKAGENAME\"");
+            $packageName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $statusInfo = $this->serverAdmin->GetPackageStatus($packageName);
+            $statusCode = $statusInfo->GetStatusCode();
+
+            return new Result($statusCode, "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetPackageLog($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PACKAGENAME\"");
+            $packageName = $this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->serverAdmin->GetPackageLog($packageName);
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/SiteServiceAPI.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/SiteServiceAPI.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/SiteServiceAPI.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,787 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+//This class defines the operations that are currently supported by the unit test infrastructure for Site Service API .
+//There is one function per operation. Each function tries to retrieve the parameters that are required for its corresponding
+//operation and executes the operation
+//Edit that file to add more operations for Site Service API
+
+require_once("master.php");
+require_once("Result.php");
+require_once("Utils.php");
+
+$previousSession = " ";
+
+class SiteServiceAPI
+{
+    private $unitTestParamVm;
+    private $cred;
+    private $site;
+    private $arrayParam;
+    private $drawingSrvc;
+
+    function __construct($db)
+    {
+        Utils::MapAgentInit(WEBCONFIGINI);
+
+        $this->cred = new MgUserInformation();
+        $this->cred->SetMgUsernamePassword("Administrator","admin");
+        $this->cred->SetLocale("en");
+
+        $this->site = new MgSite();
+        $this->site->Open($this->cred);
+
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->arrayParam = array();
+    }
+
+    function __destruct()
+    {
+        $this->site->Close();
+        unset($this->site);
+        unset($this->cred);
+        unset($this->unitTestParamVm);
+        unset($this->arrayParam);
+    }
+
+    function CreateSession ($paramSet)
+    {
+        try
+        {
+            global $previousSession;
+            $userInfo = new MgUserInformation();
+            $userInfo->SetMgUsernamePassword("Administrator","admin");
+            $userInfo->SetLocale("en");
+
+            $site2 = new MgSite();
+            $site2->Open($userInfo);
+            $sess = $site2->CreateSession();
+            $previousSession = $sess;
+            $site2->Close();
+            return (new Result($sess, "text/plain"));
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function DestroySession($paramSet)
+    {
+        try
+        {
+            return (new Result("Not Implemented Yet", "text/plain"));
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetUserForSession($paramSet)
+    {
+        try
+        {
+            global $previousSession;
+            $site2 = new MgSite();
+            $userInfo = new MgUserInformation();
+            $userInfo->SetMgSessionId($previousSession);
+            $site2->Open($userInfo);
+            $userId = $site2->GetUserForSession();
+            $site2->Close();
+            return (new Result($userId, "text/plain"));
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function GetSiteServerAddress($paramSet)
+    {
+        try
+        {
+            $result = $this->site->GetSiteServerAddress();
+
+            return (new Result($result, "text/plain"));
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+    function EnumerateUsers($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUP\"");
+            $this->arrayParam["GROUP"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"ROLE\"");
+            $this->arrayParam["ROLE"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"INCLUDEGROUPS\"");
+            $this->arrayParam["INCLUDEGROUPS"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            if ($this->arrayParam["ROLE"]!="")
+            {
+                $byteReader = $this->site->EnumerateUsers($this->arrayParam['GROUP'], $this->arrayParam['ROLE'], $this->arrayParam['INCLUDEGROUPS']);
+            }
+            else
+            {
+                $byteReader = $this->site->EnumerateUsers($this->arrayParam['GROUP']);
+            }
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function AddUser($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"USERID\"");
+            $this->arrayParam["USERID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"USERNAME\"");
+            $this->arrayParam['USERNAME']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PASSWORD\"");
+            $this->arrayParam['PASSWORD']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DESCRIPTION\"");
+            $this->arrayParam['DESCRIPTION']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $byteReader = $this->site->AddUser($this->arrayParam["USERID"], $this->arrayParam['USERNAME'], $this->arrayParam['PASSWORD'], $this->arrayParam['DESCRIPTION']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function UpdateUser($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"USERID\"");
+            $this->arrayParam["USERID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NEWUSERID\"");
+            $this->arrayParam["NEWUSERID"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NEWUSERNAME\"");
+            $this->arrayParam['NEWUSERNAME']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NEWPASSWORD\"");
+            $this->arrayParam['NEWPASSWORD']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NEWDESCRIPTION\"");
+            $this->arrayParam['NEWDESCRIPTION']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $byteReader = $this->site->UpdateUser($this->arrayParam["USERID"], $this->arrayParam["NEWUSERID"],  $this->arrayParam['NEWUSERNAME'], $this->arrayParam['NEWPASSWORD'], $this->arrayParam['NEWDESCRIPTION']);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+
+    }
+
+    function DeleteUsers($paramSet)
+    {
+        try
+        {
+            //TODO: This will not work. How to create a string collection?
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"USERS\"");
+            $this->arrayParam["USERS"]=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $byteReader = $this->site->DeleteUsers($this->arrayParam["USERS"]);
+
+            return Utils::GetResponse($byteReader);
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GrantRoleMembershipsToUsers($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"ROLES\"");
+            $this->arrayParam["ROLES"]=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"USERS\"");
+            $this->arrayParam['USERS']=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $byteReader = $this->site->GrantRoleMembershipsToUsers($this->arrayParam['ROLES'], $this->arrayParam['USERS']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+    function RevokeRoleMembershipsFromUsers($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"ROLES\"");
+            $this->arrayParam["ROLES"]=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"USERS\"");
+            $this->arrayParam['USERS']=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $byteReader = $this->site->RevokeRoleMembershipsFromUsers($this->arrayParam['ROLES'], $this->arrayParam['USERS']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GrantGroupMembershipsToUsers($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUPS\"");
+            $this->arrayParam["GROUPS"]=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"USERS\"");
+            $this->arrayParam['USERS']=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $byteReader = $this->site->GrantGroupMembershipsToUsers($this->arrayParam['GROUPS'], $this->arrayParam['USERS']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function RevokeGroupMembershipsFromUsers($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUPS\"");
+            $this->arrayParam["GROUPS"]=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"USERS\"");
+            $this->arrayParam['USERS']=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $byteReader = $this->site->RevokeGroupMembershipsFromUsers($this->arrayParam['GROUPS'], $this->arrayParam['USERS']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateGroups($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"USER\"");
+            $this->arrayParam["USER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"ROLE\"");
+            $this->arrayParam['ROLE']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $byteReader = $this->site->EnumerateGroups($this->arrayParam['USER'], $this->arrayParam['ROLE']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+
+    }
+
+    function EnumerateGroups2($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"USER\"");
+            $this->arrayParam["USER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LOGIN\"");
+            $this->arrayParam['LOGIN']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PASSWORD\"");
+            $this->arrayParam['PASSWORD']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $userInfo = new MgUserInformation();
+            $userInfo->SetMgUsernamePassword($this->arrayParam['LOGIN'], $this->arrayParam['PASSWORD']);
+            $userInfo->SetLocale("en");
+
+            $site2 = new MgSite();
+            $site2->Open($userInfo);
+
+            $byteReader = $site2->EnumerateGroups($this->arrayParam['USER']);
+
+            $site2->Close();
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+
+    }
+
+    function EnumerateRoles2($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"USER\"");
+            $this->arrayParam["USER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"LOGIN\"");
+            $this->arrayParam['LOGIN']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"PASSWORD\"");
+            $this->arrayParam['PASSWORD']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $userInfo = new MgUserInformation();
+            $userInfo->SetMgUsernamePassword($this->arrayParam['LOGIN'], $this->arrayParam['PASSWORD']);
+            $userInfo->SetLocale("en");
+
+            $site2 = new MgSite();
+            $site2->Open($userInfo);
+
+            $collection = $site2->EnumerateRoles($this->arrayParam['USER']);
+
+            $site2->Close();
+
+            return new Result(Utils::MgStringCollectionToString($collection), "text/plain");
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+
+    }
+
+    function AddGroup($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUP\"");
+            $this->arrayParam["GROUP"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DESCRIPTION\"");
+            $this->arrayParam['DESCRIPTION']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $byteReader = $this->site->AddGroup($this->arrayParam['GROUP'], $this->arrayParam['DESCRIPTION']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function UpdateGroup($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUP\"");
+            $this->arrayParam["GROUP"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NEWGROUP\"");
+            $this->arrayParam["NEWGROUP"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NEWDESCRIPTION\"");
+            $this->arrayParam['NEWDESCRIPTION']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $byteReader = $this->site->UpdateGroup($this->arrayParam['GROUP'], $this->arrayParam['NEWGROUP'], $this->arrayParam['NEWDESCRIPTION']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function DeleteGroups($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUPS\"");
+            $this->arrayParam["GROUPS"]=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $byteReader = $this->site->DeleteGroups($this->arrayParam['GROUPS']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GrantRoleMembershipsToGroups($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"ROLES\"");
+            $this->arrayParam["ROLES"]=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUPS\"");
+            $this->arrayParam['GROUPS']=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $byteReader = $this->site->GrantRoleMembershipsToGroups($this->arrayParam['ROLES'], $this->arrayParam['GROUPS']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function RevokeRoleMembershipsFromGroups($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"ROLES\"");
+            $this->arrayParam["ROLES"]=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUPS\"");
+            $this->arrayParam['GROUPS']=Utils::StringToMgStringCollection($this->unitTestParamVm->GetString("ParamValue"));
+
+            $byteReader = $this->site->RevokeRoleMembershipsFromGroups($this->arrayParam['ROLES'], $this->arrayParam['GROUPS']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateRoles($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"USER\"");
+            $this->arrayParam["USER"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUP\"");
+            $this->arrayParam['GROUP']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $collection = $this->site->EnumerateRoles($this->arrayParam['USER'], $this->arrayParam['GROUP']);
+
+            return new Result(Utils::MgStringCollectionToString($collection), "text/plain");
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateServers($paramSet)
+    {
+        try
+        {
+            $byteReader = $this->site->EnumerateServers();
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function AddServer($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NAME\"");
+            $this->arrayParam["NAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"DESCRIPTION\"");
+            $this->arrayParam['DESCRIPTION']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"ADDRESS\"");
+            $this->arrayParam["ADDRESS"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->site->AddServer($this->arrayParam['NAME'], $this->arrayParam['DESCRIPTION'], $this->arrayParam['ADDRESS']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function UpdateServer($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"OLDNAME\"");
+            $this->arrayParam["OLDNAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NEWNAME\"");
+            $this->arrayParam['NEWNAME']=$this->unitTestParamVm->GetString("ParamValue")."";
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NEWDESCRIPTION\"");
+            $this->arrayParam["NEWDESCRIPTION"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NEWADDRESS\"");
+            $this->arrayParam["NEWADDRESS"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->site->UpdateServer($this->arrayParam['OLDNAME'], $this->arrayParam['NEWNAME'], $this->arrayParam['NEWDESCRIPTION'], $this->arrayParam['NEWADDRESS']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function RemoveServer($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NAME\"");
+            $this->arrayParam["NAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->site->RemoveServer($this->arrayParam['NAME']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateServicesOnServer($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NAME\"");
+            $this->arrayParam["NAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $collection = $this->site->EnumerateServicesOnServer($this->arrayParam['NAME']);
+            $byteReader = $collection->ToXml();
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function AddServicesToServer($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NAME\"");
+            $this->arrayParam["NAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SERVICES\"");
+            $this->arrayParam["SERVICES"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->site->AddServicesToServer($this->arrayParam['NAME'], $this->arrayParam['SERVICES']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function RemoveServicesFromServer($paramSet)
+    {
+        try
+        {
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"NAME\"");
+            $this->arrayParam["NAME"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"SERVICES\"");
+            $this->arrayParam["SERVICES"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $byteReader = $this->site->RemoveServicesFromServer($this->arrayParam['NAME'], $this->arrayParam['SERVICES']);
+
+            return Utils::GetResponse($byteReader);
+        }
+
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/SiteServiceHttpRequests.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/SiteServiceHttpRequests.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/SiteServiceHttpRequests.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,83 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+
+//This file defines the operations that are currently supported by the unit test infrastructure for Site Service methods exposed through Htpp.
+//There is one function per operation. Each function tries to retrieve the parameters that are required for its corresponding
+//operation and send the Http request
+//Edit that file to add more operations from Site Service that are exposed through Http
+
+require_once("HttpRequest.php");
+require_once("Utils.php");
+
+class SiteServiceHttpRequests
+{
+    private $db;
+    private $unitTestParamVm;
+    private $URL;
+
+    function __construct($db, $newURL)
+    {
+        $this->db = $db;
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->httpRequest = new HttpRequest();
+        $this->URL = $newURL;
+    }
+
+    function __destruct()
+    {
+        unset($this->unitTestParamVm);
+        unset($this->httpRequest);
+        unset($this->db);
+    }
+
+    function EnumerateUsers($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"GROUP\"");
+            $arrayParam["GROUP"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function EnumerateGroups($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+            $arrayParam = Utils::SetCommonParams($paramSet, $this->db);
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Test1.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Test1.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Test1.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,92 @@
+<?php
+
+//This file is just to test any arbitrary API without going through the whole suite
+require_once("master.php");
+
+echo "Initializing web tier";
+
+MgInitializeWebTier("C:\\Program Files\\OSGeo\\MapGuide\\Web\\www\\webconfig.ini");
+echo "[php]: Initialized\n";
+$user = new MgUserInformation("Anonymous", "");
+$conn = new MgSiteConnection();
+$conn->Open($user);
+// Create a session repository
+$site = $conn->GetSite();
+$sessionID = $site->CreateSession();
+echo "[php]: Created session: $sessionID\n";
+$user->SetMgSessionId($sessionID);
+// Get an instance of the required services.
+$resourceService = $conn->CreateService(MgServiceType::ResourceService);
+echo "[php]: Created Resource Service\n";
+$mappingService = $conn->CreateService(MgServiceType::MappingService);
+echo "[php]: Created Mapping Service\n";
+$resId = new MgResourceIdentifier("Library://UnitTest/");
+echo "[php]: Enumeratin'\n";
+$resources = $resourceService->EnumerateResources($resId, -1, "");
+echo $resources->ToString() . "\n";
+echo "[php]: Coordinate System\n";
+$csFactory = new MgCoordinateSystemFactory();
+echo "[php]: CS Catalog\n";
+$catalog = $csFactory->GetCatalog();
+echo "[php]: Category Dictionary\n";
+$catDict = $catalog->GetCategoryDictionary();
+echo "[php]: CS Dictionary\n";
+$csDict = $catalog->GetCoordinateSystemDictionary();
+echo "[php]: Datum Dictionary\n";
+$datumDict = $catalog->GetDatumDictionary();
+echo "[php]: Coordinate System - LL84\n";
+$cs1 = $csFactory->CreateFromCode("LL84");
+echo "[php]: Coordinate System - WGS84.PseudoMercator\n";
+$cs2 = $csFactory->CreateFromCode("WGS84.PseudoMercator");
+echo "[php]: Make xform\n";
+$xform = $csFactory->GetTransform($cs1, $cs2);
+echo "[php]: WKT Reader\n";
+$wktRw = new MgWktReaderWriter();
+echo "[php]: WKT Point\n";
+$pt = $wktRw->Read("POINT (1 2)");
+$coord = $pt->GetCoordinate();
+echo "[php]: X: ".$coord->GetX().", Y: ".$coord->GetY()."\n";
+$site->DestroySession($sessionID);
+echo "[php]: Destroyed session $sessionID";
+echo "[php]: Test byte reader\n";
+$bs = new MgByteSource("abcd1234", 8);
+$content = "";
+$br = $bs->GetReader();
+$buf = "";
+while ($br->Read($buf, 2) > 0) {
+    echo "Buffer: '$buf'\n";
+    $content .= $buf;
+}
+echo "[php]: Test byte reader2\n";
+$bs2 = new MgByteSource("abcd1234", 8);
+$content = "";
+$br2 = $bs2->GetReader();
+$buf = "";
+while ($br2->Read($buf, 3) > 0) {
+    echo "Buffer: '$buf'\n";
+    $content .= $buf;
+}
+$agfRw = new MgAgfReaderWriter();
+echo "[php]: Trigger an exception\n";
+try {
+    $agfRw->Read(NULL);
+} catch (MgException $ex) {
+    echo "[php]: MgException caught\n";
+    echo "[php]: MgException - Message: ".$ex->GetExceptionMessage()."\n";
+    echo "[php]: MgException - Details: ".$ex->GetDetails()."\n";
+    echo "[php]: MgException - Stack: ".$ex->GetStackTrace()."\n";
+} catch (Exception $ex) {
+    echo "[php]: Exception: " . $ex->getMessage() . "\n";
+}
+echo "[php]: Trigger another exception\n";
+try {
+    $r2 = new MgResourceIdentifier("");
+} catch (MgException $ex2) {
+    echo "[php]: MgException caught\n";
+    echo "[php]: MgException - Message: ".$ex2->GetExceptionMessage()."\n";
+    echo "[php]: MgException - Details: ".$ex2->GetDetails()."\n";
+    echo "[php]: MgException - Stack: ".$ex2->GetStackTrace()."\n";
+} catch (Exception $ex) {
+    echo "[php]: Exception: " . $ex->getMessage() . "\n";
+}
+?>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/UpdateDumpFiles.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/UpdateDumpFiles.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/UpdateDumpFiles.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,74 @@
+<?php
+
+//
+//  Copyright (C) 2004-2014 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+//Command line script that executes all tests in the given database files
+
+define('Run_MAIN_METHOD', true);
+require_once("sqlite_constants.php");
+require_once("Utils.php");
+main();
+
+function main()
+{
+    if (($_SERVER['argc'] >1) && ($_SERVER['argv'][1] == "-help"))
+    {
+        ShowUsage();
+        return 1;
+    }
+
+    $updatesFailed = 0;
+    $updatesFailed += UpdateDumpFile("../../TestData/ResourceService/ResourceServiceTest.db");
+    $updatesFailed += UpdateDumpFile("../../TestData/DrawingService/DrawingServiceTest.db");
+    $updatesFailed += UpdateDumpFile("../../TestData/FeatureService/FeatureServiceTest.db");
+    $updatesFailed += UpdateDumpFile("../../TestData/SiteService/SiteServiceTest.db");
+    $updatesFailed += UpdateDumpFile("../../TestData/MappingService/MappingServiceTest.db");
+    $updatesFailed += UpdateDumpFile("../../TestData/ServerAdmin/ServerAdminTest.db");
+    $updatesFailed += UpdateDumpFile("../../TestData/MapLayer/MapLayerTest.db");
+    $updatesFailed += UpdateDumpFile("../../TestData/WebLayout/WebLayoutTest.db");
+    $updatesFailed += UpdateDumpFile("../../TestData/Wfs/WfsTest.db");
+    $updatesFailed += UpdateDumpFile("../../TestData/Wms/WmsTest.db");
+    $updatesFailed += UpdateDumpFile("../../TestData/Unicode/UnicodeTest.db");
+    
+    exit($updatesFailed);
+}
+
+function UpdateDumpFile($dbName)
+{
+    echo "Checking $dbName\n";
+    $ret = 0;
+    $db = NULL;
+    try {
+        $dbPath = Utils::GetPath($dbName);
+        $db = new SqliteDB();
+        $db->Open($dbPath);
+        Utils::CreateDumpFile($db, true);
+    } catch (SqliteException $ex) {
+        echo $ex->GetExceptionMessage()."\n";
+        $ret = 1;
+    }
+    $db = NULL;
+    return $ret;
+}
+
+function ShowUsage()
+{
+    printf("Usage: php UpdateDumpFiles.php\n");
+}
+
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Utils.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Utils.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Utils.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,329 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+//Class that defines static methods that are commonly used in the infrastructure
+
+require_once("master.php");
+require_once("SQLiteEngine.php");
+
+class Utils
+{
+    public static function SetCommonParams($paramSet, $db)
+    {
+        $vm = new SqliteVM($db);
+        try
+        {
+            $arrayParam = array();
+            $vm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"OPERATION\"");
+            $arrayParam["OPERATION"]=$vm->GetString("ParamValue");
+
+            $vm->Execute("Select ParamValue from CommonParams WHERE ParamName=\"VERSION\"");
+            $arrayParam["VERSION"]=$vm->GetString("ParamValue");
+
+            $vm->Execute("Select ParamValue from CommonParams WHERE ParamName=\"CREDENTIALS\"");
+            $arrayParam["CREDENTIALS"]=$vm->GetString("ParamValue");
+
+            $vm->Execute("Select ParamValue from CommonParams WHERE ParamName=\"LOCALE\"");
+            $arrayParam["LOCALE"]=$vm->GetString("ParamValue");
+
+            unset($vm);
+            return $arrayParam;
+        }
+
+        catch (MgException $e)
+        {
+            print ($e->GetExceptionMessage("en"));
+        }
+        catch (SqliteException $s)
+        {
+            try
+            {
+                unset($vm);
+                $vm = new SqliteVM($db, true);
+                $vm->Execute("Select ParamValue from Params WHERE ParamName=\"VERSION\"");
+                $arrayParam["VERSION"]=$vm->GetString("ParamValue");
+
+                $vm->Execute("Select ParamValue from Params WHERE ParamName=\"CREDENTIALS\"");
+                $arrayParam["CREDENTIALS"]=$vm->GetString("ParamValue");
+
+                $vm->Execute("Select ParamValue from Params WHERE ParamName=\"LOCALE\"");
+                $arrayParam["LOCALE"]=$vm->GetString("ParamValue");
+                return $arrayParam;
+            }
+            catch (SqliteException $sq)
+            {
+                print ($sq->GetExceptionMessage("eng"));
+            }
+        }
+    }
+
+    public static function IsWindows() {
+        return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
+    }
+
+    private static function GetAbsolutePath($path, $encoding = "UTF-8") {
+        // Attempt to avoid path encoding problems.
+        $path = iconv($encoding, "$encoding//IGNORE//TRANSLIT", $path);
+ 
+        // Process the components
+        $parts = explode('/', $path);
+        $safe = array();  
+        foreach ($parts as $idx => $part) {
+    
+            if (($idx > 0 && trim($part)=="") || $part=='.') {
+                continue;
+            } elseif ('..' == $part) {
+                array_pop($safe);
+                continue;
+            } else {
+                $safe[] = $part;
+            }
+        }
+  
+        // Return the "clean" path
+        $path = implode(DIRECTORY_SEPARATOR, $safe);
+        return $path;
+    }
+
+    public static function GetPath($fileName)
+    {
+        if (strpos($fileName, "/") === 0)
+        {
+            //echo "Utils::GetPath($fileName) - $fileName\n";
+            return self::GetAbsolutePath($fileName);
+        }
+        $pos = strrpos($fileName, ':');
+        if($pos)
+        {
+            //echo "Utils::GetPath($fileName) - $fileName\n";
+            return $fileName;
+        }
+
+        $relPath = __FILE__;
+        $pos = strrpos($relPath, '\\');
+
+        if ($pos == false)
+        {
+            $pos = strrpos($relPath, '/');
+        }
+        $relPath = substr_replace($relPath, $fileName, $pos+1);
+
+        //Replace the back slashes in the path with forward slashes for Linux compatibility
+        $relPath = str_replace("\\", "/", $relPath);
+        $relPath = self::GetAbsolutePath($relPath);
+        //echo "Utils::GetPath($fileName) - $relPath\n";
+        return $relPath;
+    }
+
+    public static function MapAgentInit($webConfigIniPath)
+    {
+        $cfgFile = self::GetPath($webConfigIniPath);
+        //echo "Initializing from $cfgFile\n\n";
+        MgInitializeWebTier($cfgFile);
+    }
+
+    public static function GetResponse($byteReader)
+    {
+        try
+        {
+            $result = new Result();
+            $responseContent="";
+            $content = "";
+
+            if($byteReader != null)
+            {
+                $result->SetContentType($byteReader->GetMimeType());
+                while( ($byteReader->Read($content, 1024)) > 0)
+                {
+                    $responseContent .=$content;
+                }
+                $result->SetResultData($responseContent);
+            }
+            return $result;
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    //Takes a comma separated string and returns an object of MgStringCollection
+    public static function StringToMgStringCollection($string)
+    {
+        $mgCollection = new MgStringCollection();
+        $stringArray = explode(",", $string);
+
+        for ($i=0; $i<count($stringArray); $i++)
+        {
+            $mgCollection->Add($stringArray[$i]);
+        }
+        return $mgCollection;
+    }
+
+    //Takes a MgStringCollection objects and returns a comma separated string
+    public static function MgStringCollectionToString($mgCollection)
+    {
+        try
+        {
+            $string="";
+            $count = $mgCollection->GetCount();
+            $items = array();
+            for ($i=0; $i<$count; $i++)
+            {
+                array_push($items, $mgCollection->GetItem($i));
+            }
+            sort($items);
+
+            for ($i=0; $i<$count; $i++)
+            {
+                $string .= $items[$i].",";
+            }
+
+            return $string;
+        }
+        catch (MgException $e)
+        {
+            return $e->GetExceptionMessage();
+        }
+    }
+
+    //Takes a boolean value and returns its string representation
+    public static function BooleanToString($bval)
+    {
+        return $bval? "True": "False";
+    }
+
+    //Takes a MgEnvelope object and returns its string representation
+    public static function MgEnvelopeToString($env)
+    {
+        try
+        {
+            $ll = $env->GetLowerLeftCoordinate();
+            $ur = $env->GetUpperRightCoordinate();
+
+            return "(" . $ll->GetX() . ":" . $ll->GetY() . ")-(" . $ur->GetX() . ":" . $ur->GetY() . ")";
+        }
+        catch (MgException $e)
+        {
+            return $e->GetExceptionMessage();
+        }
+
+    }
+
+    //Takes a MgPoint object and returns its string representation
+    public static function MgPointToString($env)
+    {
+        try
+        {
+            $coordinate = $env->GetCoordinate();
+            return "(" . $coordinate->GetX() . ":" . $coordinate->GetY() . ")";
+        }
+        catch (MgException $e)
+        {
+            return $e->GetExceptionMessage();
+        }
+
+
+    }
+
+    public static function GetDbPath($dumpFileName)
+    {
+        $db = new SqliteDB();
+        $dbPath = substr($dumpFileName, 0, strpos($dumpFileName, ".dump")).".db";
+        $dbName = Utils::GetPath($dbPath);
+        $dumpFileName = Utils::GetPath($dumpFileName);
+
+        //Clear the stat cache as filemtime may not work correctly
+        clearstatcache();
+        //If we do not have neither a dump file nor a database file we are stuck.
+        if ( (!file_exists($dumpFileName)) && (!file_exists($dbName)))
+        {
+            printf("<b>Error: Dump file <i>%s</i> not found. Unable to create database file</b><br />", $dumpFileName);
+        }
+        //If the database file is missing but we have a dump file then create a new database
+        elseif (!file_exists($dbName))
+        {
+            $db->GenerateDatabase($dumpFileName, $dbName);
+        }
+        //If we have both files but the database file has an older time stamp than the dump file then create a new database
+        elseif ((file_exists($dumpFileName) && filemtime($dumpFileName)>filemtime($dbName)))
+        {
+            //Try to delete the database and if you cannot than do not create a new database as this will result in duplicate records
+            if (@unlink($dbName))
+            {
+                $db->GenerateDatabase($dumpFileName, $dbName);
+            }
+            else
+            {
+                printf("<b>Unable to delete database file <i>%s</i>. The file is either in use or is read only.</b><br />", $dbName);
+                print("<b>The database has not been updated!</b><br />");
+            }
+        }
+
+        return $dbPath;
+    }
+    
+    public static function CreateDumpFile($db, $overwrite = false)
+    {
+        $dbName = $db->GetName();
+        $iniFileName = substr($dbName, 0, strpos($dbName, ".db")).".ini";
+        $dumpFileName = substr($dbName, 0, strpos($dbName, ".db")).".dump";
+
+        if ($overwrite)
+        {
+            if (file_exists($dumpFileName))
+                unlink($dumpFileName);
+            
+            $iniFileContent =".output ".$dumpFileName;
+            file_put_contents($iniFileName, $iniFileContent);
+            $db->DumpDatabase($iniFileName);
+            if (php_sapi_name() == 'cli')
+                printf("Updated dump file: %s\n", $dumpFileName);
+            else
+                printf("<b>Updated dump file <i>%s</i></b>", $dumpFileName);
+            unlink($iniFileName);
+        }
+        else
+        {
+            //Clear the stat cache as filemtime may not work correctly
+            clearstatcache();
+
+            //Check if the file exists and is writable before updating it
+            if (!file_exists($dumpFileName) || (is_writable($dumpFileName)&&filemtime($db->GetName())>filemtime($dumpFileName)))
+            {
+                $iniFileContent =".output ".$dumpFileName;
+                file_put_contents($iniFileName, $iniFileContent);
+                $db->DumpDatabase($iniFileName);
+                if (php_sapi_name() == 'cli')
+                    printf("Updated dump file: %s\n", $dumpFileName);
+                else
+                    printf("<b>Updated dump file <i>%s</i></b>", $dumpFileName);
+                unlink($iniFileName);
+            }
+            else
+            {
+                if (php_sapi_name() == 'cli')
+                    printf("Dump file %s already exists and is read only or is newer than the database. File has not been updated\n", $dumpFileName);
+                else
+                    printf("<b>Dump file <i>%s</i> already exists and is read only or is newer than the database. File has not been updated</b>", $dumpFileName);
+            }
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Validate.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Validate.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/Validate.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,426 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+
+//Class that performs the validation for each operation
+
+require_once("Result.php");
+require_once("HtmlPrinter.php");
+require_once("ValidateUtils.php");
+
+class Validate
+{
+    private $vm;
+
+    public function __construct($db)
+    {
+        $this->vm = new SqliteVM($db, true);
+    }
+
+    public function __destruct()
+    {
+        unset($this->vm);
+    }
+
+    public function ValidateApiRequest($serviceType, $paramSet, $operation, $actualResult, $file)
+    {
+        //Default values for the outcome
+        $exitStatus=0;
+        $outcome="pass";
+
+        //If we have an exception we need to remove the stack trace because different line numbers will fail the test
+        $resultData = ValidateUtils::RemoveStackTrace($actualResult->GetResultData());
+
+        //Get the mime type based on the content type in the result
+        $mimeType = $actualResult->GetContentType();
+
+        //If we have exception message we need to remove any parts that may contain system dependent information
+        //Ex. file paths
+        $resultData = ValidateUtils::ProcessExceptionMessage($resultData);
+
+        //Get the file extension that will be used for a dump
+        $actualExtension = ValidateUtils::GetExtension($mimeType);
+
+        $bAlwaysPass = false;
+        $passReason = "";
+        //If we have an ALWAYSPASS parameter defined for the operation then skip the whole validation process
+        //This parameter should only be used for clean up operations that are no related with the tests
+        if ($this->vm->Execute("Select ParamValue from Params where ParamName=\"ALWAYSPASS\" and ParamSet=$paramSet")!=SQLITE_ROW)
+        {
+            //Form a file name that is going to be used to write results to a file
+            $type=substr($serviceType, 0, strpos($serviceType, "_"));
+            $filePath = Utils::GetPath("../../TestData/".$type."/DumpFiles/".$type."ApiTest");
+            $fileName = $filePath."_".$paramSet.".".$actualExtension;
+
+            if ($_POST['testExecutionMode'] == "dump")
+            {
+                file_put_contents($fileName, $resultData);
+            }
+            else
+            {
+                //This section is special case handling for the operations that return different data after each call
+                $resultData=ValidateUtils::SpecialDataHandling($operation, $resultData, $mimeType);
+
+                if ($_POST['testExecutionMode'] == "generate" && array_key_exists($paramSet, $_POST))
+                {
+                    //Get the sample result that is stored in the database. If we are using file on disk for validation
+                    //then do not overwrite the filename in the database
+                    //To distinguish between sample data and filename all filenames should be prefixed with "@@"
+                    $status = $this->vm->Execute("Select Result from ApiTestResults where ParamSet=$paramSet;");
+                    $sampleResult = $this->vm->GetString("Result");
+
+                    if ("@@"!=substr($sampleResult, 0, 2))
+                    {
+                        $responseBody="";
+                        //Insert the sample data as a BLOB
+                        //Update the row for that param set or create a new row if we do not have it yet
+                        if ($status == SQLITE_ROW)
+                        {
+                            $this->vm->Prepare("update ApiTestResults set Result = :blob where ParamSet=$paramSet;");
+                        }
+                        else
+                        {
+                            printf ("<b>A new row has been created in ApiTestResults table to store the result for operation %s</b><br />", $paramSet);
+                            print ("<b>Please, update the Description field for that row later</b><br /><br />");
+                            $this->vm->Prepare("INSERT INTO ApiTestResults(ParamSet, Result) VALUES($paramSet, :blob);");
+                        }
+                        $this->vm->BindBlob(":blob", $resultData, mb_strlen($resultData, "latin1"));
+                        $this->vm->Execute();
+
+                        //Update the mime type in the database if available
+                        if ($mimeType != null)
+                        {
+                            $this->vm->Execute("UPDATE ApiTestResults SET ContentType=\"$mimeType\" WHERE ParamSet=$paramSet;");
+                        }
+                        //To ensure that the data generated in the database is correct, output the result in a file
+                        //Check the file contents to ensure that this is the data that you to be stored in the database
+                        file_put_contents($fileName, $resultData);
+                    }
+                }
+                elseif ($_POST['testExecutionMode'] == "validate" || $_POST['testExecutionMode'] == "show")
+                {
+                    $expectedResult="";
+                    $resultContent="";
+
+                    //Get the sample result and the expected content type from the database
+                    $this->vm->Execute("Select Result, ContentType from ApiTestResults where ParamSet=$paramSet");
+                    $byteReader = $this->vm->GetBlob("Result");
+                    $expectedExtension = ValidateUtils::GetExtension($this->vm->GetString("ContentType"));
+
+                    while ($byteReader->Read($resultContent, 1024)>0)
+                    {
+                        $expectedResult.=$resultContent;
+                    }
+
+                    //If we are validating from a file then get the contents of that file
+                    //File names should be prefixed with "@@" to distinguish them from BLOB data
+                    if ("@@"==substr($expectedResult, 0, 2))
+                    {
+                        //Remove "@@" from the filename. Get the path to the file and pump the contents in a variable
+                        $sampleDataFile=substr($expectedResult, 2);
+                        $sampleDataFile=Utils::GetPath($sampleDataFile);
+                        $expectedResult=file_get_contents($sampleDataFile);
+                    }
+
+                    if ($_POST['testExecutionMode'] == "validate")
+                    {
+                        //Normalize line endings so comparisons don't fall over due to incorrect line endings when expected results were entered in SQLite GUI
+                        $normResultData = str_replace("\r\n", "\n", $resultData);
+                        $normExpectedResult = str_replace("\r\n", "\n", $expectedResult);
+                    
+                        //If the results are different and special validation fails then the operation failed ->mark it red
+                        if (strncasecmp($normResultData, $normExpectedResult, strlen($normResultData . $normExpectedResult)) && !(ValidateUtils::SpecialValidation($operation, $resultData, $expectedResult)))
+                        {
+                            $outcome="fail";
+                            $exitStatus=1;
+
+                            if (($expectedExtension!="xml") && ($expectedExtension!="html") && ($expectedExtension!="txt"))
+                            {
+                                $expectedResult= "Unable to display binary data";
+                            }
+
+                            if (($actualExtension!="xml") && ($actualExtension!="html") && ($actualExtension!="txt"))
+                            {
+                                $resultData="Unable to display binary data";
+                            }
+                        }
+
+                    }
+                    else
+                    {
+                        $type=substr($serviceType, 0, strpos($serviceType, "_"));
+                        $showPath = Utils::GetPath("../../TestData/".$type."/ShowFiles/".$type."ApiTest");
+                        $showName = $showPath."_".$paramSet.".".$actualExtension;
+                        file_put_contents($showName, $expectedResult);
+                    }
+                }
+            }
+        }
+        else
+        {
+            $bAlwaysPass = true;
+            $passReason = $this->vm->GetString("ParamValue");
+        }
+
+        if ($_POST['output']=="html")
+        {
+            if ($outcome=="fail")
+            {
+                HtmlPrinter::AddResultRow($operation, $outcome, $paramSet, $resultData, $expectedResult);
+                $str = sprintf("\n****ACTUAL RESULT****\n%s\n****EXPECTED RESULT****\n%s\n********\n\n\n", $resultData, $expectedResult);
+                fwrite($file, $str);
+            }
+            else
+            {
+                HtmlPrinter::AddResultRow($operation, $outcome, $paramSet, $resultData, $expectedResult);
+                //$str = sprintf("\n****ACTUAL RESULT****\n%s\n****EXPECTED RESULT****\n%s\n********\n\n\n", $resultData, $expectedResult);
+                //fwrite($file, $str);
+            }
+        }
+        else
+        {
+            if ($outcome=="fail")
+            {
+                printf("****".$serviceType." ".$paramSet." ".$operation." failed.\n");
+                $str = sprintf("\n****ACTUAL RESULT (%d)****\n%s\n****EXPECTED RESULT (%d)****\n%s\n********\n\n\n", strlen($resultData), $resultData, strlen($expectedResult), $expectedResult);
+                echo $str;
+                fwrite($file, $str);
+            }
+            else
+            {
+                /*
+                if ($bAlwaysPass)
+                {
+                    echo "Test (ParamSet: $paramSet) set to ALWAYSPASS: ".$passReason;
+                    if (php_sapi_name() == 'cli')
+                        echo "\n";
+                    else
+                        echo "<br/>";
+                }
+                */
+                //$str = sprintf("\n****ACTUAL RESULT****\n%s\n****EXPECTED RESULT****\n%s\n********\n\n\n", $resultData, $expectedResult);
+                //fwrite($file, $str);
+            }
+        }
+        return $exitStatus;
+    }
+
+    public function ValidateHttpRequest($serviceType, $paramSet, $operation, $actualResult, $file)
+    {
+        //Default values for outcome
+        $exitStatus=0;
+        $outcome="pass";
+
+        //Get the content type of the result
+        $contentType = $actualResult->GetContentType();
+        //We need to remove the stack trace if an exception is thrown because the different line numbers may fail the test
+        $resultData = ValidateUtils::RemoveStackTrace($actualResult->GetResultData());
+
+        //If we have exception message we need to remove any parts that may contain system dependent information
+        //Ex. file paths
+        $resultData = ValidateUtils::ProcessExceptionMessage($resultData);
+
+        //Get the extension for the dump file based on the content type of the result
+        $actualExtension = ValidateUtils::GetExtension($contentType);
+
+        $bAlwaysPass = false;
+        $passReason = "";
+        //If ALWAYSPASS parameter is defined the the whole validation is skipped.
+        //This parameter should only be used for clean up operations that are no related to the test
+        if ($this->vm->Execute("Select ParamValue from Params where ParamName=\"ALWAYSPASS\" and ParamSet=$paramSet")!=SQLITE_ROW)
+        {
+            //Form a file name that is going to be used for the outputting results to a file
+            $type=substr($serviceType, 0, strpos($serviceType, "_"));
+            $dumpFilePath = Utils::GetPath("../../TestData/".$type."/DumpFiles/");
+            $filePath = Utils::GetPath("../../TestData/".$type."/DumpFiles/".$type."HttpTest");
+            $fileName = $filePath."_".$paramSet.".".$actualExtension;
+
+            if ($_POST['testExecutionMode'] == "dump")
+            {
+                //if the folder "DumpFiles" is not exist, then user need to create the folder themself
+                //or the "dump" operation fail, so add these code to create the folder automatically
+                if(!file_exists($dumpFilePath))
+                {
+                    mkdir($dumpFilePath);
+                }
+
+                file_put_contents($fileName, "$resultData");
+            }
+            else
+            {             
+                //This section is special case handling for the operations that return different data after each call
+                $resultData=ValidateUtils::SpecialDataHandling($operation, $resultData, $contentType);
+
+                if ($_POST['testExecutionMode'] == "generate" && array_key_exists($paramSet,$_POST))
+                {
+                    //Get the sample result that is stored in the database. If we are using file on disk for validation
+                    //then do not overwrite the filename in the database
+                    //To distinguish between sample data and filename all filenames should be prefixed with "@@"
+                    $status = $this->vm->Execute("Select Result from HttpTestResults where ParamSet=$paramSet;");
+                    
+                    //Get the sample data from the database
+                    $this->vm->Execute("Select Result from HttpTestResults where ParamSet = $paramSet");
+                    //When use the GetString to get the result which is actually BlOB will cause exception
+                    //unfortunately, this expection can not be caught by the php engine, cause it is not legal php exception
+                    //maybe it is a problem of the sqlite in mapguide, so use GetBlob here, if the sqlite fix this problem
+                    //we can use try catch
+                    $byteReader = $this->vm->GetBlob("Result");
+                    $sampleResult;  
+                    
+                    while ($byteReader->Read($resultContent, 1024)>0)
+                    {
+                        $sampleResult.=$resultContent;
+                    }
+
+                    if ("@@"!=substr($sampleResult, 0, 2))
+                    {
+                        $responseBody="";
+
+                        //Store the result as a BLOB in the database
+                        //Update the row for that param set or create a new row if we do not have it yet
+                        if ($status == SQLITE_ROW)
+                        {
+                            $this->vm->Prepare("update HttpTestResults set Result = :blob where ParamSet=$paramSet;");
+                        }
+                        else
+                        {
+                            printf ("<b>A new row has been created in HttpTestResults table to store the result for operation %s</b><br />", $paramSet);
+                            print ("<b>Please, update the Description field for that row later</b><br /><br />");
+                            $this->vm->Prepare("INSERT INTO HttpTestResults(ParamSet, Result) VALUES($paramSet, :blob);");
+                        }
+                        $this->vm->BindBlob(":blob", $resultData, strlen($resultData));
+                        $this->vm->Execute();
+
+                        if ($contentType != null)
+                        {
+                            $this->vm->Execute("UPDATE HttpTestResults SET ContentType=\"$contentType\" WHERE ParamSet=$paramSet;");
+                        }
+                        //Write the result to a file to ensure the correct data is populated in the database
+                        file_put_contents($fileName, $resultData);
+                    }
+                }
+                elseif ($_POST['testExecutionMode'] == "validate" || $_POST['testExecutionMode'] == "show")
+                {
+                    $expectedResult="";
+                    $resultContent="";
+
+                    //Get the sample data from the database
+                    $this->vm->Execute("Select Result, ContentType from HttpTestResults where ParamSet=$paramSet");
+                    $byteReader = $this->vm->GetBlob("Result");
+                    $expectedExtension = ValidateUtils::GetExtension($this->vm->GetString("ContentType"));
+
+                    while ($byteReader->Read($resultContent, 1024)>0)
+                    {
+                        $expectedResult.=$resultContent;
+                    }
+
+                    //If we are validating from a file then get the contents of that file
+                    //File names should be prefixed with "@@" to distinguish them from BLOB data
+                    if ("@@"==substr($expectedResult, 0, 2))
+                    {
+                        //Remove "@@" from the filename. Get the path to the file and pump the contents in a variable
+                        $sampleDataFile=substr($expectedResult, 2);
+                        $sampleDataFile=Utils::GetPath($expectedResult);
+                        $expectedResult=file_get_contents($sampleDataFile);
+                    }
+
+
+                    if ($_POST['testExecutionMode'] == "validate")
+                    {
+                        //Normalize line endings. Don't want stray CRLFs derailing verification
+                        $resultData = str_replace("\r\n", "\n", $resultData);
+                        $expectedResult = str_replace("\r\n", "\n", $expectedResult);
+
+                        //If the results are different and special validation fails then then the operation failed ->mark it red
+                        if ( strncasecmp($resultData, $expectedResult, strlen($resultData.$expectedResult)) && !(ValidateUtils::SpecialValidation($operation, $resultData, $expectedResult)))
+                        {
+                            $outcome="fail";
+                            $exitStatus=1;
+
+                            if (($expectedExtension!="xml") && ($expectedExtension!="html") && ($expectedExtension!="txt"))
+                            {
+                                $expectedResult= "Unable to display binary data";
+                            }
+
+                            if (($actualExtension!="xml") && ($actualExtension!="html") && ($actualExtension!="txt"))
+                            {
+                                $resultData="Unable to display binary data";
+                            }
+                        }
+                    }
+                    else
+                    {
+                        $type=substr($serviceType, 0, strpos($serviceType, "_"));
+                        $showPath = Utils::GetPath("../../TestData/".$type."/ShowFiles/".$type."HttpTest");
+                        $showName = $showPath."_".$paramSet.".".$actualExtension;
+                        file_put_contents($showName, $expectedResult);
+                    }
+                }
+            }
+        }
+        else
+        {
+            $bAlwaysPass = true;
+            $passReason = $this->vm->GetString("ParamValue");
+        }
+
+        if ($_POST['output']=="html")
+        {
+            if ($outcome=="fail")
+            {
+                HtmlPrinter::AddResultRow($operation, $outcome, $paramSet, $resultData, $expectedResult);
+                $str = sprintf("\n****ACTUAL RESULT****\n%s\n****EXPECTED RESULT****\n%s\n********\n\n\n", $resultData, $expectedResult);
+                fwrite($file, $str);
+            }
+            else
+            {
+                HtmlPrinter::AddResultRow($operation, $outcome, $paramSet);
+                //$str = sprintf("\n****ACTUAL RESULT****\n%s\n****EXPECTED RESULT****\n%s\n********\n\n\n", $resultData, $expectedResult);
+                //fwrite($file, $str);
+            }
+        }
+        else
+        {
+            if ($outcome=="fail")
+            {
+                printf("****".$serviceType." ".$paramSet." ".$operation." failed.\n");
+                $str = sprintf("\n****ACTUAL RESULT (%d)****\n%s\n****EXPECTED RESULT (%d)****\n%s\n********\n\n\n", strlen($resultData), $resultData, strlen($expectedResult), $expectedResult);
+                echo $str;
+                fwrite($file, $str);
+            }
+            else
+            {
+                /*
+                if ($bAlwaysPass)
+                {
+                    echo "Test (ParamSet: $paramSet) set to ALWAYSPASS: ".$passReason;
+                    if (php_sapi_name() == 'cli')
+                        echo "\n";
+                    else
+                        echo "<br/>";
+                }
+                */
+                //$str = sprintf("\n****ACTUAL RESULT****\n%s\n****EXPECTED RESULT****\n%s\n********\n\n\n", $resultData, $expectedResult);
+                //fwrite($file, $str);
+            }
+        }
+        return $exitStatus;
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ValidateUtils.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ValidateUtils.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/ValidateUtils.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,333 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+
+//Class that defines helper methods used in the validation process.
+
+class ValidateUtils
+{
+    public static function RemoveStackTrace($result)
+    {
+        $cleanResult = $result;
+        $start = strpos($result, "<h2>");
+        if ($start)
+        {
+            $start += strlen("<h2>");
+            $end = strpos($result, "</h2>");
+            $cleanResult = substr($result, $start, $end-$start);
+        }
+        return $cleanResult;
+    }
+    
+    public static function RemoveStackTraceFromOgcException($result)
+    {
+        $cleanResult = $result;
+        $start = strpos($result, "<details>");
+        if ($start)
+        {
+            $start += strlen("<details>");
+            $before = substr($result, 0, $start);
+            $upToExceptionStart = strpos($result, "\n", $start);
+            
+            $cleanResult = substr($result, 0, $upToExceptionStart);
+            $detailsEnd = strpos($result, "</details>");
+            $after = substr($result, $detailsEnd);
+            //echo "Current: $result\n";
+            //echo "Before: $before\n";
+            //echo "After: $after\n";
+            $cleanResult .= $after;
+        }
+        return $cleanResult;
+    }
+
+    public static function GetExtension($mimeType)
+    {
+        $extension="xml";
+
+        if ($mimeType == "application/agf")
+        {
+            $extension="agf";
+        }
+        elseif ($mimeType == "application/octet-stream")
+        {
+            //TODO: what extension should we put for binary data
+            $extension="bin";
+        }
+        elseif ($mimeType == "model/vnd.dwf")
+        {
+            $extension = "dwf";
+        }
+        elseif (strstr($mimeType, "ePlot") != FALSE)
+        {
+            $extension = "dwf";
+        }
+        elseif ($mimeType == "image/jpeg")
+        {
+            $extension = "jpg";
+        }
+        elseif ($mimeType == "image/png")
+        {
+            $extension = "png";
+        }
+        elseif (strstr($mimeType,"text/plain") != FALSE)
+        {
+            $extension = "txt";
+        }
+        elseif ($mimeType == "image/tiff")
+        {
+            $extension = "tiff";
+        }
+        elseif (strstr($mimeType,"text/html") != FALSE)
+        {
+            $extension = "html";
+        }
+        elseif ($mimeType == "application/x-w2d")
+        {
+            $extension = "dwf";
+        }
+
+        return $extension;
+    }
+
+    public static function RemoveTimeStamp($result)
+    {
+        $newResult=$result;
+
+        while (substr_count($result,"<CreatedDate>")>0)
+        {
+            $newResult=substr($result, 0, strpos($result, "<CreatedDate>"));
+            $newResult.=substr($result, strpos($result, "</ModifiedDate>")+strlen("</ModifiedDate>"));
+            $result=$newResult;
+        }
+        return $newResult;
+    }
+
+    public static function RemoveLogEntryTimeStamp($result)
+    {
+        $newResult = $result;
+
+        while (substr_count($result, "<") > 0)
+        {
+            $newResult = substr($result, 0, strpos($result, "<"));
+            $newResult.= substr($result, strpos($result, ">")+1);
+            $result = $newResult;
+        }
+
+        return $newResult;
+    }
+
+    public static function RemoveDwfSectionName($actualResult)
+    {
+        $newResult = substr($actualResult, strpos($actualResult, ".w2d"));
+        if (0!=strpos($newResult, "EndOfDWF"))
+        {
+            $newResult = substr($newResult, 0, strpos($newResult, "EndOfDWF"));
+        }
+        return $newResult;
+    }
+
+    public static function StripFdoVersionFromProvider($actualResult)
+    {
+        $start = strpos($actualResult, "<ProviderName>");
+        $end = strpos($actualResult, "</ProviderName>");
+
+        $tagLen = strlen("<ProviderName>");
+
+        $mid = substr($actualResult, $start + $tagLen, $end - $start - $tagLen);
+        $tokens = explode(".", $mid);
+
+        if (count($tokens) >= 2)
+        {
+            $before = substr($actualResult, 0, $start + $tagLen);
+            $after = substr($actualResult, $end);
+            return $before.$tokens[0].".".$tokens[1].$after;
+        }
+        return $actualResult;        
+    }
+
+    public static function SpecialDataHandling($operation, $resultData, $contentType)
+    {
+        if ( $operation == "ENUMERATERESOURCES")
+        {
+            $resultData = self::RemoveTimeStamp($resultData);
+        }
+        elseif ($operation == "GETDRAWINGLAYER")
+        {
+            $resultData = self::RemoveDwfSectionName($resultData);
+        }
+        elseif ($operation == "GETDRAWINGSECTION")
+        {
+            $resultData = self::RemoveDwfSectionName($resultData);
+        }
+        elseif ($operation == "GETLOG")
+        {
+            $resultData = self::RemoveLogEntryTimeStamp($resultData);
+        }
+        elseif ($operation == "GETMAP")
+        {
+            $resultData = self::GetMapHeader($resultData);
+        }
+        elseif ($operation == "GETLONGTRANSACTIONS")
+        {
+            $resultData = self::RemoveCreationDate($resultData);
+        }
+        elseif ($operation == "WmsGetMap")
+        {
+            $resultData = self::RemoveStackTraceFromOgcException($resultData);
+        }
+        elseif ($operation == "GETSPATIALCONTEXTS")
+        {
+            $resultData = self::StripFdoVersionFromProvider($resultData);
+        }
+        
+        if (strstr($contentType,"text/xml") != FALSE)
+        {
+            $doc = new DOMDocument();
+            $doc->preserveWhiteSpace = true;
+            if (strlen($resultData) > 0 && $doc->loadXML($resultData))
+            {
+                $resultData = self::SortElement($doc, '');
+            }
+        }
+        return $resultData;
+    }
+
+    //Does validation for operation that do not allow straight string comparison between expected and actual data
+    public static function SpecialValidation($operation, &$actualData, $expectedData)
+    {
+        $success = false;
+
+        if ($operation == "GETFEATUREPROVIDERS")
+        {
+            $success = self::GetFeatureProvidersValidation($actualData, $expectedData);
+        }
+        return $success;
+    }
+    //Hack...returns the part of the exception from the beginning up to and including "exception occurred"
+    public static function ProcessExceptionMessage($resultData)
+    {
+        if (stripos($resultData, "exception occurred"))
+        {
+            $length = stripos($resultData, "exception occurred")+strlen("exception occurred");
+            $resultData = substr($resultData, 0,  $length);
+        }
+        return $resultData;
+    }
+
+    //Check if we have DWF 6.01 in the header. For now this is the only way to validate maps
+    public static function GetMapHeader($resultData)
+    {
+        if (substr_count($resultData, "(DWF V06.01)")>0)
+        {
+            $resultData = "(DWF V06.01)";
+        }
+
+        return $resultData;
+    }
+
+    //The expectedData is comma separated list of all providers.
+    //The function checks if all the providers in the list are in the $actualData document
+    //Overwrites the $actualData with an error message to provide some feedback to the user in case of failure
+    public static function GetFeatureProvidersValidation(&$actualData, $expectedData)
+    {
+        $pass = true;
+        $errMsg = "";
+        $providers = explode(",", $expectedData);
+
+        foreach ($providers as $providerName)
+        {
+            if (substr_count($actualData, $providerName)==0)
+            {
+                $pass = false;
+                $errMsg .= sprintf("Provider not found: %s \n",$providerName);
+            }
+        }
+        $actualData = $errMsg;
+        return $pass;
+    }
+
+    public static function RemoveCreationDate($resultData)
+    {
+        $newResult=$resultData;
+
+        while (substr_count($resultData,"<CreationDate>")>0)
+        {
+            $newResult=substr($resultData, 0, strpos($resultData, "<CreationDate>"));
+            $newResult.=substr($resultData, strpos($resultData, "</CreationDate>")+strlen("</CreationDate>"));
+            $resultData=$newResult;
+        }
+        return $newResult;
+    }
+
+    public static function SortElement($elem, $preText)
+    {
+        $elemArray = array();
+        $elemString = '';
+
+        if ($elem->hasChildNodes())
+        {
+            $children = $elem->childNodes;
+            for($i = 0; $i < $children->length; $i++)
+            {
+                $child = $children->item($i);
+                if ($child->nodeType == XML_ELEMENT_NODE)
+                {
+                    $elemValue =self::SortElement($child, $preText.'  ');
+                    if (strlen($elemValue) > 0)
+                    {
+                        array_push($elemArray, $elemValue);
+                    }
+                }
+                else if ($child->nodeType == XML_TEXT_NODE)
+                {
+                    $content = trim($child->textContent);
+                    if (strlen($content) > 0)
+                    {
+                        array_push($elemArray,$content);
+                    }
+                }
+            }
+            sort($elemArray);
+            foreach ($elemArray as $str)
+            {
+                $elemString .= $str;
+            }
+        }
+
+        $endTag = '';
+        if (sizeof($elemArray) > 1 && strlen($elemString) > 0)
+        {
+            $endTag = "\n".$preText;
+        }
+        $tagName = '';
+        if (is_a($elem,'DOMDocument') == false)
+        {
+            $tagName = $elem->tagName;
+        }
+        $endTag .= "</".$tagName.">";
+
+        if ("" != $tagName)
+        {
+            $elemString = "\n".$preText."<".$tagName.">".$elemString.$endTag;
+        }
+
+        return $elemString;
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/WebLayoutAPI.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/WebLayoutAPI.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/WebLayoutAPI.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,508 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("master.php");
+require_once("Result.php");
+require_once("Utils.php");
+
+class WebLayoutAPI
+{
+    private $cred;
+    private $resSrvc;
+    private $unitTestParamVm;
+    private $arrayParam;
+    private $site;
+    private $wl;
+
+    function __construct($db)
+    {
+        $wl = null;
+
+        Utils::MapAgentInit(WEBCONFIGINI);
+
+        $this->cred = new MgUserInformation();
+        $this->cred->SetMgUsernamePassword("Administrator","admin");
+        $this->cred->SetLocale("en");
+
+        $this->site = new MgSiteConnection();
+        $this->site->Open($this->cred);
+        $this->resSrvc = $this->site->CreateService(MgServiceType::ResourceService);
+
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->arrayParam = array();
+
+    }
+
+    function GetTitle($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+
+            return new Result($this->wl->GetTitle(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetMapDefinition($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+
+            return new Result($this->wl->GetMapDefinition(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetScale($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+
+            return new Result($this->wl->GetScale(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetCenter($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $center = $this->wl->GetCenter();
+
+            if($center == null)
+                return new Result("", "text/plain");
+            else
+                return new Result($center->GetCoordinate()->GetX() . "/" . $center->GetCoordinate()->GetY(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function ShowToolbar($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $toolBar = $this->wl->GetToolBar();
+
+            return new Result(Utils::BooleanToString($toolBar->IsVisible()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function ShowStatusbar($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $statusBar = $this->wl->GetStatusBar();
+
+            return new Result(Utils::BooleanToString($statusBar->IsVisible()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function ShowTaskpane($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskPane = $this->wl->GetTaskPane();
+
+            return new Result(Utils::BooleanToString($taskPane->IsVisible()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function ShowTaskbar($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskPane = $this->wl->GetTaskPane();
+
+            return new Result(Utils::BooleanToString($taskPane->GetTaskBar()->IsVisible()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function ShowLegend($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $infoPane = $this->wl->GetInformationPane();
+
+            return new Result(Utils::BooleanToString($infoPane->IsLegendBandVisible()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function ShowProperties($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $infoPane = $this->wl->GetInformationPane();
+
+            return new Result(Utils::BooleanToString($infoPane->IsPropertiesBandVisible()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetTaskPaneWidth($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskPane = $this->wl->GetTaskPane();
+
+            return new Result($taskPane->GetWidth(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetInformationPaneWidth($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $infoPane = $this->wl->GetInformationPane();
+
+            return new Result($infoPane->GetWidth(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function GetInitialTaskUrl($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskPane = $this->wl->GetTaskPane();
+
+            return new Result($taskPane->GetInitialTaskUrl(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function ShowContextMenu($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $ctxMenu = $this->wl->GetTaskPane();
+
+            return new Result(Utils::BooleanToString($ctxMenu->IsVisible()), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function TestWidget($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+
+            $coll = null;
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"CONTAINER\"");
+            $container = $this->unitTestParamVm->GetString("ParamValue");
+            if($container == "toolbar")
+                $coll = $this->wl->GetToolbar()->GetWidgets();
+            else if($container == "tasklist")
+                $coll = $this->wl->GetTaskPane()->GetTaskBar()->GetTaskList();
+            else if($container == "contextmenu")
+                $coll = $this->wl->GetContextMenu();
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"INDEX\"");
+            $index = intval($this->unitTestParamVm->GetString("ParamValue"));
+
+            $widget = $coll->GetWidget($index);
+            if($widget == null)
+                $str = "Null widget";
+            else
+                $str = $this->FormatProperties($widget);
+
+            return new Result($str, "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function HomeTooltip($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskBar = $this->wl->GetTaskPane()->GetTaskBar();
+            $home = $taskBar->GetTaskButtons()->GetWidget(MgWebTaskButtonType::Home);
+
+            return new Result($home->GetTooltip(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function HomeDescription($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskBar = $this->wl->GetTaskPane()->GetTaskBar();
+            $home = $taskBar->GetTaskButtons()->GetWidget(MgWebTaskButtonType::Home);
+
+            return new Result($home->GetDescription(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function BackTooltip($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskBar = $this->wl->GetTaskPane()->GetTaskBar();
+            $back = $taskBar->GetTaskButtons()->GetWidget(MgWebTaskButtonType::Back);
+
+            return new Result($back->GetTooltip(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function BackDescription($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskBar = $this->wl->GetTaskPane()->GetTaskBar();
+            $back = $taskBar->GetTaskButtons()->GetWidget(MgWebTaskButtonType::Back);
+
+            return new Result($back->GetDescription(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function ForwardTooltip($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskBar = $this->wl->GetTaskPane()->GetTaskBar();
+            $forward = $taskBar->GetTaskButtons()->GetWidget(MgWebTaskButtonType::Forward);
+
+            return new Result($forward->GetTooltip(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function ForwardDescription($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskBar = $this->wl->GetTaskPane()->GetTaskBar();
+            $forward = $taskBar->GetTaskButtons()->GetWidget(MgWebTaskButtonType::Forward);
+
+            return new Result($forward->GetDescription(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function TasksName($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskBar = $this->wl->GetTaskPane()->GetTaskBar();
+            $tasks = $taskBar->GetTaskButtons()->GetWidget(MgWebTaskButtonType::Tasks);
+
+            return new Result($tasks->GetName(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function TasksTooltip($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskBar = $this->wl->GetTaskPane()->GetTaskBar();
+            $tasks = $taskBar->GetTaskButtons()->GetWidget(MgWebTaskButtonType::Tasks);
+
+            return new Result($tasks->GetTooltip(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function TasksDescription($paramSet)
+    {
+        try
+        {
+            $this->CreateWebLayoutFromResource($paramSet);
+            $taskBar = $this->wl->GetTaskPane()->GetTaskBar();
+            $tasks = $taskBar->GetTaskButtons()->GetWidget(MgWebTaskButtonType::Tasks);
+
+            return new Result($tasks->GetDescription(), "text/plain");
+        }
+        catch (MgException $e)
+        {
+            return new Result(get_class($e), "text/plain");
+        }
+    }
+
+    function CreateWebLayoutFromResource($paramSet)
+    {
+        if($this->wl != null)
+            return;
+
+        $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"WEBLAYOUT\"");
+        $wlId = $this->unitTestParamVm->GetString("ParamValue");
+        if ($wlId == null || strlen($wlId) < 1)
+        {
+            $wlId = "Library://UnitTest/layouts/Test.WebLayout";
+        }
+
+        $resId = new MgResourceIdentifier($wlId);
+
+        $this->wl = new MgWebLayout($this->resSrvc,$resId);
+    }
+
+    function FormatProperties($it)
+    {
+        // recursively goes into subitems definition
+        //
+        switch($it->GetType())
+        {
+            case MgWebWidgetType::Separator:
+                $name = "";
+                break;
+            case MgWebWidgetType::Command:
+                $name = $it->GetCommand()->GetLabel();
+                break;
+            case MgWebWidgetType::Flyout:
+                $name = $it->GetLabel();
+                break;
+        }
+        $str = "[" . $name . "/" . $it->GetType();
+        if($it->GetType() == MgWebWidgetType::Separator)
+            return $str . "]";
+        else if($it->GetType() == MgWebWidgetType::Command)
+            return $str . "/" . $it->GetCommand()->GetName() . "]";
+        else if($it->GetType() == MgWebWidgetType::Flyout)
+        {
+            $str = $str . "/" . $it->GetIconUrl();
+            $coll = $it->GetSubItems();
+            for($i = 0; $i < $coll->GetCount(); $i++)
+            {
+                $str = $str . "/" . $this->FormatProperties($coll->GetWidget($i));
+            }
+            $str = $str . "]";
+        }
+        else
+            return "[** error **]";
+
+        return $str;
+    }
+
+    function __destruct()
+    {
+        unset($this->wl);
+        unset($this->resSrvc);
+        unset($this->site);
+        unset($this->cred);
+        unset($this->unitTestParamVm);
+        unset($this->arrayParam);
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/WfsHttpRequests.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/WfsHttpRequests.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/WfsHttpRequests.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,134 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("HttpRequest.php");
+require_once("Utils.php");
+
+//This file defines the operations that are currently supported by the unit test infrastructure for Drawing Service methods exposed through Htpp.
+//There is one function per operation. Each function tries to retrieve the parameters that are required for its corresponding
+//operation and send the Http request
+//Edit that file to add more operations from Drawing Service that are exposed through Http
+
+class WfsHttpRequests
+{
+    private $db;
+    private $unitTestParamVm;
+    private $URL;
+
+    function __construct($db, $newURL)
+    {
+        $this->db = $db;
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->httpRequest = new HttpRequest();
+        $this->URL = $newURL;
+    }
+
+    function __destruct()
+    {
+        unset($this->unitTestParamVm);
+        unset($this->httpRequest);
+        unset($this->db);
+    }
+
+    function GetCapabilities($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"request\"");
+            $arrayParam["request"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"service\"");
+            $arrayParam["service"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"VERSION\"");
+            $arrayParam["VERSION"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function DescribeFeatureType($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"request\"");
+            $arrayParam["request"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"service\"");
+            $arrayParam["service"]=$this->unitTestParamVm->GetString("ParamValue");
+            
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"version\"");
+            $arrayParam["version"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"typeName\"");
+            $arrayParam["typeName"]=$this->unitTestParamVm->GetString("ParamValue");
+                       
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+    function GetFeature($paramSet, $request="GET")
+    {
+        try
+        {
+            $arrayParam = array();
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"request\"");
+            $arrayParam["request"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"service\"");
+            $arrayParam["service"]=$this->unitTestParamVm->GetString("ParamValue");
+            
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"version\"");
+            $arrayParam["version"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"typeName\"");
+            $arrayParam["typeName"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            if($request=="POST")
+            {
+                return $this->httpRequest->SendRequest($this->URL, $arrayParam, "POST");
+            }
+            else
+            {
+                return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+            }
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/WmsHttpRequests.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/WmsHttpRequests.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/WmsHttpRequests.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,166 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("HttpRequest.php");
+require_once("Utils.php");
+
+//This file defines the operations that are currently supported by the unit test infrastructure for Drawing Service methods exposed through Htpp.
+//There is one function per operation. Each function tries to retrieve the parameters that are required for its corresponding
+//operation and send the Http request
+//Edit that file to add more operations from Drawing Service that are exposed through Http
+
+class WmsHttpRequests
+{
+    private $db;
+    private $unitTestParamVm;
+    private $URL;
+
+    function __construct($db, $newURL)
+    {
+        $this->db = $db;
+        $this->unitTestParamVm = new SqliteVM($db, true);
+        $this->httpRequest = new HttpRequest();
+        $this->URL = $newURL;
+    }
+
+    function __destruct()
+    {
+        unset($this->unitTestParamVm);
+        unset($this->httpRequest);
+        unset($this->db);
+    }
+
+    function GetCapabilities($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"request\"");
+            $arrayParam["request"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"service\"");
+            $arrayParam["service"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"VERSION\"");
+            $arrayParam["VERSION"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetMap($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"request\"");
+            $arrayParam["request"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"service\"");
+            $arrayParam["service"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"layers\"");
+            $arrayParam["layers"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"srs\"");
+            $arrayParam["srs"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"bbox\"");
+            $arrayParam["bbox"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"width\"");
+            $arrayParam["width"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"height\"");
+            $arrayParam["height"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"format\"");
+            $arrayParam["format"]=$this->unitTestParamVm->GetString("ParamValue");
+            
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"version\"");
+            $arrayParam["version"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+
+    function GetFeatureInfo($paramSet)
+    {
+        try
+        {
+            $arrayParam = array();
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"request\"");
+            $arrayParam["request"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"service\"");
+            $arrayParam["service"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"layers\"");
+            $arrayParam["layers"]=$this->unitTestParamVm->GetString("ParamValue");            
+            $arrayParam["query_layers"]=$this->unitTestParamVm->GetString("ParamValue");
+            
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"srs\"");
+            $arrayParam["srs"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"bbox\"");
+            $arrayParam["bbox"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"width\"");
+            $arrayParam["width"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"height\"");
+            $arrayParam["height"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"I\"");
+            $arrayParam["I"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"J\"");
+            $arrayParam["J"]=$this->unitTestParamVm->GetString("ParamValue");
+
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"format\"");
+            $arrayParam["format"]=$this->unitTestParamVm->GetString("ParamValue");
+            
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"version\"");
+            $arrayParam["version"]=$this->unitTestParamVm->GetString("ParamValue");
+            
+            $this->unitTestParamVm->Execute("Select ParamValue from Params WHERE ParamSet=$paramSet AND ParamName=\"info_format\"");
+            $arrayParam["info_format"]= $this->unitTestParamVm->GetString("ParamValue");
+
+            return $this->httpRequest->SendRequest($this->URL, $arrayParam);
+
+        }
+        catch (SqliteException $s)
+        {
+            return new Result($s->GetExceptionMessage(), "text/plain");
+        }
+    }
+}
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/createSdf.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/createSdf.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/createSdf.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,178 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("master.php");
+require_once("Utils.php");
+
+try
+{
+
+    Utils::MapAgentInit(WEBCONFIGINI);
+    $site = new MgSiteConnection();
+    $cred = new MgUserInformation();
+    $cred->SetMgUsernamePassword("Administrator","admin");
+    $cred->SetLocale("en");
+    $site->Open($cred);
+    $svc = $site->CreateService(MgServiceType::FeatureService);
+}
+catch (MgException $exc)
+{
+    echo $exc->GetExceptionMessage()."\n";
+    echo $exc->GetDetails()."\n";
+    return;
+}
+
+echo "Created Services\n";
+
+// Create class definition for new feature  class
+$classDef = new MgClassDefinition();
+
+$classDef->SetName("Buffer");
+$classDef->SetDescription("Feature class for buffer layer");
+$classDef->SetDefaultGeometryPropertyName("GEOM");
+
+// Set key property
+$prop = new MgDataPropertyDefinition("KEY");
+$prop->SetDataType(MgPropertyType::Int32);
+$prop->SetAutoGeneration(true);
+$prop->SetReadOnly(true);
+
+$classDef->GetIdentityProperties()->Add($prop);
+$classDef->GetProperties()->Add($prop);
+
+$prop = new MgDataPropertyDefinition("NAME");
+$prop->SetDataType(MgPropertyType::String);
+
+$classDef->GetProperties()->Add($prop);
+
+// Set geometry property
+$prop = new MgGeometricPropertyDefinition("GEOM");
+$prop->SetGeometryTypes(MgFeatureGeometricType::Surface);
+
+$classDef->GetProperties()->Add($prop);
+$schema = new MgFeatureSchema("BufferSchema", "Temporary buffer schema");
+$schema->GetClasses()->Add($classDef);
+
+$wkt = "LOCALCS[\"Non-Earth (Meter)\",LOCAL_DATUM[\"Local Datum\",0],UNIT[\"Meter\", 1],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH]]";
+$params = new MgCreateSdfParams("ArbitraryXY", $wkt, $schema);
+
+echo "Created sdfparams\n";
+
+$layerName = 'Library://TrevorWekel/NewSdf.FeatureSource';
+$id = new MgResourceIdentifier($layerName);
+
+try
+{
+
+    $svc->CreateFeatureSource($id, $params);
+}
+catch (MgException $exc)
+{
+    echo $exc->GetExceptionMessage()."\n";
+    echo $exc->GetDetails()."\n";
+    return;
+}
+
+echo "Created featuresource\n";
+
+// We need to add some data to the sdf before using it.  The spatial context
+// reader must have an extent.
+
+$batchProp = new MgBatchPropertyCollection();
+$wkt = new MgWktReaderWriter();
+$agf = new MgAgfReaderWriter();
+$fact = new MgGeometryFactory();
+
+echo "Created wkt/agf\n";
+
+$count = 100;
+$i = 0;
+for ($i=1; $i<=$count; $i++)
+{
+
+    $bufferProps = new MgPropertyCollection();
+    $nameProp = new MgStringProperty("NAME", "NewName_".$i);
+    $bufferProps->Add($nameProp);
+
+    $x = 120+$i/$count;
+    $y = 100+$i/$count;
+    //$wktText = "POLYGON ((20 20, 20 100, {$x} {$y}, 140 20, 20 20))";
+    //$geom = $wkt->Read($wktText);
+    $coord = $fact->CreateCoordinateXY($x, $y);
+    $geom = $fact->CreatePoint($coord);
+    $geomReader = $agf->Write($geom);
+    $geomProp = new MgGeometryProperty("GEOM", $geomReader);
+    $bufferProps->Add($geomProp);
+
+    $batchProp->Add($bufferProps);
+}
+
+echo "Created geometries via wkt\n";
+
+$cmd = new MgInsertFeatures("Buffer", $batchProp);
+$cmdColl = new MgFeatureCommandCollection();
+
+$cmdColl->Add($cmd);
+
+try
+{
+    $svc->CreateFeatureSource($id, $params);
+    $startTime = microtime(true);
+    $props = $svc->UpdateFeatures($id, $cmdColl, false);
+    $endTime = microtime(true);
+    $diffTime = $endTime - $startTime;
+    $reader = $props->GetItem(0)->GetValue();
+    while ($reader->ReadNext() == true)
+    {
+        $key = $reader->GetInt32("KEY");
+    }
+    $reader->Close();
+
+    // Now select a few of the feature and make sure we get everything back correctly.
+
+    $query = new MgFeatureQueryOptions();
+    $query->AddFeatureProperty("NAME");
+    $query->AddFeatureProperty("KEY");
+    $query->AddFeatureProperty("GEOM");
+    $query->SetFilter("");
+
+    $reader = $svc->SelectFeatures($id, "Buffer", $query);
+    while ($reader->ReadNext() == true)
+    {
+        $geomText = '';
+        $agfStream = $reader->GetGeometry("GEOM");
+        $geom = $agf->Read($agfStream);
+        $geomText = $wkt->Write($geom);
+        echo $reader->GetInt32("KEY")." ".$reader->GetString("NAME")." ".$geomText."\n";
+    }
+
+    echo "Time for ".$i." ops was ".$diffTime. " seconds\n";
+    echo "Average throughput of ".$i/$diffTime. " features per second\n";
+}
+catch (MgException $exc)
+{
+    echo "First failure on insert... caught...\n";
+    echo $exc->GetExceptionMessage()."\n";
+    echo $exc->GetDetails()."\n";
+    echo $exc->GetStackTrace()."\n";
+}
+
+
+echo "Done.\n";
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/createSdfUnicode.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/createSdfUnicode.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/createSdfUnicode.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,179 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+require_once("master.php");
+require_once("Utils.php");
+
+try
+{
+
+    Utils::MapAgentInit(WEBCONFIGINI);
+    $site = new MgSiteConnection();
+    $cred = new MgUserInformation();
+    $cred->SetMgUsernamePassword("Administrator","admin");
+    $cred->SetLocale("en");
+    $site->Open($cred);
+    $svc = $site->CreateService(MgServiceType::FeatureService);
+}
+catch (MgException $exc)
+{
+    echo $exc->GetExceptionMessage()."\n";
+    echo $exc->GetDetails()."\n";
+    return;
+}
+
+echo "Created Services\n";
+
+// Create class definition for new feature  class
+$classDef = new MgClassDefinition();
+
+$classDef->SetName("Buffer");
+$classDef->SetDescription("Feature class for buffer layer");
+$classDef->SetDefaultGeometryPropertyName("GEOM");
+
+// Set key property
+$prop = new MgDataPropertyDefinition("KEY");
+$prop->SetDataType(MgPropertyType::Int32);
+$prop->SetAutoGeneration(true);
+$prop->SetReadOnly(true);
+
+$classDef->GetIdentityProperties()->Add($prop);
+$classDef->GetProperties()->Add($prop);
+
+$prop = new MgDataPropertyDefinition("NAME");
+$prop->SetDataType(MgPropertyType::String);
+
+$classDef->GetProperties()->Add($prop);
+
+// Set geometry property
+$prop = new MgGeometricPropertyDefinition("GEOM");
+//$prop->SetGeometryTypes(MgFeatureGeometricType::Surface);
+$prop->SetGeometryTypes(MgFeatureGeometricType::Point);
+
+$classDef->GetProperties()->Add($prop);
+$schema = new MgFeatureSchema("BufferSchema", "Temporary buffer schema");
+$schema->GetClasses()->Add($classDef);
+
+$wkt = "LOCALCS[\"Non-Earth (Meter)\",LOCAL_DATUM[\"Local Datum\",0],UNIT[\"Meter\", 1],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH]]";
+$params = new MgCreateSdfParams("ArbitraryXY", $wkt, $schema);
+
+echo "Created sdfparams\n";
+
+$layerName = 'Library://TrevorWekel/NewSdf.FeatureSource';
+$id = new MgResourceIdentifier($layerName);
+
+try
+{
+
+    $svc->CreateFeatureSource($id, $params);
+}
+catch (MgException $exc)
+{
+    echo $exc->GetExceptionMessage()."\n";
+    echo $exc->GetDetails()."\n";
+    return;
+}
+
+echo "Created featuresource\n";
+
+// We need to add some data to the sdf before using it.  The spatial context
+// reader must have an extent.
+
+$batchProp = new MgBatchPropertyCollection();
+$wkt = new MgWktReaderWriter();
+$agf = new MgAgfReaderWriter();
+$fact = new MgGeometryFactory();
+
+echo "Created wkt/agf\n";
+
+$count = 10;
+$i = 0;
+for ($i=1; $i<=$count; $i++)
+{
+
+    $bufferProps = new MgPropertyCollection();
+    $nameProp = new MgStringProperty("NAME", "Name_E�??????Ab2??�Ab2????_".$i);
+    $bufferProps->Add($nameProp);
+
+    $x = 120+$i/$count;
+    $y = 100+$i/$count;
+    //$wktText = "POLYGON ((20 20, 20 100, {$x} {$y}, 140 20, 20 20))";
+    //$geom = $wkt->Read($wktText);
+    $coord = $fact->CreateCoordinateXY($x, $y);
+    $geom = $fact->CreatePoint($coord);
+    $geomReader = $agf->Write($geom);
+    $geomProp = new MgGeometryProperty("GEOM", $geomReader);
+    $bufferProps->Add($geomProp);
+
+    $batchProp->Add($bufferProps);
+}
+
+echo "Created geometries via wkt\n";
+
+$cmd = new MgInsertFeatures("Buffer", $batchProp);
+$cmdColl = new MgFeatureCommandCollection();
+
+$cmdColl->Add($cmd);
+
+try
+{
+    $svc->CreateFeatureSource($id, $params);
+    $startTime = microtime(true);
+    $props = $svc->UpdateFeatures($id, $cmdColl, false);
+    $endTime = microtime(true);
+    $diffTime = $endTime - $startTime;
+    $reader = $props->GetItem(0)->GetValue();
+    while ($reader->ReadNext() == true)
+    {
+        $key = $reader->GetInt32("KEY");
+    }
+    $reader->Close();
+
+    // Now select a few of the feature and make sure we get everything back correctly.
+
+    $query = new MgFeatureQueryOptions();
+    $query->AddFeatureProperty("NAME");
+    $query->AddFeatureProperty("KEY");
+    $query->AddFeatureProperty("GEOM");
+    $query->SetFilter("");
+
+    $reader = $svc->SelectFeatures($id, "Buffer", $query);
+    while ($reader->ReadNext() == true)
+    {
+        $geomText = '';
+        $agfStream = $reader->GetGeometry("GEOM");
+        $geom = $agf->Read($agfStream);
+        $geomText = $wkt->Write($geom);
+        echo $reader->GetInt32("KEY")." ".$reader->GetString("NAME")." ".$geomText."\n";
+    }
+
+    echo "Time for ".$i." ops was ".$diffTime. " seconds\n";
+    echo "Average throughput of ".$i/$diffTime. " features per second\n";
+}
+catch (MgException $exc)
+{
+    echo "First failure on insert... caught...\n";
+    echo $exc->GetExceptionMessage()."\n";
+    echo $exc->GetDetails()."\n";
+    echo $exc->GetStackTrace()."\n";
+}
+
+
+echo "Done.\n";
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/index.html
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/index.html	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/index.html	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,6 @@
+<html>
+<FRAMESET cols="15%, 85%">
+    <FRAME src="Menu.html" noresize>
+    <FRAME src="ApiUnitTests.php" noresize name="showframe">
+</FRAMESET>
+</html>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/master.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/master.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/master.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,6 @@
+<?php
+
+require_once("MapGuideApi.php");
+require_once("constants.php");
+
+?>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/setResourceData.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/setResourceData.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/setResourceData.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,98 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+//
+// Usage:
+// php-cgi updateResource.php user=SomeUserName num=NumberOfResources loop=LoopCount
+//
+//
+require_once("master.php");
+require_once("Utils.php");
+
+try
+{
+
+    Utils::MapAgentInit(WEBCONFIGINI);
+    $site = new MgSiteConnection();
+    $cred = new MgUserInformation();
+    $cred->SetMgUsernamePassword("Administrator","admin");
+    $cred->SetLocale("en");
+    $site->Open($cred);
+    $svc = $site->CreateService(MgServiceType::ResourceService);
+}
+catch (MgException $exc)
+{
+    header("HTTP/1.1 559".$exc->GetExceptionMessage());
+    $hdr = "Status: 559 ".$exc->GetExceptionMessage();
+    header($hdr);
+    echo "<html>\n";
+    echo "<body>\n";
+    echo $hdr."\n";
+    echo $exc->GetExceptionMessage()."\n";
+    echo $exc->GetDetails()."\n";
+    echo "</body>\n";
+    echo "</html>\n";
+    return;
+}
+echo "<html>\n";
+echo "<body>\n";
+
+$layerName = 'Library://TrevorWekel/PerfTest/Sdf1.FeatureSource';
+
+$res = new MgByteSource("../../TestData/FeatureService/Sdf1.FeatureSource");
+echo "Updating resource " . $layerName . "\n";
+ob_flush();
+
+$id = new MgResourceIdentifier($layerName);
+
+try
+{
+
+    $svc->SetResource($id, $res->GetReader(), null);
+
+}
+catch (MgException $e)
+{
+    // Resource already exists, probably ok.
+    echo $e->GetDetails(). "\n";
+    ob_flush();
+}
+
+$shortFileName = $_POST["file"];
+
+if (strlen($shortFileName) <= 0)
+{
+    echo "Need file argument.  Exiting\n";
+    return;
+}
+
+$fileName = "../../TestData/FeatureService/".$shortFileName;
+$res = new MgByteSource($fileName);
+$rdr = $res->GetReader();
+echo "Starting data transmission of " .filesize($fileName) . " bytes from " . $fileName . " \n.";
+ob_flush();
+$opStart = microtime(true);
+$svc->SetResourceData($id, $shortFileName, "File", $rdr);
+$opEnd = microtime(true);
+$diffTime = $opEnd - $opStart;
+echo "Transmission completed in " . $diffTime . " seconds\n";
+echo "Effective bandwidth was " . filesize($fileName)/$diffTime/1000000 . " million bytes/sec\n";
+echo "</body>\n";
+echo "</html>\n";
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/sortXml.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/sortXml.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/sortXml.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,82 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+function SortElement($elem, $preText)
+{
+    $elemArray = array();
+    $elemString = '';
+
+    if ($elem->hasChildNodes())
+    {
+        $children = $elem->childNodes;
+        for($i = 0; $i < $children->length; $i++)
+        {
+            $child = $children->item($i);
+            if ($child->nodeType == XML_ELEMENT_NODE)
+            {
+                $elemValue = SortElement($child, $preText.'  ');
+                if (strlen($elemValue) > 0)
+                {
+                    array_push($elemArray, $elemValue);
+                }
+            }
+            else if ($child->nodeType == XML_TEXT_NODE)
+            {
+                $content = trim($child->textContent);
+                if (strlen($content) > 0)
+                {
+                    array_push($elemArray,$content);
+                }
+            }
+        }
+        sort($elemArray);
+        foreach ($elemArray as $str)
+        {
+            $elemString .= $str;
+        }
+    }
+
+    $endTag = '';
+    if (sizeof($elemArray) > 1 && strlen($elemString) > 0)
+    {
+        $endTag = "\n".$preText;
+    }
+    $tagName = '';
+    if (is_a($elem,'DOMDocument') == false)
+    {
+        $tagName = $elem->tagName;
+    }
+    $endTag .= "</".$tagName.">";
+
+    if ($tagName != "")
+    {
+        $elemString = "\n".$preText."<".$tagName.">".$elemString.$endTag;
+    }
+
+    return $elemString;
+}
+
+$doc = new DOMDocument();
+$doc->load('D:\MgDev\UnitTest\TestData\Unicode\Sample_World_Unicode.FeatureSource');
+$doc->preserveWhiteSpace = false;
+$sorted = SortElement($doc, '');
+//$sorted = $doc->saveXML();
+print $sorted;
+
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/sqlite_constants.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/sqlite_constants.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/sqlite_constants.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,35 @@
+<?php
+
+define('SQLITE_OK'           ,0   ); /* Successful result */
+// beginning-of-error-codes 
+define('SQLITE_ERROR'        ,1   ); /* SQL error or missing database */
+define('SQLITE_INTERNAL'     ,2   ); /* Internal logic error in SQLite */
+define('SQLITE_PERM'         ,3   ); /* Access permission denied */
+define('SQLITE_ABORT'        ,4   ); /* Callback routine requested an abort */
+define('SQLITE_BUSY'         ,5   ); /* The database file is locked */
+define('SQLITE_LOCKED'       ,6   ); /* A table in the database is locked */
+define('SQLITE_NOMEM'        ,7   ); /* A malloc() failed */
+define('SQLITE_READONLY'     ,8   ); /* Attempt to write a readonly database */
+define('SQLITE_INTERRUPT'    ,9   ); /* Operation terminated by sqlite3_interrupt()*/
+define('SQLITE_IOERR'       ,10   ); /* Some kind of disk I/O error occurred */
+define('SQLITE_CORRUPT'     ,11   ); /* The database disk image is malformed */
+define('SQLITE_NOTFOUND'    ,12   ); /* Unknown opcode in sqlite3_file_control() */
+define('SQLITE_FULL'        ,13   ); /* Insertion failed because database is full */
+define('SQLITE_CANTOPEN'    ,14   ); /* Unable to open the database file */
+define('SQLITE_PROTOCOL'    ,15   ); /* Database lock protocol error */
+define('SQLITE_EMPTY'       ,16   ); /* Database is empty */
+define('SQLITE_SCHEMA'      ,17   ); /* The database schema changed */
+define('SQLITE_TOOBIG'      ,18   ); /* String or BLOB exceeds size limit */
+define('SQLITE_CONSTRAINT'  ,19   ); /* Abort due to constraint violation */
+define('SQLITE_MISMATCH'    ,20   ); /* Data type mismatch */
+define('SQLITE_MISUSE'      ,21   ); /* Library used incorrectly */
+define('SQLITE_NOLFS'       ,22   ); /* Uses OS features not supported on host */
+define('SQLITE_AUTH'        ,23   ); /* Authorization denied */
+define('SQLITE_FORMAT'      ,24   ); /* Auxiliary database format error */
+define('SQLITE_RANGE'       ,25   ); /* 2nd parameter to sqlite3_bind out of range */
+define('SQLITE_NOTADB'      ,26   ); /* File opened that is not a database file */
+define('SQLITE_ROW'         ,100  ); /* sqlite3_step() has another row ready */
+define('SQLITE_DONE'        ,101  ); /* sqlite3_step() has finished executing */
+// end-of-error-codes
+
+?>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Test/Php/testAwSelection.php
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/Php/testAwSelection.php	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/Php/testAwSelection.php	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,272 @@
+<?php
+
+//
+//  Copyright (C) 2004-2011 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+
+require_once("master.php");
+require_once("Utils.php");
+
+interface FeatureClass
+{
+    function ClassDefinition();
+
+    function InsertCommand($key);
+
+    function ValidateFeature($key, $featureReader);
+
+}
+
+class IntKeyFeature implements FeatureClass
+{
+    function ClassDefinition()
+    {
+        $classDef = new MgClassDefinition();
+
+        $classDef->SetName("IntKey");
+        $classDef->SetDescription("Integer key feature class");
+        $classDef->SetDefaultGeometryPropertyName("GEOM");
+
+        // Set key property
+        $prop = new MgDataPropertyDefinition("KEY");
+        $prop->SetDataType(MgPropertyType::Int32);
+        $prop->SetAutoGeneration(false);
+
+        $classDef->GetIdentityProperties()->Add($prop);
+        $classDef->GetProperties()->Add($prop);
+
+        $prop = new MgDataPropertyDefinition("NAME");
+        $prop->SetDataType(MgPropertyType::String);
+
+        $classDef->GetProperties()->Add($prop);
+
+        // Set geometry property
+        $prop = new MgGeometricPropertyDefinition("GEOM");
+        $prop->SetGeometryTypes(4);
+
+        $classDef->GetProperties()->Add($prop);
+
+        return $classDef;
+    }
+
+    function InsertCommand($key)
+    {
+
+        $props = new MgPropertyCollection();
+
+        // key is autogen
+        $keyProp = new MgInt32Property("KEY", $key);
+        $props->Add($keyProp);
+
+        $nameProp = new MgStringProperty("NAME", "IntKey".$key);
+        $props->Add($nameProp);
+
+        $wkt = new MgWktReaderWriter();
+        $agf = new MgAgfReaderWriter();
+        $geom = $wkt->Read("POLYGON ((20 20, 20 100, 120 100, 140 20, 20 20))");
+        $geomReader = $agf->Write($geom);
+        $geomProp = new MgGeometryProperty("GEOM", $geomReader);
+        $props->Add($geomProp);
+
+        $cmd = new MgInsertFeatures("IntKey", $props);
+        return $cmd;
+    }
+
+    function ValidateFeature($key, $featureReader)
+    {
+    }
+}
+
+class StringKeyFeature implements FeatureClass
+{
+    function ClassDefinition()
+    {
+        $classDef = new MgClassDefinition();
+
+        $classDef->SetName("StringKey");
+        $classDef->SetDescription("String key feature class");
+        $classDef->SetDefaultGeometryPropertyName("GEOM");
+
+        // Set key property
+        $prop = new MgDataPropertyDefinition("SKEY");
+        $prop->SetDataType(MgPropertyType::String);
+        $prop->SetAutoGeneration(false);
+
+        $classDef->GetIdentityProperties()->Add($prop);
+        $classDef->GetProperties()->Add($prop);
+
+        $prop = new MgDataPropertyDefinition("NAME");
+        $prop->SetDataType(MgPropertyType::String);
+
+        $classDef->GetProperties()->Add($prop);
+
+        // Set geometry property
+        $prop = new MgGeometricPropertyDefinition("GEOM");
+        $prop->SetGeometryTypes(4);
+
+        $classDef->GetProperties()->Add($prop);
+
+        return $classDef;
+    }
+
+    function InsertCommand($key)
+    {
+
+        $props = new MgPropertyCollection();
+
+        $keyProp = new MgStringProperty("SKEY", $key);
+        $props->Add($keyProp);
+
+        $nameProp = new MgStringProperty("NAME", "StringKey".$key);
+        $props->Add($nameProp);
+
+        $wkt = new MgWktReaderWriter();
+        $agf = new MgAgfReaderWriter();
+        $geom = $wkt->Read("POLYGON ((20 20, 20 100, 120 100, 140 20, 20 20))");
+        $geomReader = $agf->Write($geom);
+        $geomProp = new MgGeometryProperty("GEOM", $geomReader);
+        $props->Add($geomProp);
+
+        $cmd = new MgInsertFeatures("StringKey", $props);
+        return $cmd;
+    }
+
+    function ValidateFeature($key, $featureReader)
+    {
+    }
+}
+
+
+
+
+try
+{
+
+    Utils::MapAgentInit(WEBCONFIGINI);
+    $site = new MgSiteConnection();
+    $cred = new MgUserInformation();
+    $cred->SetMgUsernamePassword("Administrator","admin");
+    $cred->SetLocale("en");
+    $site->Open($cred);
+    $fsvc = $site->CreateService(MgServiceType::FeatureService);
+    $rsvc = $site->CreateService(MgServiceType::ResourceService);
+
+    $wkt = "LOCALCS[\"Non-Earth (Meter)\",LOCAL_DATUM[\"Local Datum\",0],UNIT[\"Meter\", 1],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH]]";
+    $featureName = 'Library://TrevorWekel/NewSdf.FeatureSource';
+    $id = new MgResourceIdentifier($featureName);
+
+    $intFeature = new IntKeyFeature();
+    $stringFeature = new StringKeyFeature();
+    $schema = new MgFeatureSchema("TestSchema", "Temporary test schema");
+    $schema->GetClasses()->Add($intFeature->ClassDefinition());
+
+    $params = new MgCreateSdfParams("ArbitraryXY", $wkt, $schema);
+
+    $src = new MgByteSource("NewSdf.MapDefinition");
+    $rid = new MgResourceIdentifier("Library://TrevorWekel/NewSdf.MapDefinition");
+    $rsvc->SetResource($rid, $src->GetReader(), null);
+
+    $src = new MgByteSource("NewSdfInt.LayerDefinition");
+    $rid = new MgResourceIdentifier("Library://TrevorWekel/NewSdfInt.LayerDefinition");
+    $rsvc->SetResource($rid, $src->GetReader(), null);
+
+    $src = new MgByteSource("NewSdfString.LayerDefinition");
+    $rid = new MgResourceIdentifier("Library://TrevorWekel/NewSdfString.LayerDefinition");
+    $rsvc->SetResource($rid, $src->GetReader(), null);
+
+    echo "Deleting existing feature source\n";
+    $rsvc->DeleteResource($id);
+
+    echo "Creating new feature source\n";
+    $fsvc->CreateFeatureSource($id, $params);
+
+
+    // We need to add some data to the sdf before using it.  The spatial context
+    // reader must have an extent.
+
+    $cmdColl = new MgFeatureCommandCollection();
+
+    for ($i = 1; $i<=20; $i++)
+    {
+        $insert = $intFeature->InsertCommand($i);
+        $cmdColl->Add($insert);
+    }
+
+
+    echo "Updating features\n";
+    $fsvc->UpdateFeatures($id, $cmdColl, false);
+
+    $mapId = new MgResourceIdentifier("Library://TrevorWekel/NewSdf.MapDefinition");
+    $map = new MgMap($site);
+    $map->Create($mapId, "NewMap");
+
+    echo "Building Selection from Add()\n";
+    $sel = new MgSelection($map);
+
+    $slayer = $map->GetLayers()->GetItem(0);
+    $sel->AddFeatureIdInt32($slayer, "IntKey", 1);
+    $sel->AddFeatureIdInt32($slayer, "IntKey", 10);
+    $sel->AddFeatureIdInt32($slayer, "IntKey", 20);
+
+    echo "XML FeatureSet is\n".$sel->ToXml()."\n";
+
+    echo "\nString Filter: ".$sel->GenerateFilter($slayer, "StringKey")."\n\n";
+
+    echo "Building Selection from XML\n";
+    $sel2 = new MgSelection($map, $sel->ToXml());
+
+    // Test basic methods
+    $layerColl = $sel2->GetLayers();
+    $newLayer = $layerColl->GetItem(0);
+    echo "First layer selected is ".$newLayer->GetName()."\n";
+
+    echo "BadKey Filter: ".$sel2->GenerateFilter($slayer, "BadKey")."\n";
+    $filter = $sel2->GenerateFilter($slayer, "IntKey");
+    echo "\nString Filter: ".$filter."\n\n";
+
+    $query = new MgFeatureQueryOptions();
+    $query->AddFeatureProperty("KEY");
+    $query->AddFeatureProperty("NAME");
+    $query->SetFilter($filter);
+
+    echo "Selected features\n";
+    $reader = $fsvc->SelectFeatures($id, "IntKey", $query);
+    while ($reader->ReadNext() == true)
+    {
+        echo $reader->GetString("NAME")."\n";
+    }
+
+    echo "MgSelection from Reader\n";
+    $reader = $fsvc->SelectFeatures($id, "IntKey", $query);
+    $selection = new MgSelection($map);
+    $layer1 = $map->GetLayers()->GetItem(0);
+    $selection->AddFeatures($layer1, $reader, 0);
+    echo $selection->ToXml();
+
+    $envelope = $selection->GetExtents($fsvc);
+    $ll = $envelope->GetLowerLeftCoordinate();
+    $ur = $envelope->GetUpperRightCoordinate();
+    echo "(".$ll->GetX().",".$ll->GetY().") - (".$ur->GetX().",".$ur->GetY().")\n";
+
+}
+catch (MgException $exc)
+{
+    echo $exc->GetExceptionMessage()."\n";
+    echo $exc->GetDetails()."\n";
+}
+echo "Done.\n";
+?>

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/CMakeLists.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/CMakeLists.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/CMakeLists.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 2.8)
+
+if(COMMAND cmake_policy)
+    cmake_policy(SET CMP0003 NEW)
+    #cmake_policy(SET CMP0054 OLD)
+endif(COMMAND cmake_policy)
+
+set(TOOL_INSTALL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../tools")
+
+add_subdirectory(PhpPostProcess)
+add_subdirectory(StampVer)
+add_subdirectory(SwigPrepare)
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/ClassMapGen.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/ClassMapGen.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/ClassMapGen.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Update="Data\classmap_master.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+    <None Update="Data\Templates\dotnet.txt">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+    <None Update="Data\Templates\php.txt">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+    <None Update="Data\Templates\java.txt">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+  
+</Project>

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/README.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/README.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/README.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,5 @@
+Class map source templates reside in /Templates
+
+classmap_master.json is the current master class map list. This is manually curated from the MapGuide source.
+
+TODO: Repurpose our internal copy of SWIG as a class map generator so we don't have to manually maintain this list.
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/dotnet.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/dotnet.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/dotnet.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,24 @@
+// This file is autogenerated by ClassMapGen. DO NOT EDIT
+
+using System;
+using System.Collections.Generic;
+
+namespace OSGeo.MapGuide
+{
+    internal static class MgClassMap
+    {
+        static Dictionary<int, string> classNameMap;
+        
+        static MgClassMap()
+        {
+            classNameMap = new Dictionary<int, string>();
+            
+$CLASS_NAME_MAP_BODY$
+        }
+        
+        internal static string GetTypeName(int id)
+        {
+            return classNameMap.ContainsKey(id) ? classNameMap[id] : null;
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/java.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/java.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/java.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,59 @@
+package org.osgeo.mapguide;
+
+import java.util.*;
+import java.lang.reflect.*;
+
+public class ObjectFactory
+{
+    public static Object createObject(int id, long cptr, boolean ownCptr)
+    {
+        Constructor ctor = (Constructor)classMap.get(new Integer(id));
+        if(ctor == null)
+            return null;
+        try
+        {
+            return ctor.newInstance(new Object[] { new Long(cptr), new Boolean(ownCptr) });
+        }
+        catch(Exception e)
+        {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+
+    protected static Hashtable classMap;
+
+    static
+    {
+        classMap = new Hashtable();
+        try
+        {
+$CLASS_NAME_MAP_BODY$
+        }
+        catch(Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    private static Constructor getSWIGCtor(String className) throws ClassNotFoundException, Exception
+    {
+        Constructor swigCtor = null;
+        Constructor[] cons = Class.forName("org.osgeo.mapguide." + className).getDeclaredConstructors();
+        for (int i = 0; i < cons.length; i++)
+        {
+            Class[] parameterTypes = cons[i].getParameterTypes();
+            if (parameterTypes.length == 2 && parameterTypes[0].equals(Long.TYPE) && parameterTypes[1].equals(Boolean.TYPE))
+            {
+                swigCtor = cons[i];
+                swigCtor.setAccessible(true); //This ctor will be protected, so we need to make it accessible
+            }
+        }
+        if (swigCtor == null)
+        {
+            throw new Exception("Could not find the expected internal SWIG constructor for class: " + className);
+        }
+        return swigCtor;
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/php.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/php.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/Templates/php.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,21 @@
+// This file is autogenerated by ClassMapGen. DO NOT EDIT
+
+static std::map<int, const char*> InitClassMap()
+{
+    std::map<int, const char*> classNameMap;
+
+$CLASS_NAME_MAP_BODY$
+
+    return classNameMap;
+}
+
+static std::map<int, const char*> smClassMap = InitClassMap();
+
+static const char* ResolveMgClassName(const int classId)
+{
+    if (smClassMap.find(classId) != smClassMap.end())
+    {
+        return smClassMap[classId];
+    }
+    return NULL;
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/classmap_master.json
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/classmap_master.json	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Data/classmap_master.json	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,1114 @@
+{
+    "Modules": [
+        {
+            "Name": "Foundation",
+            "Classes": {
+                "1500": "MgApplicationException",
+                "1501": "MgArgumentOutOfRangeException",
+                "1005": "MgBatchPropertyCollection",
+                "1250": "MgByteReader",
+                "1257": "MgByteSink",
+                "1251": "MgByteSource",
+                "1502": "MgClassNotFoundException",
+                "1503": "MgConfigurationException",
+                "1504": "MgConfigurationLoadFailedException",
+                "1505": "MgConfigurationSaveFailedException",
+                "1256": "MgDateTime",
+                "1506": "MgDateTimeException",
+                "1507": "MgDecryptionException",
+                "1508": "MgDirectoryNotFoundException",
+                "1000": "MgDisposableCollection",
+                "1509": "MgDivideByZeroException",
+                "1510": "MgDomainException",
+                "1511": "MgDuplicateDirectoryException",
+                "1512": "MgDuplicateFileException",
+                "1513": "MgDuplicateObjectException",
+                "1514": "MgEncryptionException",
+                "1515": "MgException",
+                "1516": "MgFileIoException",
+                "1517": "MgFileNotFoundException",
+                "1518": "MgIndexOutOfRangeException",
+                "1519": "MgInvalidArgumentException",
+                "1520": "MgInvalidCastException",
+                "1522": "MgInvalidOperationException",
+                "1523": "MgInvalidPropertyTypeException",
+                "1524": "MgInvalidStreamHeaderException",
+                "1525": "MgIoException",
+                "1526": "MgLengthException",
+                "1527": "MgLogicException",
+                "1528": "MgNotFiniteNumberException",
+                "1529": "MgNotImplementedException",
+                "1530": "MgNullArgumentException",
+                "1531": "MgNullPropertyValueException",
+                "1532": "MgNullReferenceException",
+                "1533": "MgObjectNotFoundException",
+                "1534": "MgOutOfMemoryException",
+                "1535": "MgOutOfRangeException",
+                "1536": "MgOverflowException",
+                "1537": "MgPlatformNotSupportedException",
+                "2000": "MgProperty",
+                "1002": "MgPropertyCollection",
+                "2002": "MgPropertyDefinition",
+                "1540": "MgResourceTagNotFoundException",
+                "1538": "MgResourcesException",
+                "1539": "MgResourcesLoadFailedException",
+                "1541": "MgRuntimeException",
+                "1542": "MgStreamIoException",
+                "1003": "MgStringCollection",
+                "2001": "MgStringProperty",
+                "1543": "MgSystemException",
+                "1544": "MgTemporaryFileNotAvailableException",
+                "1545": "MgThirdPartyException",
+                "1547": "MgUnclassifiedException",
+                "1548": "MgUnderflowException",
+                "1549": "MgXmlException",
+                "1550": "MgXmlParserException"
+            }
+        },
+        {
+            "Name": "Geometry",
+            "Classes": {
+                "20004": "MgAgfReaderWriter",
+                "20005": "MgAggregateGeometry",
+                "1500": "MgApplicationException",
+                "20006": "MgArcSegment",
+                "1501": "MgArgumentOutOfRangeException",
+                "1005": "MgBatchPropertyCollection",
+                "1250": "MgByteReader",
+                "1257": "MgByteSink",
+                "1251": "MgByteSource",
+                "1502": "MgClassNotFoundException",
+                "1503": "MgConfigurationException",
+                "1504": "MgConfigurationLoadFailedException",
+                "1505": "MgConfigurationSaveFailedException",
+                "20003": "MgCoordinate",
+                "20009": "MgCoordinateIterator",
+                "20500": "MgCoordinateSystem",
+                "20504": "MgCoordinateSystemCatalog",
+                "20506": "MgCoordinateSystemCategory",
+                "20510": "MgCoordinateSystemCategoryDictionary",
+                "21000": "MgCoordinateSystemComputationFailedException",
+                "21001": "MgCoordinateSystemConversionFailedException",
+                "20507": "MgCoordinateSystemDatum",
+                "20512": "MgCoordinateSystemDatumDictionary",
+                "20511": "MgCoordinateSystemDictionary",
+                "20521": "MgCoordinateSystemDictionaryUtility",
+                "20509": "MgCoordinateSystemEllipsoid",
+                "20513": "MgCoordinateSystemEllipsoidDictionary",
+                "20514": "MgCoordinateSystemEnum",
+                "20515": "MgCoordinateSystemEnumInteger32",
+                "20501": "MgCoordinateSystemFactory",
+                "20516": "MgCoordinateSystemFilter",
+                "20517": "MgCoordinateSystemFilterInteger32",
+                "20505": "MgCoordinateSystemFormatConverter",
+                "20542": "MgCoordinateSystemGeodeticAnalyticalTransformDefParams",
+                "20543": "MgCoordinateSystemGeodeticInterpolationTransformDefParams",
+                "20544": "MgCoordinateSystemGeodeticMultipleRegressionTransformDefParams",
+                "20533": "MgCoordinateSystemGeodeticPath",
+                "20535": "MgCoordinateSystemGeodeticPathDictionary",
+                "20534": "MgCoordinateSystemGeodeticPathElement",
+                "20536": "MgCoordinateSystemGeodeticTransformDef",
+                "20540": "MgCoordinateSystemGeodeticTransformDefDictionary",
+                "20545": "MgCoordinateSystemGeodeticTransformGridFile",
+                "20508": "MgCoordinateSystemGeodeticTransformation",
+                "20532": "MgCoordinateSystemGridBase",
+                "20524": "MgCoordinateSystemGridBoundary",
+                "20529": "MgCoordinateSystemGridLine",
+                "20526": "MgCoordinateSystemGridLineCollection",
+                "20530": "MgCoordinateSystemGridRegion",
+                "20527": "MgCoordinateSystemGridRegionCollection",
+                "20525": "MgCoordinateSystemGridSpecification",
+                "20531": "MgCoordinateSystemGridTick",
+                "20528": "MgCoordinateSystemGridTickCollection",
+                "21002": "MgCoordinateSystemInitializationFailedException",
+                "21003": "MgCoordinateSystemLoadFailedException",
+                "20518": "MgCoordinateSystemMathComparator",
+                "20502": "MgCoordinateSystemMeasure",
+                "21004": "MgCoordinateSystemMeasureFailedException",
+                "20522": "MgCoordinateSystemMgrs",
+                "20520": "MgCoordinateSystemProjectionInformation",
+                "20503": "MgCoordinateSystemTransform",
+                "21005": "MgCoordinateSystemTransformFailedException",
+                "20519": "MgCoordinateSystemUnitInformation",
+                "20048": "MgCoordinateXY",
+                "20051": "MgCoordinateXYM",
+                "20049": "MgCoordinateXYZ",
+                "20050": "MgCoordinateXYZM",
+                "20010": "MgCurve",
+                "20011": "MgCurvePolygon",
+                "20047": "MgCurvePolygonCollection",
+                "20012": "MgCurveRing",
+                "20052": "MgCurveRingCollection",
+                "20013": "MgCurveSegment",
+                "20041": "MgCurveSegmentCollection",
+                "20014": "MgCurveString",
+                "20043": "MgCurveStringCollection",
+                "1256": "MgDateTime",
+                "1506": "MgDateTimeException",
+                "1507": "MgDecryptionException",
+                "1508": "MgDirectoryNotFoundException",
+                "1000": "MgDisposableCollection",
+                "1509": "MgDivideByZeroException",
+                "1510": "MgDomainException",
+                "1511": "MgDuplicateDirectoryException",
+                "1512": "MgDuplicateFileException",
+                "1513": "MgDuplicateObjectException",
+                "1514": "MgEncryptionException",
+                "20001": "MgEnvelope",
+                "1515": "MgException",
+                "1516": "MgFileIoException",
+                "1517": "MgFileNotFoundException",
+                "20016": "MgGeometricEntity",
+                "20019": "MgGeometry",
+                "20020": "MgGeometryCollection",
+                "20021": "MgGeometryComponent",
+                "21006": "MgGeometryException",
+                "20002": "MgGeometryFactory",
+                "1518": "MgIndexOutOfRangeException",
+                "1519": "MgInvalidArgumentException",
+                "1520": "MgInvalidCastException",
+                "21007": "MgInvalidCoordinateSystemException",
+                "21008": "MgInvalidCoordinateSystemTypeException",
+                "21009": "MgInvalidCoordinateSystemUnitsException",
+                "1522": "MgInvalidOperationException",
+                "1523": "MgInvalidPropertyTypeException",
+                "1524": "MgInvalidStreamHeaderException",
+                "1525": "MgIoException",
+                "1526": "MgLengthException",
+                "20042": "MgLineString",
+                "20044": "MgLineStringCollection",
+                "20023": "MgLinearRing",
+                "20053": "MgLinearRingCollection",
+                "20024": "MgLinearSegment",
+                "1527": "MgLogicException",
+                "20029": "MgMultiCurvePolygon",
+                "20030": "MgMultiCurveString",
+                "20031": "MgMultiGeometry",
+                "20032": "MgMultiLineString",
+                "20033": "MgMultiPoint",
+                "20034": "MgMultiPolygon",
+                "1528": "MgNotFiniteNumberException",
+                "1529": "MgNotImplementedException",
+                "1530": "MgNullArgumentException",
+                "1531": "MgNullPropertyValueException",
+                "1532": "MgNullReferenceException",
+                "1533": "MgObjectNotFoundException",
+                "1534": "MgOutOfMemoryException",
+                "1535": "MgOutOfRangeException",
+                "1536": "MgOverflowException",
+                "1537": "MgPlatformNotSupportedException",
+                "20000": "MgPoint",
+                "20045": "MgPointCollection",
+                "20035": "MgPolygon",
+                "20046": "MgPolygonCollection",
+                "2000": "MgProperty",
+                "1002": "MgPropertyCollection",
+                "2002": "MgPropertyDefinition",
+                "20037": "MgRegion",
+                "1540": "MgResourceTagNotFoundException",
+                "1538": "MgResourcesException",
+                "1539": "MgResourcesLoadFailedException",
+                "20038": "MgRing",
+                "1541": "MgRuntimeException",
+                "1542": "MgStreamIoException",
+                "1003": "MgStringCollection",
+                "2001": "MgStringProperty",
+                "1543": "MgSystemException",
+                "1544": "MgTemporaryFileNotAvailableException",
+                "1545": "MgThirdPartyException",
+                "1547": "MgUnclassifiedException",
+                "1548": "MgUnderflowException",
+                "20040": "MgWktReaderWriter",
+                "1549": "MgXmlException",
+                "1550": "MgXmlParserException"
+            }
+        },
+        {
+            "Name": "PlatformBase",
+            "Classes": {
+                "20004": "MgAgfReaderWriter",
+                "20005": "MgAggregateGeometry",
+                "1500": "MgApplicationException",
+                "20006": "MgArcSegment",
+                "1501": "MgArgumentOutOfRangeException",
+                "10500": "MgArrayTypeMismatchException",
+                "1005": "MgBatchPropertyCollection",
+                "10252": "MgBlobProperty",
+                "10253": "MgBooleanProperty",
+                "10254": "MgByteProperty",
+                "1250": "MgByteReader",
+                "1257": "MgByteSink",
+                "1251": "MgByteSource",
+                "11750": "MgClassDefinition",
+                "11780": "MgClassDefinitionCollection",
+                "1502": "MgClassNotFoundException",
+                "10255": "MgClobProperty",
+                "10250": "MgColor",
+                "1503": "MgConfigurationException",
+                "1504": "MgConfigurationLoadFailedException",
+                "1505": "MgConfigurationSaveFailedException",
+                "20003": "MgCoordinate",
+                "20009": "MgCoordinateIterator",
+                "20500": "MgCoordinateSystem",
+                "20504": "MgCoordinateSystemCatalog",
+                "20506": "MgCoordinateSystemCategory",
+                "20510": "MgCoordinateSystemCategoryDictionary",
+                "21000": "MgCoordinateSystemComputationFailedException",
+                "21001": "MgCoordinateSystemConversionFailedException",
+                "20507": "MgCoordinateSystemDatum",
+                "20512": "MgCoordinateSystemDatumDictionary",
+                "20511": "MgCoordinateSystemDictionary",
+                "20521": "MgCoordinateSystemDictionaryUtility",
+                "20509": "MgCoordinateSystemEllipsoid",
+                "20513": "MgCoordinateSystemEllipsoidDictionary",
+                "20514": "MgCoordinateSystemEnum",
+                "20515": "MgCoordinateSystemEnumInteger32",
+                "20501": "MgCoordinateSystemFactory",
+                "20516": "MgCoordinateSystemFilter",
+                "20517": "MgCoordinateSystemFilterInteger32",
+                "20505": "MgCoordinateSystemFormatConverter",
+                "20542": "MgCoordinateSystemGeodeticAnalyticalTransformDefParams",
+                "20543": "MgCoordinateSystemGeodeticInterpolationTransformDefParams",
+                "20544": "MgCoordinateSystemGeodeticMultipleRegressionTransformDefParams",
+                "20533": "MgCoordinateSystemGeodeticPath",
+                "20535": "MgCoordinateSystemGeodeticPathDictionary",
+                "20534": "MgCoordinateSystemGeodeticPathElement",
+                "20536": "MgCoordinateSystemGeodeticTransformDef",
+                "20540": "MgCoordinateSystemGeodeticTransformDefDictionary",
+                "20545": "MgCoordinateSystemGeodeticTransformGridFile",
+                "20508": "MgCoordinateSystemGeodeticTransformation",
+                "20532": "MgCoordinateSystemGridBase",
+                "20524": "MgCoordinateSystemGridBoundary",
+                "20529": "MgCoordinateSystemGridLine",
+                "20526": "MgCoordinateSystemGridLineCollection",
+                "20530": "MgCoordinateSystemGridRegion",
+                "20527": "MgCoordinateSystemGridRegionCollection",
+                "20525": "MgCoordinateSystemGridSpecification",
+                "20531": "MgCoordinateSystemGridTick",
+                "20528": "MgCoordinateSystemGridTickCollection",
+                "21002": "MgCoordinateSystemInitializationFailedException",
+                "21003": "MgCoordinateSystemLoadFailedException",
+                "20518": "MgCoordinateSystemMathComparator",
+                "20502": "MgCoordinateSystemMeasure",
+                "21004": "MgCoordinateSystemMeasureFailedException",
+                "20522": "MgCoordinateSystemMgrs",
+                "20520": "MgCoordinateSystemProjectionInformation",
+                "20503": "MgCoordinateSystemTransform",
+                "21005": "MgCoordinateSystemTransformFailedException",
+                "20519": "MgCoordinateSystemUnitInformation",
+                "20048": "MgCoordinateXY",
+                "20051": "MgCoordinateXYM",
+                "20049": "MgCoordinateXYZ",
+                "20050": "MgCoordinateXYZM",
+                "20010": "MgCurve",
+                "20011": "MgCurvePolygon",
+                "20047": "MgCurvePolygonCollection",
+                "20012": "MgCurveRing",
+                "20052": "MgCurveRingCollection",
+                "20013": "MgCurveSegment",
+                "20041": "MgCurveSegmentCollection",
+                "20014": "MgCurveString",
+                "20043": "MgCurveStringCollection",
+                "11751": "MgDataPropertyDefinition",
+                "11773": "MgDataReader",
+                "1256": "MgDateTime",
+                "1506": "MgDateTimeException",
+                "10256": "MgDateTimeProperty",
+                "1507": "MgDecryptionException",
+                "11775": "MgDeleteFeatures",
+                "1508": "MgDirectoryNotFoundException",
+                "1000": "MgDisposableCollection",
+                "1509": "MgDivideByZeroException",
+                "1510": "MgDomainException",
+                "10257": "MgDoubleProperty",
+                "1511": "MgDuplicateDirectoryException",
+                "1512": "MgDuplicateFileException",
+                "1513": "MgDuplicateObjectException",
+                "10501": "MgDuplicateResourceDataException",
+                "10502": "MgDuplicateResourceException",
+                "10503": "MgEmptyFeatureSetException",
+                "1514": "MgEncryptionException",
+                "20001": "MgEnvelope",
+                "1515": "MgException",
+                "10504": "MgFdoException",
+                "11772": "MgFeatureAggregateOptions",
+                "11774": "MgFeatureCommandCollection",
+                "11764": "MgFeatureProperty",
+                "11771": "MgFeatureQueryOptions",
+                "11753": "MgFeatureReader",
+                "11778": "MgFeatureSchema",
+                "11779": "MgFeatureSchemaCollection",
+                "11754": "MgFeatureService",
+                "10505": "MgFeatureServiceException",
+                "11786": "MgFileFeatureSourceParams",
+                "1516": "MgFileIoException",
+                "1517": "MgFileNotFoundException",
+                "20016": "MgGeometricEntity",
+                "11756": "MgGeometricPropertyDefinition",
+                "20019": "MgGeometry",
+                "20020": "MgGeometryCollection",
+                "20021": "MgGeometryComponent",
+                "21006": "MgGeometryException",
+                "20002": "MgGeometryFactory",
+                "11758": "MgGeometryProperty",
+                "11785": "MgGeometryTypeInfo",
+                "11782": "MgGwsFeatureReader",
+                "1518": "MgIndexOutOfRangeException",
+                "11776": "MgInsertFeatures",
+                "10258": "MgInt16Property",
+                "10259": "MgInt32Property",
+                "10260": "MgInt64Property",
+                "10000": "MgIntCollection",
+                "1519": "MgInvalidArgumentException",
+                "1520": "MgInvalidCastException",
+                "21007": "MgInvalidCoordinateSystemException",
+                "21008": "MgInvalidCoordinateSystemTypeException",
+                "21009": "MgInvalidCoordinateSystemUnitsException",
+                "10507": "MgInvalidMapDefinitionException",
+                "1522": "MgInvalidOperationException",
+                "1523": "MgInvalidPropertyTypeException",
+                "10508": "MgInvalidRepositoryNameException",
+                "10509": "MgInvalidRepositoryTypeException",
+                "10510": "MgInvalidResourceDataNameException",
+                "10511": "MgInvalidResourceDataTypeException",
+                "10512": "MgInvalidResourceNameException",
+                "10513": "MgInvalidResourcePathException",
+                "10514": "MgInvalidResourcePreProcessingTypeException",
+                "10515": "MgInvalidResourceTypeException",
+                "1524": "MgInvalidStreamHeaderException",
+                "1525": "MgIoException",
+                "12003": "MgLayerBase",
+                "12002": "MgLayerCollection",
+                "12001": "MgLayerGroup",
+                "12004": "MgLayerGroupCollection",
+                "10517": "MgLayerNotFoundException",
+                "1526": "MgLengthException",
+                "20042": "MgLineString",
+                "20044": "MgLineStringCollection",
+                "20023": "MgLinearRing",
+                "20053": "MgLinearRingCollection",
+                "20024": "MgLinearSegment",
+                "1527": "MgLogicException",
+                "11766": "MgLongTransactionReader",
+                "12000": "MgMapBase",
+                "12005": "MgMapCollection",
+                "20029": "MgMultiCurvePolygon",
+                "20030": "MgMultiCurveString",
+                "20031": "MgMultiGeometry",
+                "20032": "MgMultiLineString",
+                "20033": "MgMultiPoint",
+                "20034": "MgMultiPolygon",
+                "1528": "MgNotFiniteNumberException",
+                "1529": "MgNotImplementedException",
+                "1530": "MgNullArgumentException",
+                "1531": "MgNullPropertyValueException",
+                "1532": "MgNullReferenceException",
+                "1533": "MgObjectNotFoundException",
+                "11759": "MgObjectPropertyDefinition",
+                "1534": "MgOutOfMemoryException",
+                "1535": "MgOutOfRangeException",
+                "1536": "MgOverflowException",
+                "11788": "MgParameter",
+                "10004": "MgParameterCollection",
+                "1537": "MgPlatformNotSupportedException",
+                "20000": "MgPoint",
+                "20045": "MgPointCollection",
+                "20035": "MgPolygon",
+                "20046": "MgPolygonCollection",
+                "2000": "MgProperty",
+                "1002": "MgPropertyCollection",
+                "2002": "MgPropertyDefinition",
+                "10001": "MgPropertyDefinitionCollection",
+                "11769": "MgRaster",
+                "11770": "MgRasterProperty",
+                "11768": "MgRasterPropertyDefinition",
+                "12006": "MgReadOnlyLayerCollection",
+                "20037": "MgRegion",
+                "11526": "MgResource",
+                "10518": "MgResourceBusyException",
+                "10519": "MgResourceDataNotFoundException",
+                "11500": "MgResourceIdentifier",
+                "10520": "MgResourceNotFoundException",
+                "11501": "MgResourceService",
+                "1540": "MgResourceTagNotFoundException",
+                "1538": "MgResourcesException",
+                "1539": "MgResourcesLoadFailedException",
+                "20038": "MgRing",
+                "1541": "MgRuntimeException",
+                "12007": "MgSelectionBase",
+                "11251": "MgService",
+                "10521": "MgServiceNotAvailableException",
+                "10522": "MgServiceNotSupportedException",
+                "10261": "MgSingleProperty",
+                "11761": "MgSpatialContextReader",
+                "11762": "MgSqlDataReader",
+                "1542": "MgStreamIoException",
+                "1003": "MgStringCollection",
+                "2001": "MgStringProperty",
+                "10003": "MgStringPropertyCollection",
+                "1543": "MgSystemException",
+                "1544": "MgTemporaryFileNotAvailableException",
+                "1545": "MgThirdPartyException",
+                "11787": "MgTransaction",
+                "1547": "MgUnclassifiedException",
+                "1548": "MgUnderflowException",
+                "11777": "MgUpdateFeatures",
+                "10523": "MgUserNotFoundException",
+                "11257": "MgWarnings",
+                "20040": "MgWktReaderWriter",
+                "1549": "MgXmlException",
+                "1550": "MgXmlParserException"
+            }
+        },
+        {
+            "Name": "MapGuideCommon",
+            "Classes": {
+                "20004": "MgAgfReaderWriter",
+                "20005": "MgAggregateGeometry",
+                "1500": "MgApplicationException",
+                "20006": "MgArcSegment",
+                "1501": "MgArgumentOutOfRangeException",
+                "10500": "MgArrayTypeMismatchException",
+                "30000": "MgAuthenticationFailedException",
+                "1005": "MgBatchPropertyCollection",
+                "10252": "MgBlobProperty",
+                "10253": "MgBooleanProperty",
+                "10254": "MgByteProperty",
+                "1250": "MgByteReader",
+                "1257": "MgByteSink",
+                "1251": "MgByteSource",
+                "11750": "MgClassDefinition",
+                "11780": "MgClassDefinitionCollection",
+                "1502": "MgClassNotFoundException",
+                "10255": "MgClobProperty",
+                "10250": "MgColor",
+                "1503": "MgConfigurationException",
+                "1504": "MgConfigurationLoadFailedException",
+                "1505": "MgConfigurationSaveFailedException",
+                "30001": "MgConnectionFailedException",
+                "30002": "MgConnectionNotOpenException",
+                "20003": "MgCoordinate",
+                "20009": "MgCoordinateIterator",
+                "20500": "MgCoordinateSystem",
+                "20504": "MgCoordinateSystemCatalog",
+                "20506": "MgCoordinateSystemCategory",
+                "20510": "MgCoordinateSystemCategoryDictionary",
+                "21000": "MgCoordinateSystemComputationFailedException",
+                "21001": "MgCoordinateSystemConversionFailedException",
+                "20507": "MgCoordinateSystemDatum",
+                "20512": "MgCoordinateSystemDatumDictionary",
+                "20511": "MgCoordinateSystemDictionary",
+                "20521": "MgCoordinateSystemDictionaryUtility",
+                "20509": "MgCoordinateSystemEllipsoid",
+                "20513": "MgCoordinateSystemEllipsoidDictionary",
+                "20514": "MgCoordinateSystemEnum",
+                "20515": "MgCoordinateSystemEnumInteger32",
+                "20501": "MgCoordinateSystemFactory",
+                "20516": "MgCoordinateSystemFilter",
+                "20517": "MgCoordinateSystemFilterInteger32",
+                "20505": "MgCoordinateSystemFormatConverter",
+                "20542": "MgCoordinateSystemGeodeticAnalyticalTransformDefParams",
+                "20543": "MgCoordinateSystemGeodeticInterpolationTransformDefParams",
+                "20544": "MgCoordinateSystemGeodeticMultipleRegressionTransformDefParams",
+                "20533": "MgCoordinateSystemGeodeticPath",
+                "20535": "MgCoordinateSystemGeodeticPathDictionary",
+                "20534": "MgCoordinateSystemGeodeticPathElement",
+                "20536": "MgCoordinateSystemGeodeticTransformDef",
+                "20540": "MgCoordinateSystemGeodeticTransformDefDictionary",
+                "20545": "MgCoordinateSystemGeodeticTransformGridFile",
+                "20508": "MgCoordinateSystemGeodeticTransformation",
+                "20532": "MgCoordinateSystemGridBase",
+                "20524": "MgCoordinateSystemGridBoundary",
+                "20529": "MgCoordinateSystemGridLine",
+                "20526": "MgCoordinateSystemGridLineCollection",
+                "20530": "MgCoordinateSystemGridRegion",
+                "20527": "MgCoordinateSystemGridRegionCollection",
+                "20525": "MgCoordinateSystemGridSpecification",
+                "20531": "MgCoordinateSystemGridTick",
+                "20528": "MgCoordinateSystemGridTickCollection",
+                "21002": "MgCoordinateSystemInitializationFailedException",
+                "21003": "MgCoordinateSystemLoadFailedException",
+                "20518": "MgCoordinateSystemMathComparator",
+                "20502": "MgCoordinateSystemMeasure",
+                "21004": "MgCoordinateSystemMeasureFailedException",
+                "20522": "MgCoordinateSystemMgrs",
+                "20520": "MgCoordinateSystemProjectionInformation",
+                "20503": "MgCoordinateSystemTransform",
+                "21005": "MgCoordinateSystemTransformFailedException",
+                "20519": "MgCoordinateSystemUnitInformation",
+                "20048": "MgCoordinateXY",
+                "20051": "MgCoordinateXYM",
+                "20049": "MgCoordinateXYZ",
+                "20050": "MgCoordinateXYZM",
+                "20010": "MgCurve",
+                "20011": "MgCurvePolygon",
+                "20047": "MgCurvePolygonCollection",
+                "20012": "MgCurveRing",
+                "20052": "MgCurveRingCollection",
+                "20013": "MgCurveSegment",
+                "20041": "MgCurveSegmentCollection",
+                "20014": "MgCurveString",
+                "20043": "MgCurveStringCollection",
+                "11751": "MgDataPropertyDefinition",
+                "11773": "MgDataReader",
+                "1256": "MgDateTime",
+                "1506": "MgDateTimeException",
+                "10256": "MgDateTimeProperty",
+                "30003": "MgDbException",
+                "30004": "MgDbXmlException",
+                "1507": "MgDecryptionException",
+                "11775": "MgDeleteFeatures",
+                "1508": "MgDirectoryNotFoundException",
+                "1000": "MgDisposableCollection",
+                "1509": "MgDivideByZeroException",
+                "1510": "MgDomainException",
+                "10257": "MgDoubleProperty",
+                "30700": "MgDrawingService",
+                "1511": "MgDuplicateDirectoryException",
+                "1512": "MgDuplicateFileException",
+                "30005": "MgDuplicateGroupException",
+                "30006": "MgDuplicateNameException",
+                "1513": "MgDuplicateObjectException",
+                "30007": "MgDuplicateParameterException",
+                "30008": "MgDuplicateRepositoryException",
+                "10501": "MgDuplicateResourceDataException",
+                "10502": "MgDuplicateResourceException",
+                "30009": "MgDuplicateRoleException",
+                "30010": "MgDuplicateServerException",
+                "30011": "MgDuplicateSessionException",
+                "30012": "MgDuplicateUserException",
+                "30013": "MgDwfException",
+                "30014": "MgDwfSectionNotFoundException",
+                "30015": "MgDwfSectionResourceNotFoundException",
+                "30901": "MgDwfVersion",
+                "10503": "MgEmptyFeatureSetException",
+                "1514": "MgEncryptionException",
+                "30016": "MgEndOfStreamException",
+                "20001": "MgEnvelope",
+                "1515": "MgException",
+                "10504": "MgFdoException",
+                "11772": "MgFeatureAggregateOptions",
+                "11774": "MgFeatureCommandCollection",
+                "31001": "MgFeatureInformation",
+                "11764": "MgFeatureProperty",
+                "11771": "MgFeatureQueryOptions",
+                "11753": "MgFeatureReader",
+                "11778": "MgFeatureSchema",
+                "11779": "MgFeatureSchemaCollection",
+                "11754": "MgFeatureService",
+                "10505": "MgFeatureServiceException",
+                "11786": "MgFileFeatureSourceParams",
+                "1516": "MgFileIoException",
+                "1517": "MgFileNotFoundException",
+                "20016": "MgGeometricEntity",
+                "11756": "MgGeometricPropertyDefinition",
+                "20019": "MgGeometry",
+                "20020": "MgGeometryCollection",
+                "20021": "MgGeometryComponent",
+                "21006": "MgGeometryException",
+                "20002": "MgGeometryFactory",
+                "11758": "MgGeometryProperty",
+                "11785": "MgGeometryTypeInfo",
+                "30018": "MgGroupNotFoundException",
+                "11782": "MgGwsFeatureReader",
+                "1518": "MgIndexOutOfRangeException",
+                "11776": "MgInsertFeatures",
+                "10258": "MgInt16Property",
+                "10259": "MgInt32Property",
+                "10260": "MgInt64Property",
+                "10000": "MgIntCollection",
+                "1519": "MgInvalidArgumentException",
+                "1520": "MgInvalidCastException",
+                "21007": "MgInvalidCoordinateSystemException",
+                "21008": "MgInvalidCoordinateSystemTypeException",
+                "21009": "MgInvalidCoordinateSystemUnitsException",
+                "30019": "MgInvalidDwfPackageException",
+                "30020": "MgInvalidDwfSectionException",
+                "30021": "MgInvalidFeatureSourceException",
+                "30022": "MgInvalidIpAddressException",
+                "30023": "MgInvalidLicenseException",
+                "30024": "MgInvalidLogEntryException",
+                "10507": "MgInvalidMapDefinitionException",
+                "1522": "MgInvalidOperationException",
+                "30026": "MgInvalidPasswordException",
+                "30027": "MgInvalidPrintLayoutFontSizeUnitsException",
+                "30028": "MgInvalidPrintLayoutPositionUnitsException",
+                "30029": "MgInvalidPrintLayoutSizeUnitsException",
+                "1523": "MgInvalidPropertyTypeException",
+                "10508": "MgInvalidRepositoryNameException",
+                "10509": "MgInvalidRepositoryTypeException",
+                "10510": "MgInvalidResourceDataNameException",
+                "10511": "MgInvalidResourceDataTypeException",
+                "10512": "MgInvalidResourceNameException",
+                "10513": "MgInvalidResourcePathException",
+                "10514": "MgInvalidResourcePreProcessingTypeException",
+                "10515": "MgInvalidResourceTypeException",
+                "30031": "MgInvalidServerNameException",
+                "1524": "MgInvalidStreamHeaderException",
+                "1525": "MgIoException",
+                "31300": "MgKmlService",
+                "30501": "MgLayer",
+                "12003": "MgLayerBase",
+                "12002": "MgLayerCollection",
+                "12001": "MgLayerGroup",
+                "12004": "MgLayerGroupCollection",
+                "10517": "MgLayerNotFoundException",
+                "30904": "MgLayout",
+                "1526": "MgLengthException",
+                "30032": "MgLicenseException",
+                "30033": "MgLicenseExpiredException",
+                "20042": "MgLineString",
+                "20044": "MgLineStringCollection",
+                "20023": "MgLinearRing",
+                "20053": "MgLinearRingCollection",
+                "20024": "MgLinearSegment",
+                "1527": "MgLogicException",
+                "11766": "MgLongTransactionReader",
+                "30500": "MgMap",
+                "12000": "MgMapBase",
+                "12005": "MgMapCollection",
+                "30905": "MgMapPlot",
+                "30906": "MgMapPlotCollection",
+                "30900": "MgMappingService",
+                "20029": "MgMultiCurvePolygon",
+                "20030": "MgMultiCurveString",
+                "20031": "MgMultiGeometry",
+                "20032": "MgMultiLineString",
+                "20033": "MgMultiPoint",
+                "20034": "MgMultiPolygon",
+                "1528": "MgNotFiniteNumberException",
+                "1529": "MgNotImplementedException",
+                "1530": "MgNullArgumentException",
+                "1531": "MgNullPropertyValueException",
+                "1532": "MgNullReferenceException",
+                "1533": "MgObjectNotFoundException",
+                "11759": "MgObjectPropertyDefinition",
+                "30035": "MgOperationProcessingException",
+                "1534": "MgOutOfMemoryException",
+                "1535": "MgOutOfRangeException",
+                "1536": "MgOverflowException",
+                "30604": "MgPackageStatusInformation",
+                "11788": "MgParameter",
+                "10004": "MgParameterCollection",
+                "30036": "MgParameterNotFoundException",
+                "30037": "MgPathTooLongException",
+                "1537": "MgPlatformNotSupportedException",
+                "30902": "MgPlotSpecification",
+                "20000": "MgPoint",
+                "20045": "MgPointCollection",
+                "20035": "MgPolygon",
+                "20046": "MgPolygonCollection",
+                "30039": "MgPortNotAvailableException",
+                "30040": "MgPrintToScaleModeNotSelectedException",
+                "31400": "MgProfilingService",
+                "2000": "MgProperty",
+                "1002": "MgPropertyCollection",
+                "2002": "MgPropertyDefinition",
+                "10001": "MgPropertyDefinitionCollection",
+                "11769": "MgRaster",
+                "11770": "MgRasterProperty",
+                "11768": "MgRasterPropertyDefinition",
+                "12006": "MgReadOnlyLayerCollection",
+                "20037": "MgRegion",
+                "31002": "MgRenderingOptions",
+                "31000": "MgRenderingService",
+                "30041": "MgRepositoryCreationFailedException",
+                "30042": "MgRepositoryNotFoundException",
+                "30043": "MgRepositoryNotOpenException",
+                "30044": "MgRepositoryOpenFailedException",
+                "11526": "MgResource",
+                "10518": "MgResourceBusyException",
+                "10519": "MgResourceDataNotFoundException",
+                "11500": "MgResourceIdentifier",
+                "10520": "MgResourceNotFoundException",
+                "11501": "MgResourceService",
+                "1540": "MgResourceTagNotFoundException",
+                "1538": "MgResourcesException",
+                "1539": "MgResourcesLoadFailedException",
+                "20038": "MgRing",
+                "30045": "MgRoleNotFoundException",
+                "1541": "MgRuntimeException",
+                "30502": "MgSelection",
+                "12007": "MgSelectionBase",
+                "30607": "MgServerAdmin",
+                "30046": "MgServerNotFoundException",
+                "30047": "MgServerNotOnlineException",
+                "11251": "MgService",
+                "10521": "MgServiceNotAvailableException",
+                "10522": "MgServiceNotSupportedException",
+                "30048": "MgSessionExpiredException",
+                "30052": "MgSessionNotFoundException",
+                "10261": "MgSingleProperty",
+                "30605": "MgSite",
+                "30601": "MgSiteConnection",
+                "30608": "MgSiteInfo",
+                "11761": "MgSpatialContextReader",
+                "11762": "MgSqlDataReader",
+                "1542": "MgStreamIoException",
+                "1003": "MgStringCollection",
+                "2001": "MgStringProperty",
+                "10003": "MgStringPropertyCollection",
+                "1543": "MgSystemException",
+                "1544": "MgTemporaryFileNotAvailableException",
+                "1545": "MgThirdPartyException",
+                "31200": "MgTileService",
+                "11787": "MgTransaction",
+                "30049": "MgUnauthorizedAccessException",
+                "1547": "MgUnclassifiedException",
+                "1548": "MgUnderflowException",
+                "30056": "MgUnknownTileProviderException",
+                "30057": "MgUnsupportedTileProviderException",
+                "11777": "MgUpdateFeatures",
+                "30050": "MgUriFormatException",
+                "30606": "MgUserInformation",
+                "10523": "MgUserNotFoundException",
+                "11257": "MgWarnings",
+                "20040": "MgWktReaderWriter",
+                "1549": "MgXmlException",
+                "1550": "MgXmlParserException"
+            }
+        },
+        {
+            "Name": "Web",
+            "Classes": {
+                "20004": "MgAgfReaderWriter",
+                "20005": "MgAggregateGeometry",
+                "1500": "MgApplicationException",
+                "20006": "MgArcSegment",
+                "1501": "MgArgumentOutOfRangeException",
+                "10500": "MgArrayTypeMismatchException",
+                "30000": "MgAuthenticationFailedException",
+                "1005": "MgBatchPropertyCollection",
+                "10252": "MgBlobProperty",
+                "10253": "MgBooleanProperty",
+                "10254": "MgByteProperty",
+                "1250": "MgByteReader",
+                "1257": "MgByteSink",
+                "1251": "MgByteSource",
+                "11750": "MgClassDefinition",
+                "11780": "MgClassDefinitionCollection",
+                "1502": "MgClassNotFoundException",
+                "10255": "MgClobProperty",
+                "10250": "MgColor",
+                "1503": "MgConfigurationException",
+                "1504": "MgConfigurationLoadFailedException",
+                "1505": "MgConfigurationSaveFailedException",
+                "30001": "MgConnectionFailedException",
+                "30002": "MgConnectionNotOpenException",
+                "20003": "MgCoordinate",
+                "20009": "MgCoordinateIterator",
+                "20500": "MgCoordinateSystem",
+                "20504": "MgCoordinateSystemCatalog",
+                "20506": "MgCoordinateSystemCategory",
+                "20510": "MgCoordinateSystemCategoryDictionary",
+                "21000": "MgCoordinateSystemComputationFailedException",
+                "21001": "MgCoordinateSystemConversionFailedException",
+                "20507": "MgCoordinateSystemDatum",
+                "20512": "MgCoordinateSystemDatumDictionary",
+                "20511": "MgCoordinateSystemDictionary",
+                "20521": "MgCoordinateSystemDictionaryUtility",
+                "20509": "MgCoordinateSystemEllipsoid",
+                "20513": "MgCoordinateSystemEllipsoidDictionary",
+                "20514": "MgCoordinateSystemEnum",
+                "20515": "MgCoordinateSystemEnumInteger32",
+                "20501": "MgCoordinateSystemFactory",
+                "20516": "MgCoordinateSystemFilter",
+                "20517": "MgCoordinateSystemFilterInteger32",
+                "20505": "MgCoordinateSystemFormatConverter",
+                "20542": "MgCoordinateSystemGeodeticAnalyticalTransformDefParams",
+                "20543": "MgCoordinateSystemGeodeticInterpolationTransformDefParams",
+                "20544": "MgCoordinateSystemGeodeticMultipleRegressionTransformDefParams",
+                "20533": "MgCoordinateSystemGeodeticPath",
+                "20535": "MgCoordinateSystemGeodeticPathDictionary",
+                "20534": "MgCoordinateSystemGeodeticPathElement",
+                "20536": "MgCoordinateSystemGeodeticTransformDef",
+                "20540": "MgCoordinateSystemGeodeticTransformDefDictionary",
+                "20545": "MgCoordinateSystemGeodeticTransformGridFile",
+                "20508": "MgCoordinateSystemGeodeticTransformation",
+                "20532": "MgCoordinateSystemGridBase",
+                "20524": "MgCoordinateSystemGridBoundary",
+                "20529": "MgCoordinateSystemGridLine",
+                "20526": "MgCoordinateSystemGridLineCollection",
+                "20530": "MgCoordinateSystemGridRegion",
+                "20527": "MgCoordinateSystemGridRegionCollection",
+                "20525": "MgCoordinateSystemGridSpecification",
+                "20531": "MgCoordinateSystemGridTick",
+                "20528": "MgCoordinateSystemGridTickCollection",
+                "21002": "MgCoordinateSystemInitializationFailedException",
+                "21003": "MgCoordinateSystemLoadFailedException",
+                "20518": "MgCoordinateSystemMathComparator",
+                "20502": "MgCoordinateSystemMeasure",
+                "21004": "MgCoordinateSystemMeasureFailedException",
+                "20522": "MgCoordinateSystemMgrs",
+                "20520": "MgCoordinateSystemProjectionInformation",
+                "20503": "MgCoordinateSystemTransform",
+                "21005": "MgCoordinateSystemTransformFailedException",
+                "20519": "MgCoordinateSystemUnitInformation",
+                "20048": "MgCoordinateXY",
+                "20051": "MgCoordinateXYM",
+                "20049": "MgCoordinateXYZ",
+                "20050": "MgCoordinateXYZM",
+                "20010": "MgCurve",
+                "20011": "MgCurvePolygon",
+                "20047": "MgCurvePolygonCollection",
+                "20012": "MgCurveRing",
+                "20052": "MgCurveRingCollection",
+                "20013": "MgCurveSegment",
+                "20041": "MgCurveSegmentCollection",
+                "20014": "MgCurveString",
+                "20043": "MgCurveStringCollection",
+                "11751": "MgDataPropertyDefinition",
+                "11773": "MgDataReader",
+                "1256": "MgDateTime",
+                "1506": "MgDateTimeException",
+                "10256": "MgDateTimeProperty",
+                "30003": "MgDbException",
+                "30004": "MgDbXmlException",
+                "1507": "MgDecryptionException",
+                "11775": "MgDeleteFeatures",
+                "1508": "MgDirectoryNotFoundException",
+                "1000": "MgDisposableCollection",
+                "1509": "MgDivideByZeroException",
+                "1510": "MgDomainException",
+                "10257": "MgDoubleProperty",
+                "30700": "MgDrawingService",
+                "1511": "MgDuplicateDirectoryException",
+                "1512": "MgDuplicateFileException",
+                "30005": "MgDuplicateGroupException",
+                "30006": "MgDuplicateNameException",
+                "1513": "MgDuplicateObjectException",
+                "30007": "MgDuplicateParameterException",
+                "30008": "MgDuplicateRepositoryException",
+                "10501": "MgDuplicateResourceDataException",
+                "10502": "MgDuplicateResourceException",
+                "30009": "MgDuplicateRoleException",
+                "30010": "MgDuplicateServerException",
+                "30011": "MgDuplicateSessionException",
+                "30012": "MgDuplicateUserException",
+                "30013": "MgDwfException",
+                "30014": "MgDwfSectionNotFoundException",
+                "30015": "MgDwfSectionResourceNotFoundException",
+                "30901": "MgDwfVersion",
+                "10503": "MgEmptyFeatureSetException",
+                "1514": "MgEncryptionException",
+                "30016": "MgEndOfStreamException",
+                "20001": "MgEnvelope",
+                "1515": "MgException",
+                "10504": "MgFdoException",
+                "11772": "MgFeatureAggregateOptions",
+                "11774": "MgFeatureCommandCollection",
+                "31001": "MgFeatureInformation",
+                "11764": "MgFeatureProperty",
+                "11771": "MgFeatureQueryOptions",
+                "11753": "MgFeatureReader",
+                "11778": "MgFeatureSchema",
+                "11779": "MgFeatureSchemaCollection",
+                "11754": "MgFeatureService",
+                "10505": "MgFeatureServiceException",
+                "11786": "MgFileFeatureSourceParams",
+                "1516": "MgFileIoException",
+                "1517": "MgFileNotFoundException",
+                "20016": "MgGeometricEntity",
+                "11756": "MgGeometricPropertyDefinition",
+                "20019": "MgGeometry",
+                "20020": "MgGeometryCollection",
+                "20021": "MgGeometryComponent",
+                "21006": "MgGeometryException",
+                "20002": "MgGeometryFactory",
+                "11758": "MgGeometryProperty",
+                "11785": "MgGeometryTypeInfo",
+                "30018": "MgGroupNotFoundException",
+                "11782": "MgGwsFeatureReader",
+                "40000": "MgHttpHeader",
+                "40006": "MgHttpPrimitiveValue",
+                "40004": "MgHttpRequest",
+                "40002": "MgHttpRequestMetadata",
+                "40001": "MgHttpRequestParam",
+                "40005": "MgHttpResponse",
+                "40003": "MgHttpResult",
+                "1518": "MgIndexOutOfRangeException",
+                "11776": "MgInsertFeatures",
+                "10258": "MgInt16Property",
+                "10259": "MgInt32Property",
+                "10260": "MgInt64Property",
+                "10000": "MgIntCollection",
+                "1519": "MgInvalidArgumentException",
+                "1520": "MgInvalidCastException",
+                "21007": "MgInvalidCoordinateSystemException",
+                "21008": "MgInvalidCoordinateSystemTypeException",
+                "21009": "MgInvalidCoordinateSystemUnitsException",
+                "30019": "MgInvalidDwfPackageException",
+                "30020": "MgInvalidDwfSectionException",
+                "30021": "MgInvalidFeatureSourceException",
+                "30022": "MgInvalidIpAddressException",
+                "30023": "MgInvalidLicenseException",
+                "30024": "MgInvalidLogEntryException",
+                "10507": "MgInvalidMapDefinitionException",
+                "1522": "MgInvalidOperationException",
+                "30026": "MgInvalidPasswordException",
+                "30027": "MgInvalidPrintLayoutFontSizeUnitsException",
+                "30028": "MgInvalidPrintLayoutPositionUnitsException",
+                "30029": "MgInvalidPrintLayoutSizeUnitsException",
+                "1523": "MgInvalidPropertyTypeException",
+                "10508": "MgInvalidRepositoryNameException",
+                "10509": "MgInvalidRepositoryTypeException",
+                "10510": "MgInvalidResourceDataNameException",
+                "10511": "MgInvalidResourceDataTypeException",
+                "10512": "MgInvalidResourceNameException",
+                "10513": "MgInvalidResourcePathException",
+                "10514": "MgInvalidResourcePreProcessingTypeException",
+                "10515": "MgInvalidResourceTypeException",
+                "30031": "MgInvalidServerNameException",
+                "1524": "MgInvalidStreamHeaderException",
+                "1525": "MgIoException",
+                "31300": "MgKmlService",
+                "30501": "MgLayer",
+                "12003": "MgLayerBase",
+                "12002": "MgLayerCollection",
+                "12001": "MgLayerGroup",
+                "12004": "MgLayerGroupCollection",
+                "10517": "MgLayerNotFoundException",
+                "30904": "MgLayout",
+                "1526": "MgLengthException",
+                "30032": "MgLicenseException",
+                "30033": "MgLicenseExpiredException",
+                "20042": "MgLineString",
+                "20044": "MgLineStringCollection",
+                "20023": "MgLinearRing",
+                "20053": "MgLinearRingCollection",
+                "20024": "MgLinearSegment",
+                "1527": "MgLogicException",
+                "11766": "MgLongTransactionReader",
+                "30500": "MgMap",
+                "12000": "MgMapBase",
+                "12005": "MgMapCollection",
+                "30905": "MgMapPlot",
+                "30906": "MgMapPlotCollection",
+                "30900": "MgMappingService",
+                "20029": "MgMultiCurvePolygon",
+                "20030": "MgMultiCurveString",
+                "20031": "MgMultiGeometry",
+                "20032": "MgMultiLineString",
+                "20033": "MgMultiPoint",
+                "20034": "MgMultiPolygon",
+                "1528": "MgNotFiniteNumberException",
+                "1529": "MgNotImplementedException",
+                "1530": "MgNullArgumentException",
+                "1531": "MgNullPropertyValueException",
+                "1532": "MgNullReferenceException",
+                "1533": "MgObjectNotFoundException",
+                "11759": "MgObjectPropertyDefinition",
+                "30035": "MgOperationProcessingException",
+                "1534": "MgOutOfMemoryException",
+                "1535": "MgOutOfRangeException",
+                "1536": "MgOverflowException",
+                "30604": "MgPackageStatusInformation",
+                "11788": "MgParameter",
+                "10004": "MgParameterCollection",
+                "30036": "MgParameterNotFoundException",
+                "30037": "MgPathTooLongException",
+                "1537": "MgPlatformNotSupportedException",
+                "30902": "MgPlotSpecification",
+                "20000": "MgPoint",
+                "20045": "MgPointCollection",
+                "20035": "MgPolygon",
+                "20046": "MgPolygonCollection",
+                "30039": "MgPortNotAvailableException",
+                "30040": "MgPrintToScaleModeNotSelectedException",
+                "31400": "MgProfilingService",
+                "2000": "MgProperty",
+                "1002": "MgPropertyCollection",
+                "2002": "MgPropertyDefinition",
+                "10001": "MgPropertyDefinitionCollection",
+                "11769": "MgRaster",
+                "11770": "MgRasterProperty",
+                "11768": "MgRasterPropertyDefinition",
+                "12006": "MgReadOnlyLayerCollection",
+                "20037": "MgRegion",
+                "31002": "MgRenderingOptions",
+                "31000": "MgRenderingService",
+                "30041": "MgRepositoryCreationFailedException",
+                "30042": "MgRepositoryNotFoundException",
+                "30043": "MgRepositoryNotOpenException",
+                "30044": "MgRepositoryOpenFailedException",
+                "11526": "MgResource",
+                "10518": "MgResourceBusyException",
+                "10519": "MgResourceDataNotFoundException",
+                "11500": "MgResourceIdentifier",
+                "10520": "MgResourceNotFoundException",
+                "11501": "MgResourceService",
+                "1540": "MgResourceTagNotFoundException",
+                "1538": "MgResourcesException",
+                "1539": "MgResourcesLoadFailedException",
+                "20038": "MgRing",
+                "30045": "MgRoleNotFoundException",
+                "1541": "MgRuntimeException",
+                "30502": "MgSelection",
+                "12007": "MgSelectionBase",
+                "30607": "MgServerAdmin",
+                "30046": "MgServerNotFoundException",
+                "30047": "MgServerNotOnlineException",
+                "11251": "MgService",
+                "10521": "MgServiceNotAvailableException",
+                "10522": "MgServiceNotSupportedException",
+                "30048": "MgSessionExpiredException",
+                "30052": "MgSessionNotFoundException",
+                "10261": "MgSingleProperty",
+                "30605": "MgSite",
+                "30601": "MgSiteConnection",
+                "30608": "MgSiteInfo",
+                "11761": "MgSpatialContextReader",
+                "11762": "MgSqlDataReader",
+                "1542": "MgStreamIoException",
+                "1003": "MgStringCollection",
+                "2001": "MgStringProperty",
+                "10003": "MgStringPropertyCollection",
+                "1543": "MgSystemException",
+                "1544": "MgTemporaryFileNotAvailableException",
+                "1545": "MgThirdPartyException",
+                "31200": "MgTileService",
+                "11787": "MgTransaction",
+                "30049": "MgUnauthorizedAccessException",
+                "1547": "MgUnclassifiedException",
+                "1548": "MgUnderflowException",
+                "30056": "MgUnknownTileProviderException",
+                "30057": "MgUnsupportedTileProviderException",
+                "11777": "MgUpdateFeatures",
+                "30050": "MgUriFormatException",
+                "30606": "MgUserInformation",
+                "10523": "MgUserNotFoundException",
+                "11257": "MgWarnings",
+                "50005": "MgWebBufferCommand",
+                "50000": "MgWebCommand",
+                "50012": "MgWebCommandCollection",
+                "50015": "MgWebCommandWidget",
+                "50025": "MgWebContextMenu",
+                "50016": "MgWebFlyoutWidget",
+                "50009": "MgWebGetPrintablePageCommand",
+                "50011": "MgWebHelpCommand",
+                "50022": "MgWebInformationPane",
+                "50003": "MgWebInvokeScriptCommand",
+                "50004": "MgWebInvokeUrlCommand",
+                "50026": "MgWebLayout",
+                "50008": "MgWebMeasureCommand",
+                "50007": "MgWebPrintCommand",
+                "50002": "MgWebSearchCommand",
+                "50006": "MgWebSelectWithinCommand",
+                "50014": "MgWebSeparatorWidget",
+                "50023": "MgWebTaskBar",
+                "50017": "MgWebTaskBarWidget",
+                "50024": "MgWebTaskPane",
+                "50021": "MgWebToolBar",
+                "50019": "MgWebUiPane",
+                "50020": "MgWebUiSizablePane",
+                "50001": "MgWebUiTargetCommand",
+                "50010": "MgWebViewOptionsCommand",
+                "50013": "MgWebWidget",
+                "50018": "MgWebWidgetCollection",
+                "20040": "MgWktReaderWriter",
+                "1549": "MgXmlException",
+                "1550": "MgXmlParserException"
+            }
+        }
+    ]
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Program.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Program.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/Program.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,145 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Collections.Generic;
+using Newtonsoft.Json;
+using System.Text;
+
+namespace ClassMapGen
+{
+    public class ModuleDef
+    {
+        public string Name { get; set; }
+
+        public Dictionary<int, string> Classes { get; set; }
+    }
+
+    public class MasterClassMap
+    {
+        public List<ModuleDef> Modules { get; set; }
+    }
+
+    class Program
+    {
+        static int Main(string[] args)
+        {
+            string srcBase = null;
+            if (args.Length == 1)
+            {
+                srcBase = args[0];
+            }
+            if (!Directory.Exists(srcBase))
+            {
+                Console.WriteLine($"ERROR: Source base directory not found: {srcBase}");
+                return 1;
+            }
+
+            string phpOut = Path.Combine(srcBase, "Bindings/Php/PhpClassMap.cpp");
+            string dotNetOut = Path.Combine(srcBase, "Bindings/DotNet/MapGuideDotNetApi/custom/MgClassMap.cs");
+            string javaOut = Path.Combine(srcBase, "Bindings/Java/org/osgeo/mapguide/ObjectFactory.java");
+
+            var phpTpl = new StringBuilder(File.ReadAllText("Data/Templates/php.txt"));
+            var dotNetTpl = new StringBuilder(File.ReadAllText("Data/Templates/dotnet.txt"));
+            var javaTpl = new StringBuilder(File.ReadAllText("Data/Templates/java.txt"));
+
+            MasterClassMap clsMap = JsonConvert.DeserializeObject<MasterClassMap>(File.ReadAllText("Data/classmap_master.json"));
+
+            var foundation = clsMap.Modules.FirstOrDefault(m => m.Name == "Foundation");
+            var geometry = clsMap.Modules.FirstOrDefault(m => m.Name == "Geometry");
+            var platform = clsMap.Modules.FirstOrDefault(m => m.Name == "PlatformBase");
+            var mapguide = clsMap.Modules.FirstOrDefault(m => m.Name == "MapGuideCommon");
+            var web = clsMap.Modules.FirstOrDefault(m => m.Name == "Web");
+
+            int foundationAdded = 0;
+            int geometryAdded = 0;
+            int platformAdded = 0;
+            int mapguideAdded = 0;
+            int webAdded = 0;
+
+            var classMapMaster = new Dictionary<int, string>();
+            var classMapMasterReverse = new SortedDictionary<string, int>();
+
+            foreach (var kvp in foundation.Classes)
+            {
+                if (classMapMaster.TryAdd(kvp.Key, kvp.Value))
+                    foundationAdded++;
+            }
+            foreach (var kvp in geometry.Classes)
+            {
+                if (classMapMaster.TryAdd(kvp.Key, kvp.Value))
+                    geometryAdded++;
+            }
+            foreach (var kvp in platform.Classes)
+            {
+                if (classMapMaster.TryAdd(kvp.Key, kvp.Value))
+                    platformAdded++;
+            }
+            foreach (var kvp in mapguide.Classes)
+            {
+                if (classMapMaster.TryAdd(kvp.Key, kvp.Value))
+                    mapguideAdded++;
+            }
+            foreach (var kvp in web.Classes)
+            {
+                if (classMapMaster.TryAdd(kvp.Key, kvp.Value))
+                    webAdded++;
+            }
+
+            //Now populate reverse map
+            foreach (var kvp in classMapMaster)
+            {
+                classMapMasterReverse[kvp.Value] = kvp.Key;
+            }
+
+            Console.WriteLine($"Foundation: {foundationAdded} classes added");
+            Console.WriteLine($"Geometry: {geometryAdded} classes added");
+            Console.WriteLine($"PlatformBase: {platformAdded} classes added");
+            Console.WriteLine($"MapGuideCommon: {mapguideAdded} classes added");
+            Console.WriteLine($"Web: {webAdded} classes added");
+
+            Console.WriteLine($"Class map has {classMapMaster.Count} classes");
+
+            var phpClassMaps = new StringBuilder();
+            string PHP_INDENT = "    ";
+            string DOTNET_INDENT = "            ";
+            string JAVA_INDENT = "            ";
+
+            //PHP
+            foreach (var kvp in classMapMasterReverse)
+            {
+                phpClassMaps.AppendLine($"{PHP_INDENT}classNameMap[{kvp.Value}] = \"_p_{kvp.Key}\";");
+            }
+
+            phpTpl.Replace("$CLASS_NAME_MAP_BODY$", phpClassMaps.ToString());
+
+            var dotNetClassMaps = new StringBuilder();
+            
+            //.net
+            foreach (var kvp in classMapMasterReverse)
+            {
+                dotNetClassMaps.AppendLine($"{DOTNET_INDENT}classNameMap[{kvp.Value}] = \"OSGeo.MapGuide.{kvp.Key}\";");
+            }
+
+            dotNetTpl.Replace("$CLASS_NAME_MAP_BODY$", dotNetClassMaps.ToString());
+
+            var javaClassMaps = new StringBuilder(); 
+
+            //Java
+            foreach (var kvp in classMapMasterReverse)
+            {
+                javaClassMaps.AppendLine($"{JAVA_INDENT}classMap.put(new Integer({kvp.Value}), getSWIGCtor(\"{kvp.Key}\"));");
+            }
+
+            javaTpl.Replace("$CLASS_NAME_MAP_BODY$", javaClassMaps.ToString());
+
+            File.WriteAllText(phpOut, phpTpl.ToString());
+            Console.WriteLine($"Written: {phpOut}");
+            File.WriteAllText(dotNetOut, dotNetTpl.ToString());
+            Console.WriteLine($"Written: {dotNetOut}");
+            File.WriteAllText(javaOut, javaTpl.ToString());
+            Console.WriteLine($"Written: {javaOut}");
+
+            return 0;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/Common/helpers.h
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/Common/helpers.h	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/Common/helpers.h	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,177 @@
+#ifndef FS_HELPER_H
+#define FS_HELPER_H
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sys/stat.h> // stat
+#include <errno.h>    // errno, ENOENT, EEXIST
+#if defined(_WIN32)
+#include <direct.h>   // _mkdir
+#endif
+
+// Based on the following code samples:
+// https://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux
+// https://stackoverflow.com/questions/1494399/how-do-i-search-find-and-replace-in-a-standard-string
+// https://stackoverflow.com/questions/874134/find-if-string-ends-with-another-string-in-c
+// http://www.cplusplus.com/doc/tutorial/files/
+
+bool file_exists(const std::string &path)
+{
+#if defined(_WIN32)
+    struct _stat info;
+    if (_stat(path.c_str(), &info) != 0)
+#else
+    struct stat info;
+    if (stat(path.c_str(), &info) != 0)
+#endif
+        return false;
+    else
+        return true;
+}
+
+bool directory_exists(const std::string& path)
+{
+#if defined(_WIN32)
+    struct _stat info;
+    if (_stat(path.c_str(), &info) != 0)
+    {
+        return false;
+    }
+    return (info.st_mode & _S_IFDIR) != 0;
+#else 
+    struct stat info;
+    if (stat(path.c_str(), &info) != 0)
+    {
+        return false;
+    }
+    return (info.st_mode & S_IFDIR) != 0;
+#endif
+}
+
+bool make_path(const std::string& path)
+{
+#if defined(_WIN32)
+    int ret = _mkdir(path.c_str());
+#else
+    mode_t mode = 0755;
+    int ret = mkdir(path.c_str(), mode);
+#endif
+    if (ret == 0)
+        return true;
+
+    switch (errno)
+    {
+    case ENOENT:
+        // parent didn't exist, try to create it
+        {
+            int pos = path.find_last_of('/');
+            if (pos == std::string::npos)
+#if defined(_WIN32)
+                pos = path.find_last_of('\\');
+            if (pos == std::string::npos)
+#endif
+                return false;
+            if (!make_path( path.substr(0, pos) ))
+                return false;
+        }
+        // now, try to create again
+#if defined(_WIN32)
+        return 0 == _mkdir(path.c_str());
+#else 
+        return 0 == mkdir(path.c_str(), mode);
+#endif
+
+    case EEXIST:
+        // done!
+        return directory_exists(path);
+
+    default:
+        return false;
+    }
+}
+
+bool str_ends_with(std::string const &fullString, std::string const &ending)
+{
+    if (fullString.length() >= ending.length()) {
+        return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
+    } else {
+        return false;
+    }
+}
+
+void str_replace(std::string& str,
+                 const std::string& oldStr,
+                 const std::string& newStr)
+{
+    std::string::size_type pos = 0u;
+    while((pos = str.find(oldStr, pos)) != std::string::npos) {
+        str.replace(pos, oldStr.length(), newStr);
+        pos += newStr.length();
+    }
+}
+
+bool replace_content_between(std::string& content,
+                             const std::string& start,
+                             const std::string& end,
+                             const std::string& replace)
+{
+    bool didAtLeastOneReplacement = false;
+    size_t pos_start = content.find(start);
+    while (std::string::npos != pos_start)
+    {
+        size_t pos_end = content.find(end, pos_start);
+        if (std::string::npos != pos_end)
+        {
+            size_t pos_repl = pos_start + replace.length() - 1;
+            content.replace(pos_repl, pos_end - pos_repl, replace);
+            didAtLeastOneReplacement = true;
+
+            pos_start = content.find(start, pos_end);
+        }
+    }
+    return didAtLeastOneReplacement;
+}
+
+bool write_all_text(const std::string& path, const std::string& content)
+{
+    std::ofstream myfile(path.c_str());
+    if (myfile.is_open())
+    {
+        myfile << content;
+        myfile.close();
+        return true;
+    }
+    return false;
+}
+
+bool read_all_text(const std::string& path, std::string& content)
+{
+    std::string line;
+    std::ifstream myfile(path.c_str());
+    if (myfile.is_open())
+    {
+        while (std::getline(myfile,line))
+        {
+            content += line;
+            content += "\n";
+        }
+        myfile.close();
+        return true;
+    }
+    return false;
+}
+
+std::string combine_paths(const std::string& basePath, const std::string& part)
+{
+    if (basePath[basePath.length() - 1] == '/' || basePath[basePath.length() - 1] == '\\')
+    {
+        return basePath + part;
+    }
+    else
+    {
+        return basePath + "/" + part;
+    }
+}
+
+#endif
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/CMakeLists.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/CMakeLists.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/CMakeLists.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,22 @@
+project(PhpPostProcess)
+
+include_directories(
+    "${CMAKE_CURRENT_SOURCE_DIR}"
+    "${CMAKE_CURRENT_SOURCE_DIR}/../Common"
+)
+
+set(PhpPostProcess_SRCS
+    main.cpp
+)
+
+add_executable(PhpPostProcess ${PhpPostProcess_SRCS})
+
+if (MSVC)
+    set_target_properties(PhpPostProcess PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TOOL_INSTALL_PATH} )
+    set_target_properties(PhpPostProcess PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${TOOL_INSTALL_PATH} )
+    set_target_properties(PhpPostProcess PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${TOOL_INSTALL_PATH} )
+endif (MSVC)
+
+if (UNIX)
+    install(TARGETS PhpPostProcess DESTINATION ${TOOL_INSTALL_PATH})
+endif (UNIX)
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/PhpPostProcess.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/PhpPostProcess.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/PhpPostProcess.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <LangVersion>latest</LangVersion>
+  </PropertyGroup>
+
+</Project>

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/Program.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/Program.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/Program.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PhpPostProcess
+{
+    class Program
+    {
+        static async Task<int> Main(string[] args)
+        {
+            string srcBase = null;
+            if (args.Length == 1)
+            {
+                srcBase = args[0];
+            }
+            if (!Directory.Exists(srcBase))
+            {
+                Console.WriteLine($"ERROR: Source base directory not found: {srcBase}");
+                return 1;
+            }
+
+            string phpApi = Path.Combine(srcBase, "MapGuideApi.php");
+            var originalLines = await File.ReadAllLinesAsync(phpApi);
+            var buffer = new StringBuilder();
+
+            var traits = new HashSet<string>();
+            foreach (var line in originalLines)
+            {
+                CheckTraits(line, traits);
+                buffer.AppendLine(line);
+                ApplyTraits(line, buffer, traits);
+            }
+
+            await File.WriteAllTextAsync(phpApi, buffer.ToString());
+            return 0;
+        }
+
+        static void ApplyTraits(ReadOnlySpan<char> line, StringBuilder buffer, HashSet<string> traits)
+        {
+            var idx = line.IndexOf("class ");
+            if (idx >= 0) //Found a class definition
+            {
+                var slice = line.Slice(idx + 6 /* "class " */);
+                var nIdx = slice.IndexOf(" ");
+                if (nIdx >= 0)
+                {
+                    var substr = slice.Slice(0, nIdx);
+                    string className = new string(substr);
+                    if (traits.Contains(className))
+                    {
+                        buffer.AppendLine("    use " + className + "Patched; /* Inserted by PhpPostProcess tool */");
+                        Console.WriteLine("Applying trait for: " + className);
+                    }
+                }
+            }
+        }
+
+        static void CheckTraits(ReadOnlySpan<char> line, HashSet<string> traits)
+        {
+            var idx = line.IndexOf("trait ");
+            if (idx >= 0)
+            {
+                var slice = line.Slice(idx + 6 /* "trait " */);
+                var nIdx = slice.IndexOf(" ");
+                if (nIdx > 7)
+                {
+                    //Traits follow a naming convention of *ClassName*Patched
+                    var substr = slice.Slice(0, nIdx - 7 /* "Patched " */);
+
+                    string trait = new string(substr);
+                    traits.Add(trait);
+                    Console.WriteLine("Found trait: " + trait);
+                }
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/main.cpp
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/main.cpp	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/main.cpp	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,98 @@
+#include <iostream>
+#include <set>
+//#include <vector>
+#include "helpers.h"
+
+void check_traits(const std::string& line, std::set<std::string>& traits)
+{
+    size_t pos = line.find("trait ");
+    if (pos != std::string::npos)
+    {
+        size_t pos_end = line.find("Patched ", pos + 6 /* "trait " */);
+        if (pos_end != std::string::npos)
+        {
+            std::string className = line.substr(pos + 6, pos_end - 6);
+            std::cout << "Found trait for class: " << className << std::endl;
+
+            traits.insert(className);
+        }
+    }
+}
+
+void apply_traits(const std::string& line, std::string& content, std::set<std::string>& traits)
+{
+    size_t pos = line.find("class ");
+    if (pos != std::string::npos)
+    {
+        size_t pos_class_start = pos + 6;
+        size_t pos_end = line.find(" ", pos_class_start /* "class " */);
+        if (pos_end != std::string::npos)
+        {
+            std::string className = line.substr(pos_class_start, pos_end - pos_class_start);
+            //std::cout << pos_class_start << ":" << pos_end << " Checking if trait exists for class: `" << className << "`" << std::endl;
+            if (traits.find(className) != traits.end())
+            {
+                content += "    use ";
+                content += className;
+                content += "Patched;  /* Inserted by PhpPostProcess tool */";
+                content += "\n";
+                std::cout << "Applied trait for class: " << className << std::endl;
+            }
+        }
+    }
+}
+
+int main(int argc, char **argv)
+{
+    /*
+    std::vector<std::string> lines;
+    lines.push_back("<?php");
+    lines.push_back("trait MgExceptionPatched {");
+    lines.push_back("}");
+    lines.push_back("class MgException {");
+    lines.push_back("    function getMessage() { }");
+    lines.push_back("}");
+    lines.push_back("class MgSiteConnection {");
+    lines.push_back("    function CreateService($serviceType) { }");
+    lines.push_back("}");
+    lines.push_back("?>");
+
+    std::set<std::string> traits;
+    std::string content;
+    for (std::vector<std::string>::iterator it = lines.begin(); it != lines.end(); it++)
+    {
+        std::string line = *it;
+        check_traits(line, traits);
+        content += line;
+        content += "\n";
+        apply_traits(line, content, traits);
+    }
+
+    std::cout << content << std::endl;
+    return 0;
+    */
+    if (argc != 2)
+    {
+        std::cout << "Usage: PhpPostProcess [path to MapGuideApi.php]" << std::endl;
+        return 1;
+    }
+
+    std::set<std::string> traits;
+    std::string content;
+
+    std::string line;
+    std::ifstream myfile(argv[1]);
+    if (myfile.is_open())
+    {
+        while (std::getline(myfile,line))
+        {
+            check_traits(line, traits);
+            content += line;
+            content += "\n";
+            apply_traits(line, content, traits);
+        }
+        myfile.close();
+        return 0;
+    }
+    return 1;
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/CMakeLists.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/CMakeLists.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/CMakeLists.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,22 @@
+project(StampVer)
+
+include_directories(
+    "${CMAKE_CURRENT_SOURCE_DIR}"
+    "${CMAKE_CURRENT_SOURCE_DIR}/../Common"
+)
+
+set(StampVer_SRCS
+    main.cpp
+)
+
+add_executable(StampVer ${StampVer_SRCS})
+
+if (MSVC)
+    set_target_properties(StampVer PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TOOL_INSTALL_PATH} )
+    set_target_properties(StampVer PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${TOOL_INSTALL_PATH} )
+    set_target_properties(StampVer PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${TOOL_INSTALL_PATH} )
+endif (MSVC)
+
+if (UNIX)
+    install(TARGETS StampVer DESTINATION ${TOOL_INSTALL_PATH})
+endif (UNIX)
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/Program.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/Program.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/Program.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,89 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace StampVer
+{
+    public class Program
+    {
+        class Arguments
+        {
+            public string SourceDir { get; set; }
+
+            public Version Version { get; set; }
+        }
+
+        static Arguments Parse(string [] args)
+        {
+            if (args.Length != 5)
+                throw new Exception($"Unexpected number of arguments. Expected 5. Got {args.Length}");
+
+            string sourceDir = args[0];
+            if (!Directory.Exists(sourceDir))
+                throw new DirectoryNotFoundException($"Source directory not found: {sourceDir}");
+
+            Version ver = new Version($"{args[1]}.{args[2]}.{args[3]}.{args[4]}");
+            return new Arguments()
+            {
+                SourceDir = sourceDir,
+                Version = ver
+            };
+        }
+
+        public static int Main(string [] args)
+        {
+            Arguments arg = null;
+
+            try
+            {
+                arg = Parse(args);
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine($"ERROR: {ex.Message}");
+                Console.WriteLine($"Usage: StampVer [source directory] [ver:major] [ver:minor] [ver:build] [ver:rev]");
+                return 1;
+            }
+
+            StampAssemblyInfo(arg.Version, GetFullPath(arg, "Bindings/DotNet/MapGuideDotNetApi/Properties/AssemblyInfo.cs"));
+            StampProjectFile(arg.Version, GetFullPath(arg, "Bindings/DotNet/MapGuideDotNetApi/MapGuideDotNetApi.csproj"));
+
+            return 0;
+        }
+        
+        static string GetFullPath(Arguments arg, string relPath)
+        {
+            var path = Path.Combine(arg.SourceDir, relPath);
+            return Path.GetFullPath(path);
+        }
+
+        static void StampProjectFile(Version ver, string path)
+        {
+            if (!File.Exists(path))
+            {
+                Console.WriteLine($"WARNING: File not found ({path}). Skipping");
+                return;
+            }
+            var asmVerRegx = new Regex(@"<Version>\d+\.\d+\.\d+\.\d+</Version>");
+            string asmVerReplace = $"<Version>{ver.ToString()}</Version>";
+            string content = asmVerRegx.Replace(File.ReadAllText(path), asmVerReplace);
+            File.WriteAllText(path, content);
+            Console.WriteLine($"Updated: {path}");
+        }
+
+        static void StampAssemblyInfo(Version ver, string path)
+        {
+            if (!File.Exists(path))
+            {
+                Console.WriteLine($"WARNING: File not found ({path}). Skipping");
+                return;
+            }
+            var asmVerRegx = new Regex(@"Version\(\""\d+\.\d+\.\d+\.\d+\""\)");
+            string asmVerReplace = $"Version(\"{ver.ToString()}\")";
+            string content = asmVerRegx.Replace(File.ReadAllText(path), asmVerReplace);
+            File.WriteAllText(path, content);
+            Console.WriteLine($"Updated: {path}");
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/StampVer.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/StampVer.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/StampVer.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
+  </PropertyGroup>
+
+</Project>

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/main.cpp
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/main.cpp	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/main.cpp	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,86 @@
+#include <iostream>
+#include "helpers.h"
+
+int stamp_project_file(const std::string& path, const std::string& version)
+{
+    if (!file_exists(path))
+    {
+        std::cout << "Project file not found: " << path << std::endl;
+        return 1;
+    }
+
+    std::string content;
+    if (!read_all_text(path, content))
+    {
+        std::cout << "Failed to read project file content" << std::endl;
+        return 1;
+    }
+
+    if (!replace_content_between(content, "<Version>", "</Version>", version))
+    {
+        std::cout << "No replacements made in project file" << std::endl;
+        return 1;
+    }
+
+    return 0;
+}
+
+int stamp_assembly_info(const std::string& path, const std::string& version)
+{
+    if (!file_exists(path))
+    {
+        std::cout << "AssemblyInfo file not found: " << path << std::endl;
+        return 1;
+    }
+
+    std::string content;
+    if (!read_all_text(path, content))
+    {
+        std::cout << "Failed to read AssemblyInfo file content" << std::endl;
+        return 1;
+    }
+
+    if (!replace_content_between(content, "Version(\"", "\")]", version))
+    {
+        std::cout << "No replacements made in AssemblyInfo file" << std::endl;
+        return 1;
+    }
+
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    /*
+    std::string content = "<Project>\n\t<Version>1.0.0.0</Version>\n</Project>";
+    std::string content2 = "using System.Runtime.CompilerServices;\n\n[assembly: Version(\"1.0.0.0\")]\n[assembly: AssemblyVersion(\"1.0.0.1\")]";
+
+    replace_content_between(content, "<Version>", "</Version>", "3.1.1.9389");
+    replace_content_between(content2, "Version(\"", "\")]", "3.1.1.9389");
+
+    std::cout << "Content:" << std::endl << content << std::endl;
+    std::cout << "Content2:" << std::endl << content2 << std::endl;
+    */
+    if (argc != 7)
+    {
+        std::cout << "Usage: StampVer [ver:major] [ver:minor] [ver:build] [ver:rev] [assembly_info_file] [.net project]" << std::endl;
+        return 1;
+    }
+
+    std::string version = argv[1];
+    version += ".";
+    version += argv[2];
+    version += ".";
+    version += argv[3];
+    version += ".";
+    version += argv[4];
+
+    std::string asmInfoPath = argv[5];
+    std::string projectFile = argv[6];
+
+    int ret = 0;
+    ret += stamp_assembly_info(asmInfoPath, version);
+    ret += stamp_project_file(projectFile, version);
+
+    return ret;
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/CMakeLists.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/CMakeLists.txt	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/CMakeLists.txt	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,22 @@
+project(SwigPrepare)
+
+include_directories(
+    "${CMAKE_CURRENT_SOURCE_DIR}"
+    "${CMAKE_CURRENT_SOURCE_DIR}/../Common"
+)
+
+set(SwigPrepare_SRCS
+    main.cpp
+)
+
+add_executable(SwigPrepare ${SwigPrepare_SRCS})
+
+if (MSVC)
+    set_target_properties(SwigPrepare PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TOOL_INSTALL_PATH} )
+    set_target_properties(SwigPrepare PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${TOOL_INSTALL_PATH} )
+    set_target_properties(SwigPrepare PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${TOOL_INSTALL_PATH} )
+endif (MSVC)
+
+if (UNIX)
+    install(TARGETS SwigPrepare DESTINATION ${TOOL_INSTALL_PATH})
+endif (UNIX)
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/Program.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/Program.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/Program.cs	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,132 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml;
+
+namespace SwigPrepare
+{
+    public class Program
+    {
+        static string GetFullPath(params string[] parts) => Path.GetFullPath(Path.Combine(parts));
+
+        static void FixHeaderNodes(XmlNodeList nodes, string sdkRoot)
+        {
+            foreach (XmlNode node in nodes)
+            {
+                string path = node.Attributes["path"].Value;
+
+                //Replace known relative paths
+                path.Replace("../../../Common", $"{sdkRoot}/Inc/Common")
+                    .Replace("../WebApp", $"{sdkRoot}/Inc/Web/WebApp")
+                    .Replace("../HttpHandler", $"{sdkRoot}/Inc/Web/HttpHandler");
+
+                node.Attributes["path"].Value = path;
+            }
+        }
+
+        public static int Main(string [] args)
+        {
+            if (args.Length != 2)
+            {
+                Console.WriteLine("Usage: SwigPrepare [sdk path] [target dir]");
+                return 1;
+            }
+
+            string sdkRoot = args[0];
+            string targetDir = args[1];
+            if (!Directory.Exists(sdkRoot))
+            {
+                Console.WriteLine($"Directory not found: {sdkRoot}");
+                return 1;
+            }
+            if (!Directory.Exists(targetDir))
+            {
+                Directory.CreateDirectory(targetDir);
+                Console.WriteLine($"Created directory: {targetDir}");
+            }
+
+            //Normalize on / as separator
+            sdkRoot = sdkRoot.Replace("\\", "/");
+
+            string fConstants = Path.GetFullPath(Path.Combine(sdkRoot, "SWIG/Constants.xml"));
+            string fMapGuideApiGen = Path.GetFullPath(Path.Combine(sdkRoot, "SWIG/MapGuideApiGen.xml"));
+
+            if (!File.Exists(fConstants))
+            {
+                Console.WriteLine($"File not found: {fConstants}");
+                return 1;
+            }
+
+            if (!File.Exists(fMapGuideApiGen))
+            {
+                Console.WriteLine($"File not found: {fMapGuideApiGen}");
+                return 1;
+            }
+            
+            StringBuilder sbConstants = new StringBuilder(File.ReadAllText(fConstants));
+            StringBuilder sbMapGuideApiGen = new StringBuilder(File.ReadAllText(fMapGuideApiGen));
+
+            sbMapGuideApiGen
+                //Add extra STRINGPARAM typedef for .net Core
+                .Replace("#if defined(PHP) || defined(JAVA)",
+                         "#if defined(PHP) || defined(JAVA) || defined(DOTNETCORE)")
+                //Patch STRINGPARAM typedef for PHP
+                .Replace("typedef char*         STRINGPARAM;",
+                         "typedef std::wstring STRINGPARAM;")
+                //Comment out class id includes
+                .Replace("%include \"../../../Common", "//%include \"../../../Common")
+                .Replace("%include \"../WebApp", "//%include \"../WebApp")
+                .Replace("%include \"../HttpHandler", "//%include \"../HttpHandler")
+                //Fix header relative paths
+                .Replace("<Header path=\"../../../Common", $"<Header path=\"{sdkRoot}/Inc/Common")
+                .Replace("<Header path=\"../WebApp", $"<Header path=\"{sdkRoot}/Inc/Web/WebApp")
+                .Replace("<Header path=\"../HttpHandler", $"<Header path=\"{sdkRoot}/Inc/Web/HttpHandler")
+                //#elseif must've been valid in our custom version of SWIG we're using. Not here
+                .Replace("#elseif", "#elif");
+
+            sbConstants
+                //Fix header relative paths
+                .Replace("<Header path=\"../../../Common", $"<Header path=\"{sdkRoot}/Inc/Common")
+                .Replace("<Header path=\"../WebApp", $"<Header path=\"{sdkRoot}/Inc/Web/WebApp")
+                .Replace("<Header path=\"../HttpHandler", $"<Header path=\"{sdkRoot}/Inc/Web/HttpHandler");
+
+            /*
+            XmlNodeList constHeaders = constants.GetElementsByTagName("Header");
+            XmlNodeList apigenHeaders = apigen.GetElementsByTagName("Header");
+
+            XmlNode swigInline = apigen.GetElementsByTagName("SwigInline")[0];
+            XmlNode cppInline = apigen.GetElementsByTagName("CppInline")[0];
+
+            //Add extra STRINGPARAM typedef for .net Core
+            StringBuilder cppInlineContent = new StringBuilder(cppInline.InnerText);
+            cppInlineContent
+                .Replace("#if defined(PHP) || defined(JAVA)", 
+                         "#if defined(PHP) || defined(JAVA) || (defined(DOTNETCORE) && !defined(_WIN32))");
+
+            cppInline.InnerText = cppInlineContent.ToString();
+
+            //Comment out class id includes
+            StringBuilder swigInlineContent = new StringBuilder(swigInline.InnerText);
+            swigInlineContent
+                .Replace("%include \"../../../Common", "//%include \"../../../Common")
+                .Replace("%include \"../WebApp", "//%include \"../WebApp")
+                .Replace("%include \"../HttpHandler", "//%include \"../HttpHandler");
+
+            swigInline.InnerText = swigInlineContent.ToString();
+
+            //Fix relative header paths
+            FixHeaderNodes(constHeaders, sdkRoot);
+            FixHeaderNodes(apigenHeaders, sdkRoot);
+            */
+
+            File.WriteAllText(GetFullPath(targetDir, "Constants.xml"), sbConstants.ToString());
+            Console.WriteLine($"Saved: {GetFullPath(targetDir, "Constants.xml")}");
+
+            File.WriteAllText(GetFullPath(targetDir, "MapGuideApiGen.xml"), sbMapGuideApiGen.ToString());
+            Console.WriteLine($"Saved: {GetFullPath(targetDir, "MapGuideApiGen.xml")}");
+
+            return 0;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/SwigPrepare.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/SwigPrepare.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/SwigPrepare.csproj	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
+  </PropertyGroup>
+
+</Project>

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/main.cpp
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/main.cpp	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/main.cpp	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,82 @@
+#include <iostream>
+#include "helpers.h"
+
+int main(int argc, char **argv)
+{
+    if (argc != 4)
+    {
+        std::cout << "Usage: SwigPrepare [sdk path] [sdk rel path] [target dir]" << std::endl;
+        return 1;
+    }
+
+    std::string sdkRoot = argv[1];
+    std::string sdkRelPath = argv[2];
+    std::string targetDir = argv[3];
+
+    if (!directory_exists(sdkRoot))
+    {
+        std::cout << "Directory not found: " << sdkRoot << std::endl;
+        return 1;
+    }
+    if (!directory_exists(targetDir))
+    {
+        make_path(targetDir);
+        std::cout << "Created directory: " << targetDir << std::endl;
+    }
+
+    //Normalize on / as separator
+    str_replace(sdkRoot, "\\", "/");
+
+    std::string fConstants = combine_paths(sdkRoot, "SWIG/Constants.xml");
+    std::string fMapGuideApiGen = combine_paths(sdkRoot, "SWIG/MapGuideApiGen.xml");
+
+    std::string sbConstants;
+    if (!read_all_text(fConstants, sbConstants))
+    {
+        std::cout << "File not found: " << fConstants << std::endl;
+        return 1;
+    }
+    std::string sbMapGuideApiGen;
+    if (!read_all_text(fMapGuideApiGen, sbMapGuideApiGen))
+    {
+        std::cout << "File not found: " << fMapGuideApiGen << std::endl;
+        return 1;
+    }
+
+    //Add extra STRINGPARAM typedef for .net Core
+    str_replace(sbMapGuideApiGen, "#if defined(PHP) || defined(JAVA)", "#if defined(PHP) || defined(JAVA) || defined(DOTNETCORE)");
+    //Patch STRINGPARAM typedef for PHP
+    str_replace(sbMapGuideApiGen, "typedef char*         STRINGPARAM;", "typedef std::wstring STRINGPARAM;");
+    //Patch class id includes
+    str_replace(sbMapGuideApiGen, "%include \"../../../Common", "%include \"" + sdkRelPath + "/Inc/Common");
+    str_replace(sbMapGuideApiGen, "%include \"../WebApp", "%include \"" + sdkRelPath + "/Inc/Web/WebApp");
+    str_replace(sbMapGuideApiGen, "%include \"../HttpHandler", "%include \"" + sdkRelPath + "/Inc/Web/HttpHandler");
+    //Fix header relative paths
+    str_replace(sbMapGuideApiGen, "<Header path=\"../../../Common", "<Header path=\"" + sdkRelPath + "/Inc/Common");
+    str_replace(sbMapGuideApiGen, "<Header path=\"../WebApp", "<Header path=\"" + sdkRelPath + "/Inc/Web/WebApp");
+    str_replace(sbMapGuideApiGen, "<Header path=\"../HttpHandler", "<Header path=\"" + sdkRelPath + "/Inc/Web/HttpHandler");
+    //#elseif must've been valid in our custom version of SWIG we're using. Not here
+    str_replace(sbMapGuideApiGen, "#elseif", "#elif");
+
+    //Fix header relative paths
+    str_replace(sbConstants, "<Header path=\"../../../Common", "<Header path=\"" + sdkRelPath + "/Inc/Common");
+    str_replace(sbConstants, "<Header path=\"../WebApp", "<Header path=\"" + sdkRelPath + "/Inc/Web/WebApp");
+    str_replace(sbConstants, "<Header path=\"../HttpHandler", "<Header path=\"" + sdkRelPath + "/Inc/Web/HttpHandler");
+
+    std::string outConstants = combine_paths(targetDir, "Constants.xml");
+    std::string outMapGuideApiGen = combine_paths(targetDir, "MapGuideApiGen.xml");
+
+    if (!write_all_text(outConstants, sbConstants))
+    {
+        std::cout << "Failed to write Constants.xml" << std::endl;
+        return 1;
+    }
+    std::cout << "Wrote: " << outConstants << std::endl;
+    if (!write_all_text(outMapGuideApiGen, sbMapGuideApiGen))
+    {
+        std::cout << "Failed to write MapGuideApiGen.xml" << std::endl;
+        return 1;
+    }
+    std::cout << "Wrote: " << outMapGuideApiGen << std::endl;
+    return 0;
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/Tools.sln
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/Tools.sln	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/Tools.sln	2020-11-06 16:04:14 UTC (rev 9757)
@@ -0,0 +1,79 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26124.0
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StampVer", "StampVer\StampVer.csproj", "{00311DA9-58F4-4460-B44E-2B60900A9E10}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwigPrepare", "SwigPrepare\SwigPrepare.csproj", "{63E7E7D7-1256-440B-8700-00D33E6DDDAE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassMapGen", "ClassMapGen\ClassMapGen.csproj", "{505B50EF-9041-49AD-8B78-7EC6FE98D990}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhpPostProcess", "PhpPostProcess\PhpPostProcess.csproj", "{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Release|Any CPU = Release|Any CPU
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Debug|x64.Build.0 = Debug|Any CPU
+		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Debug|x86.Build.0 = Debug|Any CPU
+		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Release|Any CPU.Build.0 = Release|Any CPU
+		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Release|x64.ActiveCfg = Release|Any CPU
+		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Release|x64.Build.0 = Release|Any CPU
+		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Release|x86.ActiveCfg = Release|Any CPU
+		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Release|x86.Build.0 = Release|Any CPU
+		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Debug|x64.Build.0 = Debug|Any CPU
+		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Debug|x86.Build.0 = Debug|Any CPU
+		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Release|x64.ActiveCfg = Release|Any CPU
+		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Release|x64.Build.0 = Release|Any CPU
+		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Release|x86.ActiveCfg = Release|Any CPU
+		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Release|x86.Build.0 = Release|Any CPU
+		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Debug|x64.Build.0 = Debug|Any CPU
+		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Debug|x86.Build.0 = Debug|Any CPU
+		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Release|Any CPU.Build.0 = Release|Any CPU
+		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Release|x64.ActiveCfg = Release|Any CPU
+		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Release|x64.Build.0 = Release|Any CPU
+		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Release|x86.ActiveCfg = Release|Any CPU
+		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Release|x86.Build.0 = Release|Any CPU
+		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Debug|x64.Build.0 = Debug|Any CPU
+		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Debug|x86.Build.0 = Debug|Any CPU
+		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Release|Any CPU.Build.0 = Release|Any CPU
+		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Release|x64.ActiveCfg = Release|Any CPU
+		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Release|x64.Build.0 = Release|Any CPU
+		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Release|x86.ActiveCfg = Release|Any CPU
+		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Release|x86.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {5BB4C6B2-72AF-4F0A-8FB1-0575EBC08176}
+	EndGlobalSection
+EndGlobal



More information about the mapguide-commits mailing list