Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / EntityModel / SchemaObjectModel / SchemaElement.cs
1 //---------------------------------------------------------------------
2 // <copyright file="SchemaElement.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 namespace System.Data.EntityModel.SchemaObjectModel
11 {
12     using System;
13     using System.Collections.Generic;
14     using System.Data;
15     using System.Data.Entity;
16     using System.Data.Metadata.Edm;
17     using System.Diagnostics;
18     using System.Globalization;
19     using System.IO;
20     using System.Xml;
21     using System.Xml.Linq;
22
23     /// <summary>
24     /// Summary description for SchemaElement.
25     /// </summary>
26     [DebuggerDisplay("Name={Name}")]
27     internal abstract class SchemaElement
28     {
29         // see http://www.w3.org/TR/2006/REC-xml-names-20060816/
30         internal const string XmlNamespaceNamespace = "http://www.w3.org/2000/xmlns/";
31
32
33         #region Instance Fields
34         private SchemaElement _parentElement = null;
35         private Schema _schema = null;
36         private int _lineNumber = 0;
37         private int _linePosition = 0;
38         private string _name = null;
39         private DocumentationElement _documentation = null;
40
41         private List<MetadataProperty> _otherContent;
42
43         #endregion
44
45         #region Static Fields
46         /// <summary></summary>
47         protected const int MaxValueVersionComponent = short.MaxValue;
48         #endregion
49
50         #region Public Properties
51         /// <summary>
52         /// 
53         /// </summary>
54         internal  int LineNumber
55         {
56             get
57             {
58                 return _lineNumber;
59             }
60         }
61
62         /// <summary>
63         /// 
64         /// </summary>
65         internal  int LinePosition
66         {
67             get
68             {
69                 return _linePosition;
70             }
71         }
72
73         /// <summary>
74         /// 
75         /// </summary>
76         public virtual string Name
77         {
78             get
79             {
80                 return _name;
81             }
82             set
83             {
84                 _name = value;
85             }
86         }
87
88         /// <summary>
89         /// 
90         /// </summary>
91         internal  DocumentationElement Documentation
92         {
93             get
94             {
95                 return _documentation;
96             }
97             set
98             {
99                 _documentation = value;
100             }
101         }
102
103         /// <summary>
104         /// 
105         /// </summary>
106         internal  SchemaElement ParentElement
107         {
108             get
109             {
110                 return _parentElement;
111             }
112             private set
113             {
114                 _parentElement = value;
115             }
116         }
117
118         /// <summary>
119         /// 
120         /// </summary>
121         internal Schema Schema
122         {
123             get
124             {
125                 return _schema;
126             }
127             set
128             {
129                 _schema = value;
130             }
131         }
132         
133         /// <summary>
134         /// 
135         /// </summary>
136         public  virtual string FQName
137         {
138             get
139             {
140                 return Name;
141             }
142         }
143
144         /// <summary>
145         /// 
146         /// </summary>
147         public virtual string Identity
148         {
149             get
150             {
151                 return Name;
152             }
153         }
154
155        
156         public List<MetadataProperty> OtherContent
157         {
158             get 
159             {
160                 if (_otherContent == null)
161                 {
162                     _otherContent = new List<MetadataProperty>();
163                 }
164
165                 return _otherContent; 
166             }
167         }
168         #endregion
169
170         #region Internal Methods
171         /// <summary>
172         /// Validates this element and its children
173         /// </summary>
174         
175         internal virtual void Validate()
176         {
177         }
178
179         /// <summary>
180         /// 
181         /// </summary>
182         /// <param name="errorCode"></param>
183         /// <param name="severity"></param>
184         /// <param name="lineNumber"></param>
185         /// <param name="linePosition"></param>
186         /// <param name="message"></param>
187         internal void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, int lineNumber, int linePosition, object message )
188         {
189             AddError(errorCode,severity,SchemaLocation,lineNumber,linePosition,message);
190         }
191
192         /// <summary>
193         /// 
194         /// </summary>
195         /// <param name="errorCode"></param>
196         /// <param name="severity"></param>
197         /// <param name="reader"></param>
198         /// <param name="message"></param>
199         internal void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, XmlReader reader, object message )
200         {
201             int lineNumber;
202             int linePosition;
203             GetPositionInfo(reader, out lineNumber, out linePosition);
204             AddError(errorCode,severity,SchemaLocation,lineNumber,linePosition,message);
205         }
206
207         /// <summary>
208         /// 
209         /// </summary>
210         /// <param name="errorCode"></param>
211         /// <param name="severity"></param>
212         /// <param name="message"></param>
213         internal void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, object message )
214         {
215             AddError(errorCode,severity,SchemaLocation,LineNumber,LinePosition,message);
216         }
217
218         /// <summary>
219         /// 
220         /// </summary>
221         /// <param name="errorCode"></param>
222         /// <param name="severity"></param>
223         /// <param name="element"></param>
224         /// <param name="message"></param>
225         internal void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, SchemaElement element, object message )
226         {
227             AddError(errorCode,severity,element.Schema.Location,element.LineNumber,element.LinePosition,message);
228         }
229
230         /// <summary>
231         /// 
232         /// </summary>
233         /// <param name="reader"></param>
234         /// <returns></returns>
235         internal void Parse(XmlReader reader)
236         {
237             GetPositionInfo(reader);
238
239             bool hasEndElement = !reader.IsEmptyElement;
240
241             Debug.Assert(reader.NodeType == XmlNodeType.Element);
242             for ( bool more = reader.MoveToFirstAttribute(); more; more = reader.MoveToNextAttribute() )
243             {
244                 ParseAttribute(reader);
245             }
246             HandleAttributesComplete();
247
248             bool done = !hasEndElement;
249             bool skipToNextElement = false;
250             while ( !done )
251             {
252                 if ( skipToNextElement )
253                 {
254                     skipToNextElement = false;
255                     reader.Skip();
256                     if ( reader.EOF )
257                         break;
258                 }
259                 else
260                 {
261                     if ( !reader.Read() )
262                         break;
263                 }
264                 switch ( reader.NodeType )
265                 {
266                     case XmlNodeType.Element:
267                         skipToNextElement = ParseElement(reader);
268                         break;
269
270                     case XmlNodeType.EndElement:
271                     {
272                         done = true;
273                         break;
274                     }
275
276                     case XmlNodeType.CDATA:
277                     case XmlNodeType.Text:
278                     case XmlNodeType.SignificantWhitespace:
279                         ParseText(reader);
280                         break;
281
282                         // we ignore these childless elements
283                     case XmlNodeType.Whitespace:
284                     case XmlNodeType.XmlDeclaration:
285                     case XmlNodeType.Comment:
286                     case XmlNodeType.Notation:
287                     case XmlNodeType.ProcessingInstruction:
288                     {
289                         break;
290                     }
291
292                         // we ignore these elements that can have children
293                     case XmlNodeType.DocumentType:
294                     case XmlNodeType.EntityReference:
295                     {
296                         skipToNextElement = true;
297                         break;
298                     }
299
300                     default:
301                     {
302                         AddError( ErrorCode.UnexpectedXmlNodeType, EdmSchemaErrorSeverity.Error, reader,
303                             System.Data.Entity.Strings.UnexpectedXmlNodeType(reader.NodeType));
304                         skipToNextElement = true;
305                         break;
306                     }
307                 }
308             }
309             HandleChildElementsComplete();
310             if ( reader.EOF && reader.Depth > 0 )
311             {
312                 AddError( ErrorCode.MalformedXml, EdmSchemaErrorSeverity.Error, 0, 0,
313                     System.Data.Entity.Strings.MalformedXml(LineNumber,LinePosition));
314             }
315         }
316
317         /// <summary>
318         /// Set the current line number and position for an XmlReader
319         /// </summary>
320         /// <param name="reader">the reader whose position is desired</param>
321         internal void GetPositionInfo(XmlReader reader)
322         {
323             GetPositionInfo(reader,out _lineNumber,out _linePosition);
324         }
325
326         /// <summary>
327         /// Get the current line number and position for an XmlReader
328         /// </summary>
329         /// <param name="reader">the reader whose position is desired</param>
330         /// <param name="lineNumber">the line number</param>
331         /// <param name="linePosition">the line position</param>
332         internal static void GetPositionInfo(XmlReader reader, out int lineNumber, out int linePosition)
333         {
334             IXmlLineInfo xmlLineInfo = reader as IXmlLineInfo;
335             if ( xmlLineInfo != null && xmlLineInfo.HasLineInfo() )
336             {
337                 lineNumber = xmlLineInfo.LineNumber;
338                 linePosition = xmlLineInfo.LinePosition;
339             }
340             else
341             {
342                 lineNumber = 0;
343                 linePosition = 0;
344             }
345         }
346         
347         /// <summary>
348         /// 
349         /// </summary>
350         internal virtual void ResolveTopLevelNames()
351         {
352         }
353         internal virtual void ResolveSecondLevelNames()
354         {
355         }
356         #endregion
357
358         #region Protected Methods
359
360         /// <summary>
361         /// 
362         /// </summary>
363         /// <param name="parentElement"></param>
364         internal SchemaElement(SchemaElement parentElement)
365         {
366             if ( parentElement != null )
367             {
368                 ParentElement = parentElement;
369                 for ( SchemaElement element = parentElement; element != null; element = element.ParentElement )
370                 {
371                     Schema schema = element as Schema;
372                     if ( schema != null )
373                     {
374                         Schema = schema;
375                         break;
376                     }
377                 }
378                 
379                 if (Schema == null)
380                 {
381                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.AllElementsMustBeInSchema);
382                 }
383             }
384         }
385
386         internal SchemaElement(SchemaElement parentElement, string name)
387             : this(parentElement)
388         {
389             _name = name;
390         }
391
392         /// <summary>
393         /// 
394         /// </summary>
395         protected virtual void HandleAttributesComplete()
396         {
397         }
398
399         /// <summary>
400         /// 
401         /// </summary>
402         protected virtual void HandleChildElementsComplete()
403         {
404         }
405
406         /// <summary>
407         /// 
408         /// </summary>
409         /// <param name="reader"></param>
410         /// <param name="field"></param>
411         /// <returns></returns>
412         protected string HandleUndottedNameAttribute(XmlReader reader, string field)
413         {
414             string name = field;
415             Debug.Assert(string.IsNullOrEmpty(field), string.Format(CultureInfo.CurrentCulture, "{0} is already defined", reader.Name));
416
417             bool success = Utils.GetUndottedName(Schema, reader, out name);
418             if ( !success )
419                 return name;
420
421             return name;
422         }
423
424         /// <summary>
425         /// 
426         /// </summary>
427         /// <param name="reader"></param>
428         /// <param name="field"></param>
429         /// <param name="errorMessageId"></param>
430         /// <returns></returns>
431         protected ReturnValue<string> HandleDottedNameAttribute(XmlReader reader, string field, Func<object, string> errorFormat)
432         {
433             ReturnValue<string> returnValue = new ReturnValue<string>();
434             Debug.Assert(string.IsNullOrEmpty(field), string.Format(CultureInfo.CurrentCulture, "{0} is already defined", reader.Name));
435
436             string value;
437             if ( !Utils.GetDottedName(Schema,reader,out value) )
438                 return returnValue;
439
440             returnValue.Value = value;
441             return returnValue;
442         }
443
444         /// <summary>
445         /// Use to handle an attribute with an int data type
446         /// </summary>
447         /// <param name="reader">the reader positioned at the int attribute</param>
448         /// <param name="field">The int field to be given the value found</param>
449         /// <returns>true if an int value was successfuly extracted from the attribute, false otherwise.</returns>
450         internal bool HandleIntAttribute(XmlReader reader, ref int field)
451         {
452             int value;
453             if ( !Utils.GetInt(Schema, reader, out value) )
454                 return false;
455
456             field = value;
457             return true;
458         }
459
460         /// <summary>
461         /// Use to handle an attribute with an int data type
462         /// </summary>
463         /// <param name="reader">the reader positioned at the int attribute</param>
464         /// <param name="field">The int field to be given the value found</param>
465         /// <returns>true if an int value was successfuly extracted from the attribute, false otherwise.</returns>
466         internal bool HandleByteAttribute(XmlReader reader, ref byte field)
467         {
468             byte value;
469             if ( !Utils.GetByte(Schema, reader, out value) )
470                 return false;
471
472             field = value;
473             return true;
474         }
475         /// <summary>
476         /// 
477         /// </summary>
478         /// <param name="reader"></param>
479         /// <param name="field"></param>
480         /// <returns></returns>
481         internal bool HandleBoolAttribute(XmlReader reader, ref bool field)
482         {
483             bool value;
484             if ( !Utils.GetBool(Schema,reader,out value) )
485                 return false;
486
487             field = value;
488             return true;
489         }
490
491         /// <summary>
492         /// Use this to jump through an element that doesn't need any processing
493         /// </summary>
494         /// <param name="reader">xml reader currently positioned at an element</param>
495         protected virtual void SkipThroughElement(XmlReader reader)
496         {
497             Debug.Assert(reader != null);
498             Parse(reader);
499         }
500
501         protected void SkipElement(XmlReader reader)
502         {
503             using (XmlReader subtree = reader.ReadSubtree())
504             {
505                 while (subtree.Read()) ;
506             }
507         }
508
509         #endregion
510
511         #region Protected Properties
512         /// <summary>
513         /// 
514         /// </summary>
515         protected string SchemaLocation
516         {
517             get
518             {
519                 if ( Schema != null )
520                     return Schema.Location;
521                 return null;
522             }
523         }
524
525         protected virtual bool HandleText(XmlReader reader)
526         {
527             return false;
528         }
529
530         internal virtual SchemaElement Clone(SchemaElement parentElement)
531         {
532             throw Error.NotImplemented();
533         }
534         #endregion
535
536         #region Private Methods
537         /// <summary>
538         /// 
539         /// </summary>
540         /// <param name="reader"></param>
541         /// <returns></returns>
542         private void HandleDocumentationElement(XmlReader reader)
543         {
544             Documentation = new DocumentationElement(this);
545             Documentation.Parse(reader);
546         }
547         
548         /// <summary>
549         /// 
550         /// </summary>
551         /// <param name="reader"></param>
552         protected virtual void HandleNameAttribute(XmlReader reader)
553         {
554             Name = HandleUndottedNameAttribute(reader, Name);
555         }
556
557         /// <summary>
558         /// 
559         /// </summary>
560         /// <param name="errorCode"></param>
561         /// <param name="severity"></param>
562         /// <param name="source"></param>
563         /// <param name="lineNumber"></param>
564         /// <param name="linePosition"></param>
565         /// <param name="message"></param>
566         private void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, string sourceLocation, int lineNumber, int linePosition, object message )
567         {
568             EdmSchemaError error = null;
569             string messageString = message as string;
570             if ( messageString != null )
571                 error = new EdmSchemaError( messageString, (int)errorCode, severity, sourceLocation, lineNumber, linePosition );
572             else
573             {
574                 Exception ex = message as Exception;
575                 if ( ex != null )
576                     error = new EdmSchemaError( ex.Message, (int)errorCode, severity, sourceLocation, lineNumber, linePosition, ex );
577                 else
578                     error = new EdmSchemaError( message.ToString(), (int)errorCode, severity, sourceLocation, lineNumber, linePosition );
579             }
580             Schema.AddError(error);
581         }
582
583         /// <summary>
584         /// Call handler for the current attribute
585         /// </summary>
586         /// <param name="reader">XmlReader positioned at the attribute</param>
587         private void ParseAttribute(XmlReader reader)
588         {
589 #if false
590             // the attribute value is schema invalid, just skip it; this avoids some duplicate errors at the expense of better error messages...
591             if ( reader.SchemaInfo != null && reader.SchemaInfo.Validity == System.Xml.Schema.XmlSchemaValidity.Invalid )
592                 continue;
593 #endif
594             string attributeNamespace = reader.NamespaceURI;
595             if (attributeNamespace == XmlConstants.AnnotationNamespace 
596                 && reader.LocalName == XmlConstants.UseStrongSpatialTypes
597                 && !ProhibitAttribute(attributeNamespace, reader.LocalName) 
598                 && HandleAttribute(reader))
599             {
600                 return;
601             }
602             else if (!Schema.IsParseableXmlNamespace(attributeNamespace, true))
603             {
604                 AddOtherContent(reader);                
605             }
606             else if (!ProhibitAttribute(attributeNamespace, reader.LocalName)&&
607                      HandleAttribute(reader))
608             {
609                 return;
610             }
611             else if (reader.SchemaInfo == null || reader.SchemaInfo.Validity != System.Xml.Schema.XmlSchemaValidity.Invalid)
612             {
613                 // there's no handler for (namespace,name) and there wasn't a validation error. 
614                 // Report an error of our own if the node is in no namespace or if it is in one of our xml schemas tartget namespace.
615                 if (string.IsNullOrEmpty(attributeNamespace) || Schema.IsParseableXmlNamespace(attributeNamespace, true))
616                 {
617                     AddError(ErrorCode.UnexpectedXmlAttribute, EdmSchemaErrorSeverity.Error, reader, System.Data.Entity.Strings.UnexpectedXmlAttribute(reader.Name));
618                 }
619             }
620         }
621
622         protected virtual bool ProhibitAttribute(string namespaceUri, string localName)
623         {
624             return false;
625         }
626
627         /// <summary>
628         /// This overload assumes the default namespace
629         /// </summary>
630         /// <param name="reader"></param>
631         /// <param name="localName"></param>
632         /// <returns></returns>
633         internal static bool CanHandleAttribute(XmlReader reader, string localName)
634         {
635             Debug.Assert(reader.NamespaceURI != null);
636             return reader.NamespaceURI.Length == 0 && reader.LocalName == localName;
637         }
638
639         protected virtual bool HandleAttribute(XmlReader reader)
640         {
641             if(CanHandleAttribute(reader, XmlConstants.Name))
642             {
643                 HandleNameAttribute(reader);
644                 return true;
645             }
646
647             return false;
648         }
649
650         private bool AddOtherContent(XmlReader reader)
651         {
652             int lineNumber;
653             int linePosition;
654             GetPositionInfo(reader, out lineNumber, out linePosition);
655
656             MetadataProperty property;
657             if (reader.NodeType == XmlNodeType.Element)
658             {
659
660                 if (this._schema.SchemaVersion == XmlConstants.EdmVersionForV1 ||
661                     this._schema.SchemaVersion == XmlConstants.EdmVersionForV1_1)
662                 {
663                     // skip this element
664                     // we don't support element annotations in v1 and v1.1
665                     return true;
666                 }
667
668                 // in V1 and V1.1 the codegen can only appear as the attribute annotation and we want to maintain
669                 // the same behavior for V2, thus we throw if we encounter CodeGen namespace 
670                 // in structural annotation in V2 and furthur version
671                 if (this._schema.SchemaVersion >= XmlConstants.EdmVersionForV2 
672                     && reader.NamespaceURI == XmlConstants.CodeGenerationSchemaNamespace)
673                 {
674                     Debug.Assert(
675                         XmlConstants.SchemaVersionLatest == XmlConstants.EdmVersionForV3, 
676                         "Please add checking for the latest namespace");
677
678                     AddError(ErrorCode.NoCodeGenNamespaceInStructuralAnnotation, EdmSchemaErrorSeverity.Error, lineNumber, linePosition, Strings.NoCodeGenNamespaceInStructuralAnnotation(XmlConstants.CodeGenerationSchemaNamespace));
679                     return true;
680                 }
681
682                 Debug.Assert(
683                         !Schema.IsParseableXmlNamespace(reader.NamespaceURI, false),
684                         "Structural annotation cannot use any edm reserved namespaces");
685
686                 // using this subtree aproach because when I call 
687                 // reader.ReadOuterXml() it positions me at the Node beyond
688                 // the end of the node I am starting on
689                 // which doesn't work with the parsing logic
690                 using (XmlReader subtree = reader.ReadSubtree())
691                 {
692                     subtree.Read();
693                     XElement element = XElement.Load(new StringReader(subtree.ReadOuterXml()));
694
695                     property = CreateMetadataPropertyFromOtherNamespaceXmlArtifact(element.Name.NamespaceName, element.Name.LocalName, element);
696                 }
697             }
698             else
699             {
700                 if (reader.NamespaceURI == XmlNamespaceNamespace)
701                 {
702                     // we don't bring in namespace definitions
703                     return true;
704                 }
705
706                 Debug.Assert(reader.NodeType == XmlNodeType.Attribute, "called an attribute function when not on an attribute");
707                 property = CreateMetadataPropertyFromOtherNamespaceXmlArtifact(reader.NamespaceURI, reader.LocalName, reader.Value);
708             }
709
710             if (!OtherContent.Exists(mp => mp.Identity == property.Identity))
711             {
712                 OtherContent.Add(property);
713             }
714             else
715             {
716                 AddError(ErrorCode.AlreadyDefined, EdmSchemaErrorSeverity.Error, lineNumber, linePosition, Strings.DuplicateAnnotation(property.Identity, this.FQName));
717             }
718             return false;
719         }
720
721         internal static MetadataProperty CreateMetadataPropertyFromOtherNamespaceXmlArtifact(string xmlNamespaceUri, string artifactName, object value)
722         {
723             MetadataProperty property;
724             property = new MetadataProperty(xmlNamespaceUri + ":" + artifactName,
725                                        TypeUsage.Create(EdmProviderManifest.Instance.GetPrimitiveType(PrimitiveTypeKind.String)),
726                                        value);
727             return property;
728         }
729
730         /// <summary>
731         /// Call handler for the current element
732         /// </summary>
733         /// <param name="reader">XmlReader positioned at the element</param>
734         /// <returns>true if element content should be skipped</returns>
735         private bool ParseElement(XmlReader reader)
736         {
737             string elementNamespace = reader.NamespaceURI;
738             // for schema element that right under the schema, we just ignore them, since schema does not
739             // have metadataproperties
740             if (!Schema.IsParseableXmlNamespace(elementNamespace, true) && this.ParentElement != null)
741             {
742                 return AddOtherContent(reader);
743             }
744             if (HandleElement(reader))
745             {
746                 return false;
747             }
748             else
749             {
750
751                 // we need to report an error if the namespace for this element is a target namespace for the xml schemas we are parsing against.
752                 // otherwise we assume that this is either a valid 'any' element or that the xsd validator has generated an error
753                 if (string.IsNullOrEmpty(elementNamespace) || Schema.IsParseableXmlNamespace(reader.NamespaceURI, false))
754                 {
755                     AddError(ErrorCode.UnexpectedXmlElement, EdmSchemaErrorSeverity.Error, reader, System.Data.Entity.Strings.UnexpectedXmlElement(reader.Name));
756                 }
757                 return true;
758             }
759         }
760
761         protected bool CanHandleElement(XmlReader reader, string localName)
762         {
763             return reader.NamespaceURI == Schema.SchemaXmlNamespace && reader.LocalName == localName;
764         }
765
766         protected virtual bool HandleElement(XmlReader reader)
767         {
768             if (CanHandleElement(reader, XmlConstants.Documentation))
769             {
770                 HandleDocumentationElement(reader);
771                 return true;
772             }
773
774             return false;
775         }
776
777         /// <summary>
778         /// Handle text data.
779         /// </summary>
780         /// <param name="reader">XmlReader positioned at Text, CData, or SignificantWhitespace </param>
781         private void ParseText(XmlReader reader)
782         {
783             if (HandleText(reader))
784             {
785                 return;
786             }
787             else if (reader.Value != null && reader.Value.Trim().Length == 0)
788             {
789                 // just ignore this text.  Don't add an error, since the value is just whitespace.
790             }
791             else
792             {
793                 AddError( ErrorCode.TextNotAllowed, EdmSchemaErrorSeverity.Error, reader, System.Data.Entity.Strings.TextNotAllowed(reader.Value ) );
794             }
795         }
796         #endregion
797
798         [Conditional("DEBUG")]
799         internal static void AssertReaderConsidersSchemaInvalid(XmlReader reader)
800         {
801             Debug.Assert(reader.SchemaInfo == null ||
802                          reader.SchemaInfo.Validity != System.Xml.Schema.XmlSchemaValidity.Valid, "The xsd should see this as not acceptable");
803         }
804
805     }
806 }