/// <summary>
        /// Reads the resource identifier.
        /// </summary>
        /// <returns></returns>
        public string ReadResourceIdentifier()
        {
            int classId = ReadClassId();

            if (classId == 0)
            {
                return(null);
            }

            if (m_siteVersion <= SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1) && classId != 12003)
            {
                throw new Exception(Strings.ErrorInvalidResourceIdentifier);
            }
            if (m_siteVersion > SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1) && classId != 11500)
            {
                throw new Exception(Strings.ErrorInvalidResourceIdentifier);
            }

            if (m_siteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
            {
                return(ReadInternalString());
            }
            else
            {
                return(ReadString());
            }
        }
        /// <summary>
        /// Reads the coordinates.
        /// </summary>
        /// <returns></returns>
        public double[] ReadCoordinates()
        {
            int classid = ReadClassId();

            if (m_siteVersion <= SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1) && classid != 18000)
            {
                throw new Exception(string.Format(Strings.ErrorBinarySerializerCoordinateUnexpectedType, classid));
            }
            if (m_siteVersion > SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1) && classid != 20000)
            {
                throw new Exception(string.Format(Strings.ErrorBinarySerializerCoordinateUnexpectedType, classid));
            }

            int count      = ReadInt32();
            int dimensions = ReadInt32();

            if (dimensions == 0)
            {
                dimensions = 2;
            }
            else if (dimensions > 4)
            {
                throw new Exception(string.Format(Strings.ErrorBinarySerializerInvalidCoordinateDimensionCount, dimensions));
            }

            double[] args = new double[dimensions * count];
            for (int i = 0; i < (dimensions * count); i++)
            {
                args[i] = ReadDouble();
            }

            return(args);
        }
Exemple #3
0
        /// <summary>
        /// Initialize this instance using the specified binary stream
        /// </summary>
        /// <param name="d"></param>
        public virtual void Deserialize(MgBinaryDeserializer d)
        {
            this.Group = d.ReadString();
            int objid = d.ReadClassId();

            if (d.SiteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
            {
                if (objid != 12001)
                {
                    throw new Exception(string.Format(Strings.ErrorInvalidGroupObjectId, objid));
                }
            }
            else if (objid != 19001)
            {
                throw new Exception(string.Format(Strings.ErrorInvalidGroupObjectId, objid));
            }

            this.Name           = d.ReadString();
            this.ObjectId       = d.ReadString();
            this.Type           = d.ReadInt32();
            this.Visible        = d.ReadBool();
            this.ShowInLegend   = d.ReadBool();
            this.ExpandInLegend = d.ReadBool();
            this.LegendLabel    = d.ReadString();
        }
Exemple #4
0
        /// <summary>
        /// Writes the coordinates.
        /// </summary>
        /// <param name="coordinates">The coordinates.</param>
        /// <param name="dimensions">The dimensions.</param>
        public void WriteCoordinates(double[] coordinates, int dimensions)
        {
            int ndim = dimensions;

            if (ndim == 0)
            {
                ndim = 2;
            }
            else if (ndim > 4)
            {
                throw new Exception(string.Format(Strings.ErrorBinarySerializerInvalidCoordinateDimensionCount, dimensions));
            }

            if ((coordinates.Length % ndim) != 0)
            {
                throw new Exception(Strings.ErrorBinarySerializerInvalidAmountOfCoordinates);
            }

            if (m_siteVersion <= SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1))
            {
                WriteClassId(18000);
            }
            else
            {
                WriteClassId(20000);
            }
            Write(coordinates.Length / ndim);
            Write(dimensions);

            for (int i = 0; i < coordinates.Length; i++)
            {
                Write(coordinates[i]);
            }
        }
Exemple #5
0
 /// <summary>
 /// Writes the resource identifier.
 /// </summary>
 /// <param name="resourceID">The resource ID.</param>
 public void WriteResourceIdentifier(string resourceID)
 {
     if (resourceID == null)
     {
         //For null objects, we write a class ID of 0
         WriteClassId(0);
     }
     else
     {
         if (m_siteVersion <= SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1))
         {
             WriteClassId(12003);
         }
         else
         {
             WriteClassId(11500);
         }
         if (m_siteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
         {
             WriteStringInternal(resourceID);
         }
         else
         {
             Write(resourceID);
         }
     }
 }
Exemple #6
0
 public void TestSiteVersions()
 {
     foreach (KnownSiteVersions ver in Enum.GetValues(typeof(KnownSiteVersions)))
     {
         var version = SiteVersions.GetVersion(ver);
         Assert.NotNull(version);
     }
 }
Exemple #7
0
 /// <summary>
 /// Writes the specified value.
 /// </summary>
 /// <param name="value">The value.</param>
 public void Write(string value)
 {
     if (m_siteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
     {
         Write(new MgArgumentPacket(MgPacketHeader.ArgumentSimple, MgArgumentType.String, null, (ulong)value.Length + 1));
         WriteStringInternal(value);
     }
     else
     {
         byte[] buf = System.Text.Encoding.UTF8.GetBytes(value + "\0"); //NOXLATE
         Write(new MgArgumentPacket(MgPacketHeader.ArgumentSimple, MgArgumentType.String, null, (ulong)buf.Length));
         WriteRaw(buf);
     }
 }
Exemple #8
0
 /// <summary>
 /// Serializes this instance
 /// </summary>
 /// <param name="s"></param>
 public virtual void Serialize(MgBinarySerializer s)
 {
     s.Write(this.Group);
     if (s.SiteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
     {
         s.WriteClassId(12001);
     }
     else
     {
         s.WriteClassId(19001);
     }
     s.Write(this.Name);
     s.Write(this.ObjectId);
     s.Write(this.Type);
     s.Write(this.Visible);
     s.Write(this.ShowInLegend);
     s.Write(this.ExpandInLegend);
     s.Write(this.LegendLabel);
 }
        /// <summary>
        /// Reads the string.
        /// </summary>
        /// <returns></returns>
        public string ReadString()
        {
            MgArgumentPacket p = ReadArgumentPacket();

            if (p.ArgumentType != MgArgumentType.String)
            {
                throw new InvalidCastException(string.Format(Strings.ErrorBinarySerializerUnexpectedType, p.ArgumentType.ToString(), "string")); //NOXLATE
            }
            if (m_siteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
            {
                return(ReadInternalString());
            }
            else
            {
                byte[] buf = ReadStreamRepeat((int)p.Length);

                string b = System.Text.Encoding.UTF8.GetString(buf);
                //Chop of C zero terminator... Why store it, when the length is also present?
                return(b.Substring(0, b.Length - 1));
            }
        }
Exemple #10
0
        /// <summary>
        /// Performs base resource validation
        /// </summary>
        /// <param name="context"></param>
        /// <param name="resource"></param>
        /// <param name="recurse"></param>
        /// <returns></returns>
        protected ValidationIssue[] ValidateBase(ResourceValidationContext context, IResource resource, bool recurse)
        {
            Check.ArgumentNotNull(context, nameof(context));

            if (context.IsAlreadyValidated(resource.ResourceID))
            {
                return(null);
            }

            if (resource.ResourceType != ResourceTypes.MapDefinition.ToString())
            {
                return(null);
            }

            List <ValidationIssue> issues = new List <ValidationIssue>();

            IMapDefinition mdef = resource as IMapDefinition;

            if (string.IsNullOrEmpty(mdef.CoordinateSystem))
            {
                issues.Add(new ValidationIssue(mdef, ValidationStatus.Warning, ValidationStatusCode.Warning_MapDefinition_MissingCoordinateSystem, Strings.MDF_NoCoordinateSystem));
            }

            foreach (IMapLayerGroup g in mdef.MapLayerGroup)
            {
                if (g.ShowInLegend && (g.LegendLabel == null || g.LegendLabel.Trim().Length == 0))
                {
                    issues.Add(new ValidationIssue(mdef, ValidationStatus.Information, ValidationStatusCode.Info_MapDefinition_GroupMissingLabelInformation, string.Format(Strings.MDF_GroupMissingLabelInformation, g.Name)));
                }
                else if (g.ShowInLegend && g.LegendLabel.Trim().ToLower() == "layer group") //NOXLATE
                {
                    issues.Add(new ValidationIssue(mdef, ValidationStatus.Information, ValidationStatusCode.Info_MapDefinition_GroupHasDefaultLabel, string.Format(Strings.MDF_GroupHasDefaultLabelInformation, g.Name)));
                }

                if (!string.IsNullOrEmpty(g.Group))
                {
                    var grp = mdef.GetGroupByName(g.Group);
                    if (grp == null)
                    {
                        issues.Add(new ValidationIssue(mdef, ValidationStatus.Error, ValidationStatusCode.Error_MapDefinition_GroupWithNonExistentGroup, string.Format(Strings.MDF_GroupWithNonExistentGroup, g.Name, g.Group)));
                    }
                }
            }

            List <IBaseMapLayer> layers = new List <IBaseMapLayer>();

            foreach (IBaseMapLayer l in mdef.MapLayer)
            {
                layers.Add(l);
            }

            if (mdef.BaseMap != null && mdef.BaseMap.HasGroups())
            {
                if (mdef.BaseMap.ScaleCount == 0)
                {
                    issues.Add(new ValidationIssue(mdef, ValidationStatus.Error, ValidationStatusCode.Error_MapDefinition_NoFiniteDisplayScales, Strings.MDF_NoFiniteDisplayScalesSpecified));
                }

                foreach (IBaseMapGroup g in mdef.BaseMap.BaseMapLayerGroups)
                {
                    foreach (IBaseMapLayer l in g.BaseMapLayer)
                    {
                        layers.Add(l);
                    }
                }
            }
            Dictionary <string, IBaseMapLayer> nameCounter = new Dictionary <string, IBaseMapLayer>();

            foreach (IBaseMapLayer l in layers)
            {
                if (nameCounter.ContainsKey(l.Name))
                {
                    issues.Add(new ValidationIssue(mdef, ValidationStatus.Warning, ValidationStatusCode.Error_MapDefinition_DuplicateLayerName, string.Format(Strings.MDF_LayerNameDuplicateWarning, l.Name, l.ResourceId, nameCounter[l.Name].ResourceId)));
                }
                else
                {
                    nameCounter.Add(l.Name, l);
                }

                var ml = l as IMapLayer;
                if (ml != null && !string.IsNullOrEmpty(ml.Group))
                {
                    var grp = mdef.GetGroupByName(ml.Group);
                    if (grp == null)
                    {
                        issues.Add(new ValidationIssue(mdef, ValidationStatus.Error, ValidationStatusCode.Error_MapDefinition_LayerWithNonExistentGroup, string.Format(Strings.MDF_LayerWithNonExistentGroup, ml.Name, ml.Group)));
                    }
                }

                if (l.ShowInLegend && (string.IsNullOrEmpty(l.LegendLabel) || l.LegendLabel.Trim().Length == 0))
                {
                    issues.Add(new ValidationIssue(mdef, ValidationStatus.Information, ValidationStatusCode.Warning_MapDefinition_LayerMissingLegendLabel, string.Format(Strings.MDF_LayerMissingLabelInformation, l.Name)));
                }

                var mapEnv = ObjectFactory.CreateEnvelope(mdef.Extents.MinX, mdef.Extents.MinY, mdef.Extents.MaxX, mdef.Extents.MaxY);

                try
                {
                    ILayerDefinition layer = null;
                    IResource        res   = context.GetResource(l.ResourceId);
                    if (!ResourceValidatorSet.HasValidator(res.ResourceType, res.ResourceVersion))
                    {
                        //Need to trap the no registered validator message
                        issues.AddRange(ResourceValidatorSet.Validate(context, res, true));
                        continue;
                    }

                    layer = (ILayerDefinition)res;
                    if (recurse)
                    {
                        issues.AddRange(ResourceValidatorSet.Validate(context, layer, recurse));
                    }

                    IVectorLayerDefinition vl = null;
                    if (layer.SubLayer.LayerType == LayerType.Vector)
                    {
                        vl = (IVectorLayerDefinition)layer.SubLayer;
                    }

                    if (vl != null)
                    {
                        try
                        {
                            IFeatureSource fs = (IFeatureSource)context.GetResource(vl.ResourceId);
                            if (l.Selectable)
                            {
                                //Test selectability requirement
                                string[] idProps = fs.GetIdentityProperties(this.Connection, vl.FeatureName);
                                if (idProps == null || idProps.Length == 0)
                                {
                                    issues.Add(new ValidationIssue(resource, ValidationStatus.Warning, ValidationStatusCode.Warning_MapDefinition_UnselectableLayer, string.Format(Strings.MDF_UnselectableLayer, l.Name, vl.FeatureName, fs.ResourceID)));
                                }
                            }

                            try
                            {
                                FdoSpatialContextList scList = context.GetSpatialContexts(fs.ResourceID);

                                if (scList.SpatialContext == null || scList.SpatialContext.Count == 0)
                                {
                                    issues.Add(new ValidationIssue(resource, ValidationStatus.Warning, ValidationStatusCode.Warning_MapDefinition_MissingSpatialContext, string.Format(Strings.MDF_MissingSpatialContextWarning, fs.ResourceID)));
                                }
                                else
                                {
                                    if (scList.SpatialContext.Count > 1)
                                    {
                                        issues.Add(new ValidationIssue(resource, ValidationStatus.Information, ValidationStatusCode.Info_MapDefinition_MultipleSpatialContexts, string.Format(Strings.MDF_MultipleSpatialContextsInformation, fs.ResourceID)));
                                    }

                                    bool skipGeomCheck = false;

                                    //TODO: Switch to the correct version (2.1), once released
                                    if (scList.SpatialContext[0].CoordinateSystemWkt != mdef.CoordinateSystem)
                                    {
                                        if (layer.SubLayer.LayerType == LayerType.Raster && this.Connection.SiteVersion <= SiteVersions.GetVersion(OSGeo.MapGuide.MaestroAPI.KnownSiteVersions.MapGuideOS2_0_2))
                                        {
                                            issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_MapDefinition_RasterReprojection, string.Format(Strings.MDF_RasterReprojectionError, fs.ResourceID)));
                                        }
                                        else
                                        {
                                            issues.Add(new ValidationIssue(resource, ValidationStatus.Warning, ValidationStatusCode.Warning_MapDefinition_LayerReprojection, string.Format(Strings.MDF_DataReprojectionWarning, fs.ResourceID)));
                                        }

                                        skipGeomCheck = true;
                                    }

                                    if (vl.Geometry != null && !skipGeomCheck)
                                    {
                                        var env = this.Connection.FeatureService.GetSpatialExtent(fs.ResourceID, vl.FeatureName, vl.Geometry);
                                        if (!env.Intersects(mapEnv))
                                        {
                                            issues.Add(new ValidationIssue(resource, ValidationStatus.Warning, ValidationStatusCode.Warning_MapDefinition_DataOutsideMapBounds, string.Format(Strings.MDF_DataOutsideMapWarning, fs.ResourceID)));
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                var nex = ex as NullExtentException;
                                if (nex != null)
                                {
                                    issues.Add(new ValidationIssue(resource, ValidationStatus.Warning, ValidationStatusCode.Warning_MapDefinition_FeatureSourceWithNullExtent, string.Format(Strings.MDF_LayerWithNullExtent, fs.ResourceID)));
                                }
                                else
                                {
                                    string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
                                    issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_MapDefinition_ResourceRead, string.Format(Strings.MDF_ResourceReadError, fs.ResourceID, msg)));
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
                            issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_MapDefinition_FeatureSourceRead, string.Format(Strings.MDF_FeatureSourceReadError, l.ResourceId, msg)));
                        }
                    }
                }
                catch (Exception ex)
                {
                    string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
                    issues.Add(new ValidationIssue(resource, ValidationStatus.Error, ValidationStatusCode.Error_MapDefinition_LayerRead, string.Format(Strings.MDF_LayerReadError, l.ResourceId, msg)));
                }
            }

            context.MarkValidated(resource.ResourceID);

            return(issues.ToArray());
        }
Exemple #11
0
        /// <summary>
        /// Initializes this instance with the specified binary stream
        /// </summary>
        /// <param name="d"></param>
        public virtual void Deserialize(MgBinaryDeserializer d)
        {
            this.Group = d.ReadString();

            int classid = d.ReadClassId();

            if (d.SiteVersion <= SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1) && classid != 19003)
            {
                throw new Exception(string.Format(Strings.ErrorResourceIdentifierClassIdNotFound, classid));
            }
            if (d.SiteVersion > SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1) && classid != 30501)
            {
                throw new Exception(string.Format(Strings.ErrorResourceIdentifierClassIdNotFound, classid));
            }

            this.LayerDefinitionID = d.ReadResourceIdentifier();

            if (d.SiteVersion < SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
            {
                this.Name = d.ReadString();
                _objectId = d.ReadString();
                _type     = d.ReadInt32();

                _visible        = d.ReadByte() > 0;
                _selectable     = d.ReadByte() > 0;
                _showInLegend   = d.ReadByte() > 0;
                _expandInLegend = d.ReadByte() > 0;

                _legendLabel  = d.ReadString();
                _needsRefresh = d.ReadByte() > 0;
                _displayOrder = d.ReadDouble();

                var scaleRanges = new List <double>();
                int scales      = d.ReadInt32();
                while (scales-- > 0)
                {
                    scaleRanges.Add(d.ReadDouble());
                }

                _scaleRanges = scaleRanges.ToArray();

                _featureSourceId      = d.ReadString();
                _qualifiedClassName   = d.ReadString();
                _geometryPropertyName = d.ReadString();

                var ids     = new List <PropertyInfo>();
                int idCount = d.ReadInt32();

                while (idCount-- > 0)
                {
                    short  idType = d.ReadInt16();
                    string idName = d.ReadString();
                    ids.Add(new PropertyInfo(idName, ConvertMgTypeToNetType(idType)));
                }

                this.IdentityProperties = ids.ToArray();
            }
            else
            {
                //AAARGH!!! Now they bypass their own header system ....
                this.Name = d.ReadInternalString();
                _objectId = d.ReadInternalString();
                _type     = BitConverter.ToInt32(d.ReadStreamRepeat(4), 0);

                int flags = d.ReadStreamRepeat(1)[0];
                _visible        = (flags & 1) > 0;
                _selectable     = (flags & 2) > 0;
                _showInLegend   = (flags & 4) > 0;
                _expandInLegend = (flags & 8) > 0;
                _needsRefresh   = (flags & 16) > 0;
                _hasTooltips    = (flags & 32) > 0;

                _legendLabel  = d.ReadInternalString();
                _displayOrder = BitConverter.ToDouble(d.ReadStreamRepeat(8), 0);

                var scaleRanges = new List <double>();
                int scales      = BitConverter.ToInt32(d.ReadStreamRepeat(4), 0);
                while (scales-- > 0)
                {
                    scaleRanges.Add(BitConverter.ToDouble(d.ReadStreamRepeat(8), 0));
                }

                _scaleRanges = scaleRanges.ToArray();

                _featureSourceId    = d.ReadInternalString();
                _qualifiedClassName = d.ReadInternalString();
                if (d.SiteVersion > SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS2_1))
                {
                    _filter = d.ReadInternalString();
                }
                //this.SchemaName = d.ReadInternalString();
                d.ReadInternalString();
                _geometryPropertyName = d.ReadInternalString();

                var ids     = new List <PropertyInfo>();
                int idCount = BitConverter.ToInt32(d.ReadStreamRepeat(4), 0);

                while (idCount-- > 0)
                {
                    short  idType = BitConverter.ToInt16(d.ReadStreamRepeat(2), 0);
                    string idName = d.ReadInternalString();
                    ids.Add(new PropertyInfo(idName, ConvertMgTypeToNetType(idType)));
                }

                this.IdentityProperties = ids.ToArray();
            }

            EnsureOrderedMinMaxScales();
        }
Exemple #12
0
        /// <summary>
        /// Serializes this instance to a binary stream
        /// </summary>
        /// <param name="s"></param>
        public virtual void Serialize(MgBinarySerializer s)
        {
            s.Write(this.Group);

            if (s.SiteVersion <= SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1))
            {
                s.WriteClassId(19003);
            }
            else
            {
                s.WriteClassId(30501);
            }

            s.WriteResourceIdentifier(this.LayerDefinitionID);

            if (s.SiteVersion < SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
            {
                s.Write(this.Name);
                s.Write(this.ObjectId);

                s.Write(this.Type);

                s.Write((byte)(this.Visible ? 1 : 0));
                s.Write((byte)(this.Selectable ? 1 : 0));
                s.Write((byte)(this.ShowInLegend ? 1 : 0));
                s.Write((byte)(this.ExpandInLegend ? 1 : 0));

                s.Write(this.LegendLabel);
                s.Write((byte)(this.NeedsRefresh ? 1 : 0));
                s.Write(this.DisplayOrder);

                s.Write(_scaleRanges.Length);
                foreach (double d in _scaleRanges)
                {
                    s.Write(d);
                }

                s.Write(this.FeatureSourceID);
                s.Write(this.QualifiedClassName);
                s.Write(this.GeometryPropertyName);

                s.Write(this.IdentityProperties.Length);
                foreach (var x in this.IdentityProperties)
                {
                    s.Write((short)ConvertNetTypeToMgType(x.Type));
                    s.Write(x.Name);
                }
            }
            else
            {
                s.WriteStringInternal(this.Name);
                s.WriteStringInternal(this.ObjectId);
                s.WriteRaw(BitConverter.GetBytes(this.Type));
                int flags = 0;
                flags |= this.Visible ? 1 : 0;
                flags |= this.Selectable ? 2 : 0;
                flags |= this.ShowInLegend ? 4 : 0;
                flags |= this.ExpandInLegend ? 8 : 0;
                flags |= this.NeedsRefresh ? 16 : 0;
                flags |= this.HasTooltips ? 32 : 0;
                s.WriteRaw(new byte[] { (byte)flags });

                s.WriteStringInternal(this.LegendLabel);
                s.WriteRaw(BitConverter.GetBytes(this.DisplayOrder));

                s.WriteRaw(BitConverter.GetBytes(_scaleRanges.Length));
                foreach (double d in _scaleRanges)
                {
                    s.WriteRaw(BitConverter.GetBytes(d));
                }

                s.WriteStringInternal(this.FeatureSourceID);
                s.WriteStringInternal(this.QualifiedClassName);
                if (s.SiteVersion > SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS2_1))
                {
                    s.WriteStringInternal(this.Filter);
                }
                s.WriteStringInternal(this.SchemaName);
                s.WriteStringInternal(this.GeometryPropertyName);

                s.WriteRaw(BitConverter.GetBytes(this.IdentityProperties.Length));
                foreach (var x in this.IdentityProperties)
                {
                    s.WriteRaw(BitConverter.GetBytes((short)ConvertNetTypeToMgType(x.Type)));
                    s.WriteStringInternal(x.Name);
                }
            }
        }