#region Disclaimer / License
// Copyright (C) 2010, Jackie Ng
// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie@gmail.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using OSGeo.MapGuide.ObjectModels.LayerDefinition;
using OSGeo.MapGuide.ObjectModels.FeatureSource;
using OSGeo.MapGuide.MaestroAPI.Services;
using OSGeo.MapGuide.ObjectModels.Common;
using OSGeo.MapGuide.ObjectModels.DrawingSource;
using OSGeo.MapGuide.MaestroAPI.Schema;
namespace OSGeo.MapGuide.MaestroAPI.Resource.Validation
{
///
/// A base layer definition validator class the provides the common validation logic. Because this is working against
/// the layer definition interfaces, this common logic can apply to all versions of the layer definition schema (that
/// implement these interfaces)
///
public abstract class BaseLayerDefinitionValidator : IResourceValidator
{
///
/// Validats the specified resources for common issues associated with this
/// resource type
///
///
///
///
///
public virtual ValidationIssue[] Validate(ResourceValidationContext context, IResource resource, bool recurse)
{
if (!resource.GetResourceTypeDescriptor().Equals(this.SupportedResourceAndVersion))
return null;
return ValidateBase(context, resource, recurse);
}
///
/// Perform base validation logic
///
///
///
///
///
protected static ValidationIssue[] ValidateBase(ResourceValidationContext context, IResource resource, bool recurse)
{
Check.NotNull(context, "context");
if (context.IsAlreadyValidated(resource.ResourceID))
return null;
var ldef = resource as ILayerDefinition;
var vldef = ldef.SubLayer as IVectorLayerDefinition;
var gldef = ldef.SubLayer as IRasterLayerDefinition;
var dldef = ldef.SubLayer as IDrawingLayerDefinition;
List issues = new List();
if (ldef.SubLayer == null)
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_LayerNull, Properties.Resources.LDF_LayerNullError));
ClassDefinition cls = null;
if (vldef != null || gldef != null)
{
//Load referenced feature source
IFeatureSource fs = null;
try
{
fs = (IFeatureSource)context.GetResource(ldef.SubLayer.ResourceId);
issues.AddRange(ResourceValidatorSet.Validate(context, fs, recurse));
}
catch (Exception)
{
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_FeatureSourceLoadError, string.Format(Properties.Resources.LDF_FeatureSourceLoadError)));
}
if (fs != null)
{
//Verify specified feature class and geometry check out
try
{
string qualClassName = vldef == null ? gldef.FeatureName : vldef.FeatureName;
string geometry = vldef == null ? gldef.Geometry : vldef.Geometry;
bool foundSchema = false;
bool foundGeometry = false;
cls = fs.GetClass(qualClassName);
if (cls != null)
{
foundSchema = true;
foreach (PropertyDefinition col in cls.Properties)
{
if (col.Name == geometry)
{
foundGeometry = true;
break;
}
}
if (vldef != null && vldef.PropertyMapping != null)
{
foreach (INameStringPair s in vldef.PropertyMapping)
{
bool found = false;
foreach (PropertyDefinition col in cls.Properties)
{
if (col.Name == s.Name)
{
found = true;
break;
}
}
if (!found)
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_ClassNotFound, string.Format(Properties.Resources.LDF_SchemaMissingError, qualClassName, fs.ResourceID)));
}
}
}
if (!foundSchema)
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_ClassNotFound, string.Format(Properties.Resources.LDF_SchemaMissingError, qualClassName, fs.ResourceID)));
else if (!foundGeometry)
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_GeometryNotFound, string.Format(Properties.Resources.LDF_GeometryMissingError, geometry, qualClassName, fs.ResourceID)));
}
catch (Exception)
{
issues.Add(new ValidationIssue(fs, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_Generic, string.Format(Properties.Resources.LDF_SchemaAndColumnReadFailedError)));
}
if (recurse)
{
issues.AddRange(ResourceValidatorSet.Validate(context, fs, recurse));
}
}
}
if (vldef != null)
{
if (string.IsNullOrEmpty(vldef.FeatureName))
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_MissingFeatureSource, Properties.Resources.LDF_MissingFeatureSourceError));
if (string.IsNullOrEmpty(vldef.Geometry))
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_MissingGeometry, Properties.Resources.LDF_MissingGeometryError));
if (vldef.VectorScaleRange == null || !vldef.HasVectorScaleRanges())
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_MissingScaleRanges, Properties.Resources.LDF_MissingScaleRangesError));
else
{
//Test for overlapping scale ranges
List> ranges = new List>();
foreach (IVectorScaleRange vsr in vldef.VectorScaleRange)
{
IVectorScaleRange2 vsr2 = vsr as IVectorScaleRange2;
if (vsr2 != null)
{
var area = vsr2.AreaStyle;
var line = vsr2.LineStyle;
var point = vsr2.PointStyle;
if (vsr2.CompositeStyleCount > 0 && (area != null || line != null || point != null))
{
issues.Add(new ValidationIssue(resource, ValidationStatus.Warning, ValidationStatusCode.Warning_LayerDefinition_CompositeStyleDefinedAlongsideBasicStyle, string.Format(
Properties.Resources.LDF_CompositeStyleDefinedAlongsideBasicStyle,
vsr2.MinScale.HasValue ? vsr2.MinScale.Value : 0,
vsr2.MaxScale.HasValue ? vsr2.MaxScale.Value.ToString() : Properties.Resources.Infinity)));
}
}
ranges.Add(new KeyValuePair(
vsr.MaxScale.HasValue ? vsr.MaxScale.Value : double.PositiveInfinity,
vsr.MinScale.HasValue ? vsr.MinScale.Value : double.NegativeInfinity));
}
double min = double.PositiveInfinity;
double max = double.NegativeInfinity;
foreach (KeyValuePair sr in ranges)
{
min = Math.Min(min, sr.Value);
max = Math.Max(max, sr.Key);
if (sr.Key < sr.Value)
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_MinMaxScaleSwapped, string.Format(Properties.Resources.LDF_MinAndMaxScaleSwappedError, sr.Value, sr.Key)));
}
ranges.Sort(CompareScales);
//TODO: Detect gaps in scale ranges
for (int i = 0; i < ranges.Count; i++)
for (int j = i + 1; j < ranges.Count; j++)
if (ranges[i].Key > ranges[j].Value || ranges[i].Value > ranges[j].Value)
issues.Add(new ValidationIssue(resource, ValidationStatus.Information, ValidationStatusCode.Info_LayerDefinition_ScaleRangeOverlap, string.Format(Properties.Resources.LDF_ScaleRangesOverlapInformation, ranges[i].Value, ranges[i].Key, ranges[j].Value, ranges[j].Key)));
}
}
else if (gldef != null)
{
if (gldef.GridScaleRange == null || gldef.GridScaleRangeCount == 0)
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_NoGridScaleRanges, Properties.Resources.LDF_MissingScaleRangesError));
else if (gldef.GridScaleRangeCount != 1)
issues.Add(new ValidationIssue(resource, ValidationStatus.Warning, ValidationStatusCode.Warning_LayerDefinition_MultipleGridScaleRanges, Properties.Resources.LDF_MultipleScaleRangesWarning));
}
else if (dldef != null)
{
IDrawingSource dws = null;
try
{
dws = (IDrawingSource)context.GetResource(dldef.ResourceId);
}
catch (Exception)
{
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_DrawingSourceError, Properties.Resources.LDF_DrawingSourceError));
}
if (dws != null)
{
if (Array.IndexOf(ldef.CurrentConnection.Capabilities.SupportedServices, (int)ServiceType.Drawing) >= 0)
{
var dwSvc = (IDrawingService)ldef.CurrentConnection.GetService((int)ServiceType.Drawing);
//Check if specified section exists
var shtList = dwSvc.EnumerateDrawingSections(dws.ResourceID);
DrawingSectionListSection sheet = null;
foreach (var sht in shtList.Section)
{
if (sht.Name.Equals(dldef.Sheet))
{
sheet = sht;
break;
}
}
if (sheet == null)
{
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_DrawingSourceSheetNotFound, string.Format(Properties.Resources.LDF_SheetNotFound, dldef.Sheet)));
}
else
{
//null or empty filter means all layers, in that case do nothing
if (!string.IsNullOrEmpty(dldef.LayerFilter))
{
//Check if specified layers all exist in specified section
var specifiedLayers = dldef.LayerFilter.Split(',');
var dwLayers = new Dictionary();
var shtLayers = dwSvc.EnumerateDrawingLayers(dws.ResourceID, sheet.Name);
foreach (var sl in shtLayers)
{
dwLayers.Add(sl, sl);
}
foreach (var sl in specifiedLayers)
{
if (!dwLayers.ContainsKey(sl.Trim()))
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_DrawingSourceSheetLayerNotFound, string.Format(Properties.Resources.LDF_SheetLayerNotFound, sl.Trim(), sheet.Name)));
}
}
}
}
if (recurse)
{
issues.AddRange(ResourceValidatorSet.Validate(context, dws, recurse));
}
}
}
else
{
issues.Add(new ValidationIssue(resource, ValidationStatus.Warning, ValidationStatusCode.Warning_LayerDefinition_UnsupportedLayerType, Properties.Resources.LDF_UnsupportedLayerTypeWarning));
}
if (vldef != null || gldef != null)
{
//Load referenced feature source
IFeatureSource fs = null;
try
{
fs = (IFeatureSource)context.GetResource(ldef.SubLayer.ResourceId);
issues.AddRange(ResourceValidatorSet.Validate(context, fs, recurse));
}
catch (Exception)
{
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_FeatureSourceLoadError, string.Format(Properties.Resources.LDF_FeatureSourceLoadError)));
}
if (fs != null)
{
//Verify specified feature class and geometry check out
try
{
string qualClassName = vldef == null ? gldef.FeatureName : vldef.FeatureName;
string geometry = vldef == null ? gldef.Geometry : vldef.Geometry;
bool foundSchema = false;
bool foundGeometry = false;
FeatureSourceDescription desc = context.DescribeFeatureSource(ldef.SubLayer.ResourceId);
foreach (FeatureSchema fsc in desc.Schemas)
{
foreach (ClassDefinition scm in fsc.Classes)
{
if (scm.QualifiedName == qualClassName)
{
foundSchema = true;
foreach (PropertyDefinition col in scm.Properties)
{
if (col.Name == geometry)
{
foundGeometry = true;
break;
}
}
if (vldef != null && vldef.PropertyMapping != null)
{
foreach (INameStringPair s in vldef.PropertyMapping)
{
bool found = false;
foreach (PropertyDefinition col in scm.Properties)
{
if (col.Name == s.Name)
{
found = true;
break;
}
}
if (!found)
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_ClassNotFound, string.Format(Properties.Resources.LDF_SchemaMissingError, qualClassName, fs.ResourceID)));
}
}
break;
}
}
}
if (!foundSchema)
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_ClassNotFound, string.Format(Properties.Resources.LDF_SchemaMissingError, qualClassName, fs.ResourceID)));
else if (!foundGeometry)
issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_GeometryNotFound, string.Format(Properties.Resources.LDF_GeometryMissingError, geometry, qualClassName, fs.ResourceID)));
}
catch (Exception)
{
issues.Add(new ValidationIssue(fs, ValidationStatus.Error, ValidationStatusCode.Error_LayerDefinition_Generic, string.Format(Properties.Resources.LDF_SchemaAndColumnReadFailedError)));
}
if (recurse)
{
issues.AddRange(ResourceValidatorSet.Validate(context, fs, recurse));
}
}
}
context.MarkValidated(resource.ResourceID);
return issues.ToArray();
}
///
/// Gets the resource type and version this validator supports
///
///
public abstract ResourceTypeDescriptor SupportedResourceAndVersion
{
get;
}
private static int CompareScales(KeyValuePair rangeA, KeyValuePair rangeB)
{
//We are comparing from scales of both ranges
return rangeA.Key.CompareTo(rangeB.Key);
}
}
}