Major upgrade
This commit is contained in:
@@ -0,0 +1,329 @@
|
||||
// **********************************************************************************************************
|
||||
// XmlMbitParser.cs
|
||||
// 6/6/2022
|
||||
// NGI - Next Generation Interceptor
|
||||
//
|
||||
// Contract No. HQ0856-21-C-0003/1022000209
|
||||
//
|
||||
// THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S.
|
||||
// INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
|
||||
//
|
||||
// RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION PROPRIETARY TO RAYTHEON
|
||||
// COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT.
|
||||
// DISCLOSURE TO UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO RAYTHEON
|
||||
// COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS CONTENTS SHALL BE FURNISHED OR DISCLOSED
|
||||
// TO OR COPIED OR USED BY PERSONS OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF
|
||||
// RAYTHEON COMPANY.
|
||||
//
|
||||
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
|
||||
//
|
||||
// DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M,
|
||||
// NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006,
|
||||
// INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3,
|
||||
// DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3,
|
||||
// SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4,
|
||||
// INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
|
||||
//
|
||||
// CONTROLLED BY: MISSILE DEFENSE AGENCY
|
||||
// CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
|
||||
// CUI CATEGORY: CTI
|
||||
// DISTRIBUTION/DISSEMINATION CONTROL: F
|
||||
// POC: Alex Kravchenko (1118268)
|
||||
// **********************************************************************************************************
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.XPath;
|
||||
|
||||
namespace Raytheon.Common.Coe
|
||||
{
|
||||
public class XmlMbitParser
|
||||
{
|
||||
private readonly NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
private readonly Dictionary<string, XmlParser.CommonXmlElements> _commonElements = new Dictionary<string, XmlParser.CommonXmlElements>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
// store messages defined in xml files
|
||||
private readonly List<XmlParser.XmlMbitMessage> _mbitMessages = new List<XmlParser.XmlMbitMessage>();
|
||||
|
||||
// store duplicate messages for diagnostic purposes
|
||||
private readonly Dictionary<string, List<string>> _duplicateMbitMessages = new Dictionary<string, List<string>>();
|
||||
|
||||
// store each enumeration type and its associate key/value pairs
|
||||
private readonly Dictionary<string, XmlParser.XmlEnumeration> _xmlEnums = new Dictionary<string, XmlParser.XmlEnumeration>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
// store duplicate enumerations for diagnostic purposes
|
||||
private readonly Dictionary<string, List<string>> _duplicateEnums = new Dictionary<string, List<string>>();
|
||||
|
||||
// store each structure type and its associated data members
|
||||
private readonly Dictionary<string, XmlParser.XmlStructure> _xmlStructures = new Dictionary<string, XmlParser.XmlStructure>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
// store duplicate structures for diagnostic purposes
|
||||
private readonly Dictionary<string, List<string>> _duplicateStructures = new Dictionary<string, List<string>>();
|
||||
|
||||
// look up table for constants, the key is the constant name. A constant name can be defined in more than one namespace and have different values
|
||||
private ILookup<string, XmlParser.XmlConstant> _xmlConstantsLookUpByConstantName = null;
|
||||
|
||||
// look up table for constants, the key is the namespace in which the constant is defined. A constant name can be defined in more than one namespace and have different values
|
||||
private ILookup<string, XmlParser.XmlConstant> _xmlConstantsLookUpByNameSpace = null;
|
||||
|
||||
// store duplicate constants for diagnostic purposes
|
||||
private readonly Dictionary<string, List<string>> _duplicateConstants = new Dictionary<string, List<string>>();
|
||||
|
||||
// store names of files that contain data types that we need to generate
|
||||
public List<string> m_dataTypeFilesToBeGenerated = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Parse XML files from the path folder
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public bool ParseXmlFiles(string path)
|
||||
{
|
||||
bool isSuccessful = true;
|
||||
string xmlNamespace = string.Empty;
|
||||
List<XmlParser.XmlConstant> constantList = new List<XmlParser.XmlConstant>();
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
_logger.Error($"Path {path} not found");
|
||||
|
||||
isSuccessful = false;
|
||||
return isSuccessful;
|
||||
}
|
||||
|
||||
string[] files = Directory.GetFiles(path, "*.xml", SearchOption.AllDirectories);
|
||||
|
||||
List<string> dataTypeFiles = new List<string>(files.ToList());
|
||||
|
||||
foreach (string xmlFile in dataTypeFiles)
|
||||
{
|
||||
XPathDocument doc = null;
|
||||
|
||||
int prevEnumCount = _xmlEnums.Count;
|
||||
int prevStructureCount = _xmlStructures.Count;
|
||||
int prevMessageCount = _mbitMessages.Count;
|
||||
int prevConstantCount = constantList.Count;
|
||||
|
||||
List<string> comments = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
XmlReaderSettings readerSettings = new XmlReaderSettings()
|
||||
{
|
||||
// tells the xmlreader to ignore comment in XML file
|
||||
IgnoreComments = true
|
||||
};
|
||||
|
||||
using (XmlReader reader = XmlReader.Create(xmlFile, readerSettings))
|
||||
{
|
||||
// load the XML file
|
||||
doc = new XPathDocument(reader);
|
||||
}
|
||||
|
||||
XPathNavigator nav = doc.CreateNavigator();
|
||||
|
||||
xmlNamespace = Path.GetFileNameWithoutExtension(xmlFile);
|
||||
|
||||
_commonElements[xmlNamespace] = new XmlParser.CommonXmlElements();
|
||||
|
||||
nav.MoveToRoot();
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Info($"Parsing {Path.GetFileName(xmlFile)}");
|
||||
|
||||
if (nav.MoveToFirstChild())
|
||||
{
|
||||
do
|
||||
{
|
||||
if (string.Equals(nav.Name, "interface", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (nav.MoveToFirstChild())
|
||||
{
|
||||
do
|
||||
{
|
||||
if (!XmlParser.GetCommonElementsFromXml(nav, _commonElements[xmlNamespace]))
|
||||
{
|
||||
if (string.Equals(nav.Name, "enum", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
XmlParser.GetEnumeration(nav, xmlNamespace, _xmlEnums, _duplicateEnums);
|
||||
}
|
||||
else if (string.Equals(nav.Name, "structure", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
XmlParser.GetStructure(nav, xmlNamespace, _xmlStructures, comments, _duplicateStructures);
|
||||
comments.Clear();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "constant", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
XmlParser.GetConstant(nav, xmlNamespace, constantList, _duplicateConstants);
|
||||
}
|
||||
else if (string.Equals(nav.Name, "message", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
XmlParser.GetMbitMessage(nav, xmlNamespace, _mbitMessages, _duplicateMbitMessages);
|
||||
}
|
||||
else if (string.Equals(nav.Name, "comment", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
comments.Add(nav.Value.Trim());
|
||||
}
|
||||
else if (nav.Name.Length > 0)
|
||||
{
|
||||
throw new XmlParsingException("Unknown element \"" + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + "\" on line " + ((IXmlLineInfo)nav).LineNumber.ToString());
|
||||
}
|
||||
}
|
||||
} while (nav.MoveToNext());
|
||||
}
|
||||
}
|
||||
} while (nav.MoveToNext());
|
||||
}
|
||||
}
|
||||
catch (XmlParsingException ex)
|
||||
{
|
||||
string message = "File : " + xmlFile + "\n" + ex.Message;
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e);
|
||||
isSuccessful = false;
|
||||
}
|
||||
|
||||
if (isSuccessful)
|
||||
{
|
||||
_logger.Info(" - SUCCESS");
|
||||
|
||||
_logger.Info("Results:");
|
||||
_logger.Info($"Constants: {constantList.Count - prevConstantCount}");
|
||||
_logger.Info($"Enumerations: {_xmlEnums.Count - prevEnumCount}");
|
||||
_logger.Info($"Structures: {_xmlStructures.Count - prevStructureCount}");
|
||||
_logger.Info($"Messages: {_mbitMessages.Count - prevMessageCount}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Warn(" - FAIL");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (constantList.Count > 0)
|
||||
{
|
||||
// we want to create a look up table from a list of constants
|
||||
// the key for this table will be the constant name
|
||||
_xmlConstantsLookUpByConstantName = constantList.ToLookup(item => item.name);
|
||||
|
||||
// we want to create a look up table from a list of constants
|
||||
// the key for this table will be the namespace
|
||||
_xmlConstantsLookUpByNameSpace = constantList.ToLookup(item => item.nameSpace);
|
||||
}
|
||||
|
||||
if (_duplicateMbitMessages.Count > 0 || _duplicateConstants.Count > 0 || _duplicateEnums.Count > 0 || _duplicateStructures.Count > 0)
|
||||
{
|
||||
StreamWriter writer = null;
|
||||
FileStream fs = null;
|
||||
bool firstLineInFileAllocated = false;
|
||||
string textToBeWrittenToFile = string.Empty;
|
||||
string diagnosticFile = Path.Combine(path, "diagnostics.txt");
|
||||
|
||||
_logger.Info("Generating diagnostic information...");
|
||||
|
||||
foreach (KeyValuePair<string, List<string>> dictItem in _duplicateMbitMessages)
|
||||
{
|
||||
if (!firstLineInFileAllocated)
|
||||
firstLineInFileAllocated = true;
|
||||
else
|
||||
textToBeWrittenToFile += "\r\n\r\n";
|
||||
|
||||
textToBeWrittenToFile += "Duplicate definition for message \"" + dictItem.Key + "\" found in the following files: ";
|
||||
foreach (string listItem in dictItem.Value)
|
||||
{
|
||||
textToBeWrittenToFile += "\r\n" + GetCodeIndentation(1) + listItem;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, List<string>> dictItem in _duplicateStructures)
|
||||
{
|
||||
if (!firstLineInFileAllocated)
|
||||
firstLineInFileAllocated = true;
|
||||
else
|
||||
textToBeWrittenToFile += "\r\n\r\n";
|
||||
|
||||
textToBeWrittenToFile += "Duplicate definition for structure \"" + dictItem.Key + "\" found in the following files: ";
|
||||
foreach (string listItem in dictItem.Value)
|
||||
{
|
||||
textToBeWrittenToFile += "\r\n" + GetCodeIndentation(1) + listItem;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, List<string>> dictItem in _duplicateEnums)
|
||||
{
|
||||
if (!firstLineInFileAllocated)
|
||||
firstLineInFileAllocated = true;
|
||||
else
|
||||
textToBeWrittenToFile += "\r\n\r\n";
|
||||
|
||||
textToBeWrittenToFile += "Duplicate definition for enum \"" + dictItem.Key + "\" found in the following files: ";
|
||||
foreach (string listItem in dictItem.Value)
|
||||
{
|
||||
textToBeWrittenToFile += "\r\n" + GetCodeIndentation(1) + listItem;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, List<string>> dictItem in _duplicateConstants)
|
||||
{
|
||||
if (!firstLineInFileAllocated)
|
||||
firstLineInFileAllocated = true;
|
||||
else
|
||||
textToBeWrittenToFile += "\r\n\r\n";
|
||||
|
||||
textToBeWrittenToFile += "Duplicate definition for constant \"" + dictItem.Key + "\" found in the following files: ";
|
||||
foreach (string listItem in dictItem.Value)
|
||||
{
|
||||
textToBeWrittenToFile += "\r\n" + GetCodeIndentation(1) + listItem;
|
||||
}
|
||||
}
|
||||
|
||||
if (textToBeWrittenToFile.Length > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
fs = new FileStream(diagnosticFile, FileMode.Create, FileAccess.ReadWrite);
|
||||
writer = new StreamWriter(fs, Encoding.Default);
|
||||
writer.Write(textToBeWrittenToFile);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
isSuccessful = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (writer != null)
|
||||
{
|
||||
writer.Close();
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//m_mainWindow.updateStatusBox("DONE\n");
|
||||
}
|
||||
|
||||
return isSuccessful;
|
||||
}
|
||||
public static string GetCodeIndentation(int indentMultiples)
|
||||
{
|
||||
string indentUnit = " ";
|
||||
string indentation = string.Empty;
|
||||
|
||||
for (int i = 1; i <= indentMultiples; i++)
|
||||
indentation += indentUnit;
|
||||
|
||||
return indentation;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,588 @@
|
||||
// **********************************************************************************************************
|
||||
// XmlParser.cs
|
||||
// 6/6/2022
|
||||
// NGI - Next Generation Interceptor
|
||||
//
|
||||
// Contract No. HQ0856-21-C-0003/1022000209
|
||||
//
|
||||
// THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S.
|
||||
// INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
|
||||
//
|
||||
// RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION PROPRIETARY TO RAYTHEON
|
||||
// COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT.
|
||||
// DISCLOSURE TO UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO RAYTHEON
|
||||
// COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS CONTENTS SHALL BE FURNISHED OR DISCLOSED
|
||||
// TO OR COPIED OR USED BY PERSONS OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF
|
||||
// RAYTHEON COMPANY.
|
||||
//
|
||||
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
|
||||
//
|
||||
// DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M,
|
||||
// NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006,
|
||||
// INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3,
|
||||
// DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3,
|
||||
// SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4,
|
||||
// INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
|
||||
//
|
||||
// CONTROLLED BY: MISSILE DEFENSE AGENCY
|
||||
// CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
|
||||
// CUI CATEGORY: CTI
|
||||
// DISTRIBUTION/DISSEMINATION CONTROL: F
|
||||
// POC: Alex Kravchenko (1118268)
|
||||
// **********************************************************************************************************
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Xml.XPath;
|
||||
|
||||
namespace Raytheon.Common.Coe
|
||||
{
|
||||
public class XmlParser
|
||||
{
|
||||
public class CommonXmlElements
|
||||
{
|
||||
public List<string> fileIncludes = new List<string>();
|
||||
public string projectName = string.Empty;
|
||||
public string classification = string.Empty;
|
||||
public string fileHeader = string.Empty;
|
||||
}
|
||||
|
||||
public class XmlConstant
|
||||
{
|
||||
public string nameSpace = string.Empty;
|
||||
public List<string> comments = new List<string>();
|
||||
public string name = string.Empty;
|
||||
public string value = string.Empty;
|
||||
public string description = string.Empty;
|
||||
}
|
||||
|
||||
public class XmlEnumeration
|
||||
{
|
||||
public string nameSpace = string.Empty;
|
||||
public string description = string.Empty;
|
||||
public List<string> comments = new List<string>();
|
||||
//public Dictionary<string, string> enumKeyAndValuePairs = new Dictionary<string, string>();
|
||||
public List<XmlEnumItem> enumItems = new List<XmlEnumItem>();
|
||||
}
|
||||
|
||||
public class XmlEnumItem
|
||||
{
|
||||
public List<string> comments = new List<string>();
|
||||
public string name = string.Empty;
|
||||
public string value = string.Empty;
|
||||
}
|
||||
|
||||
public class XmlStructure
|
||||
{
|
||||
public string nameSpace = string.Empty;
|
||||
public string name = string.Empty;
|
||||
public List<string> comments = new List<string>();
|
||||
public List<XmlStructureItem> structDataItems = new List<XmlStructureItem>();
|
||||
}
|
||||
|
||||
public class XmlStructureItem
|
||||
{
|
||||
public string nameSpace = string.Empty;
|
||||
public List<string> comments = new List<string>();
|
||||
public string name = string.Empty;
|
||||
public string description = string.Empty;
|
||||
public string type = string.Empty;
|
||||
public string defaultVal = string.Empty;
|
||||
public string arrayLength = string.Empty;
|
||||
public string bits = string.Empty;
|
||||
}
|
||||
|
||||
public class XmlMbitMessage
|
||||
{
|
||||
public string nameSpace = string.Empty;
|
||||
public string name = string.Empty;
|
||||
public string label = string.Empty;
|
||||
public string instruLabel = string.Empty;
|
||||
public string description = string.Empty;
|
||||
public List<string> comments = new List<string>();
|
||||
|
||||
public List<XmlParser.XmlStructureItem> dataItems = new List<XmlParser.XmlStructureItem>();
|
||||
}
|
||||
|
||||
///===================================================================================
|
||||
/// XmlParser.getCommonElementsFromXml
|
||||
///===================================================================================
|
||||
/// <summary>
|
||||
/// Each XML file contains common elements such as header, includes, etc
|
||||
/// We want to parse it and save the common elements
|
||||
/// </summary>
|
||||
/// <param name="nav">navigator object that points to the current XML node we are at</param>
|
||||
/// <param name="commonElements">data structure that stores all the common elements of each XML file</param>
|
||||
///===================================================================================
|
||||
public static bool GetCommonElementsFromXml(XPathNavigator nav, CommonXmlElements commonElements)
|
||||
{
|
||||
bool isSuccessful = true;
|
||||
|
||||
if (string.Equals(nav.Name, "include", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (nav.MoveToFirstChild())
|
||||
{
|
||||
GetFileIncludes(nav, commonElements);
|
||||
|
||||
nav.MoveToParent();
|
||||
}
|
||||
}
|
||||
else if (string.Equals(nav.Name, "project", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
commonElements.projectName = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "classification", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
commonElements.classification = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "fileheader", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
commonElements.fileHeader = nav.Value;
|
||||
}
|
||||
else if (string.Equals(nav.Name, "packing", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
}
|
||||
else
|
||||
isSuccessful = false;
|
||||
|
||||
return isSuccessful;
|
||||
}
|
||||
|
||||
///===================================================================================
|
||||
/// XmlParser.getFileIncludes
|
||||
///===================================================================================
|
||||
/// <summary>
|
||||
/// Get the file includes specify by the XML file
|
||||
/// </summary>
|
||||
/// <param name="nav">navigator object that points to the current XML node we are at</param>
|
||||
/// <param name="commonElements">data structure that stores all the common elements of each XML file</param>
|
||||
///===================================================================================
|
||||
public static void GetFileIncludes(XPathNavigator nav, CommonXmlElements commonElements)
|
||||
{
|
||||
do
|
||||
{
|
||||
commonElements.fileIncludes.Add(nav.Value.Trim());
|
||||
|
||||
} while (nav.MoveToNext());
|
||||
}
|
||||
|
||||
///===================================================================================
|
||||
/// XmlParser.getConstant
|
||||
///===================================================================================
|
||||
/// <summary>
|
||||
/// Parse the symbolic constant defined by the XML
|
||||
/// </summary>
|
||||
/// <param name="nav">navigator object that points to the current XML node we are at</param>
|
||||
/// <param name="nameSpace">the XML file name that defines this constant</param>
|
||||
/// <param name="xmlConstants">the Dictionary that stores this constant information</param>
|
||||
/// <param name="duplicateConstants">the Dictioanry that stores duplicate constant definitions</param>
|
||||
///===================================================================================
|
||||
public static void GetConstant(XPathNavigator nav, string nameSpace, List<XmlConstant> xmlConstants, Dictionary<string, List<string>> duplicateConstants)
|
||||
{
|
||||
XmlConstant tempXmlConstant = new XmlConstant();
|
||||
|
||||
if (nav.MoveToFirstChild())
|
||||
{
|
||||
do
|
||||
{
|
||||
if (string.Equals(nav.Name, "name", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tempXmlConstant.name = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "description", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tempXmlConstant.description = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "value", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tempXmlConstant.value = nav.Value.Trim();
|
||||
}
|
||||
else if (nav.Name.Length > 0)
|
||||
{
|
||||
throw new XmlParsingException("Unknown element \"" + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + "\" on line " + ((IXmlLineInfo)nav).LineNumber.ToString());
|
||||
}
|
||||
|
||||
} while (nav.MoveToNext());
|
||||
|
||||
nav.MoveToParent();
|
||||
}
|
||||
|
||||
if (tempXmlConstant.name.Length > 0)
|
||||
{
|
||||
tempXmlConstant.nameSpace = nameSpace;
|
||||
xmlConstants.Add(tempXmlConstant);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new XmlParsingException("Child element \"name\" not found for node \"" + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + "\" on line " + ((IXmlLineInfo)nav).LineNumber.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
///===================================================================================
|
||||
/// XmlParser.getEnumeration
|
||||
///===================================================================================
|
||||
/// <summary>
|
||||
/// Parse the enumeration type defined by the XML
|
||||
/// </summary>
|
||||
/// <param name="nav">navigator object that points to the current XML node we are at</param>
|
||||
/// <param name="nameSpace">the XML file name that defines this enumeration type</param>
|
||||
/// <param name="xmlEnum">data structure that stores enumeration data</param>
|
||||
/// <param name="duplicateEnums">the Dictioanry that stores duplicate enumeration type</param>
|
||||
///===================================================================================
|
||||
public static void GetEnumeration(XPathNavigator nav, string nameSpace, Dictionary<string, XmlEnumeration> xmlEnums, Dictionary<string, List<string>> duplicateEnums)
|
||||
{
|
||||
string enumTypeName = string.Empty;
|
||||
string tempEnumTypeName = "temp";
|
||||
Dictionary<string, XmlEnumeration> tempXmlEnums = new Dictionary<string, XmlEnumeration>
|
||||
{
|
||||
[tempEnumTypeName] = new XmlEnumeration()
|
||||
};
|
||||
tempXmlEnums[tempEnumTypeName].nameSpace = nameSpace;
|
||||
|
||||
if (nav.MoveToFirstChild())
|
||||
{
|
||||
do
|
||||
{
|
||||
if (string.Equals(nav.Name, "name", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
enumTypeName = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "description", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tempXmlEnums[tempEnumTypeName].description = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "comment", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tempXmlEnums[tempEnumTypeName].comments.Add(nav.Value.Trim());
|
||||
}
|
||||
else if (string.Equals(nav.Name, "item", StringComparison.OrdinalIgnoreCase) || string.Equals(nav.Name, "enum_item", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (nav.MoveToFirstChild())
|
||||
{
|
||||
GetEnumItem(nav, tempXmlEnums[tempEnumTypeName]);
|
||||
|
||||
nav.MoveToParent();
|
||||
}
|
||||
}
|
||||
else if (nav.Name.Length > 0)
|
||||
{
|
||||
throw new XmlParsingException("Unknown element \"" + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + "\" on line " + ((IXmlLineInfo)nav).LineNumber.ToString());
|
||||
}
|
||||
|
||||
} while (nav.MoveToNext());
|
||||
|
||||
nav.MoveToParent();
|
||||
}
|
||||
|
||||
if (enumTypeName.Length > 0)
|
||||
{
|
||||
if (xmlEnums.ContainsKey(enumTypeName))
|
||||
{
|
||||
// save file name the defines this message that is a duplicate
|
||||
if (!duplicateEnums.ContainsKey(enumTypeName))
|
||||
{
|
||||
duplicateEnums[enumTypeName] = new List<string>();
|
||||
}
|
||||
|
||||
int index2 = duplicateEnums[enumTypeName].FindIndex(f => string.Equals(f, nameSpace + ".xml", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (index2 < 0)
|
||||
duplicateEnums[enumTypeName].Add(nameSpace + ".xml");
|
||||
|
||||
// see if the official structure is already in the duplicate list
|
||||
int index3 = duplicateEnums[enumTypeName].FindIndex(f => string.Equals(f, xmlEnums[enumTypeName].nameSpace + ".xml", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// if the official structure is not in the duplicate list, we want to save it in the duplicate list
|
||||
if (index3 < 0)
|
||||
{
|
||||
duplicateEnums[enumTypeName].Add(xmlEnums[enumTypeName].nameSpace + ".xml");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlEnums[enumTypeName] = tempXmlEnums[tempEnumTypeName];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new XmlParsingException("Child element \"name\" not found for node \"" + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + "\" on line " + ((IXmlLineInfo)nav).LineNumber.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
///===================================================================================
|
||||
/// XmlParser.getEnumItem
|
||||
///===================================================================================
|
||||
/// <summary>
|
||||
/// Parse each enumerated key/value pair
|
||||
/// </summary>
|
||||
/// <param name="nav">navigator object that points to the current XML node we are at</param>
|
||||
/// <param name="xmlEnum">data structure that stores enumeration data</param>
|
||||
///===================================================================================
|
||||
public static void GetEnumItem(XPathNavigator nav, XmlEnumeration xmlEnum)
|
||||
{
|
||||
XmlEnumItem enumItem = new XmlEnumItem();
|
||||
|
||||
do
|
||||
{
|
||||
if (string.Equals(nav.Name, "name", StringComparison.OrdinalIgnoreCase) || string.Equals(nav.Name, "item_name", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
enumItem.name = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "value", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
enumItem.value = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "comment", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
enumItem.comments.Add(nav.Value.Trim());
|
||||
}
|
||||
else if (nav.Name.Length > 0)
|
||||
{
|
||||
throw new XmlParsingException("Unknown element \"" + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + "\" on line " + ((IXmlLineInfo)nav).LineNumber.ToString());
|
||||
}
|
||||
|
||||
} while (nav.MoveToNext());
|
||||
|
||||
xmlEnum.enumItems.Add(enumItem);
|
||||
}
|
||||
|
||||
public static void GetStructure(XPathNavigator nav, string nameSpace, Dictionary<string, XmlStructure> xmlStructures, List<string> comments, Dictionary<string, List<string>> duplicateStructures)
|
||||
{
|
||||
string structureTypeName = string.Empty;
|
||||
string tempStructureTypeName = "temp";
|
||||
Dictionary<string, XmlStructure> tempXmlStructures = new Dictionary<string, XmlStructure>();
|
||||
|
||||
tempXmlStructures[tempStructureTypeName] = new XmlStructure();
|
||||
tempXmlStructures[tempStructureTypeName].nameSpace = nameSpace;
|
||||
|
||||
if (nav.MoveToFirstChild())
|
||||
{
|
||||
do
|
||||
{
|
||||
if (string.Equals(nav.Name, "name", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
structureTypeName = nav.Value.Trim();
|
||||
|
||||
if (comments != null && comments.Count > 0)
|
||||
{
|
||||
tempXmlStructures[tempStructureTypeName].comments = new List<string>(comments);
|
||||
}
|
||||
}
|
||||
else if (string.Equals(nav.Name, "comment", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tempXmlStructures[tempStructureTypeName].comments.Add(nav.Value.Trim());
|
||||
}
|
||||
else if (string.Equals(nav.Name, "item", StringComparison.OrdinalIgnoreCase) || string.Equals(nav.Name, "struct_item", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (nav.MoveToFirstChild())
|
||||
{
|
||||
XmlStructureItem structureItem = new XmlStructureItem();
|
||||
structureItem.nameSpace = nameSpace;
|
||||
GetStructureItem(nav, structureItem);
|
||||
|
||||
tempXmlStructures[tempStructureTypeName].structDataItems.Add(structureItem);
|
||||
|
||||
nav.MoveToParent();
|
||||
}
|
||||
}
|
||||
else if (nav.Name.Length > 0)
|
||||
{
|
||||
throw new XmlParsingException("Unknown element \"" + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + "\" on line " + ((IXmlLineInfo)nav).LineNumber.ToString());
|
||||
}
|
||||
|
||||
} while (nav.MoveToNext());
|
||||
|
||||
nav.MoveToParent();
|
||||
}
|
||||
|
||||
if (structureTypeName.Length > 0)
|
||||
{
|
||||
if (xmlStructures.ContainsKey(structureTypeName))
|
||||
{
|
||||
// save file name the defines this message that is a duplicate
|
||||
if (!duplicateStructures.ContainsKey(structureTypeName))
|
||||
{
|
||||
duplicateStructures[structureTypeName] = new List<string>();
|
||||
}
|
||||
|
||||
int index2 = duplicateStructures[structureTypeName].FindIndex(f => string.Equals(f, nameSpace + ".xml", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (index2 < 0)
|
||||
duplicateStructures[structureTypeName].Add(nameSpace + ".xml");
|
||||
|
||||
// see if the official structure is already in the duplicate list
|
||||
int index3 = duplicateStructures[structureTypeName].FindIndex(f => string.Equals(f, xmlStructures[structureTypeName].nameSpace + ".xml", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// if the official structure is not in the duplicate list, we want to save it in the duplicate list
|
||||
if (index3 < 0)
|
||||
{
|
||||
duplicateStructures[structureTypeName].Add(xmlStructures[structureTypeName].nameSpace + ".xml");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlStructures[structureTypeName] = tempXmlStructures[tempStructureTypeName];
|
||||
xmlStructures[structureTypeName].name = structureTypeName;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new XmlParsingException("Child element \"name\" not found for node \"" + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + "\" on line " + ((IXmlLineInfo)nav).LineNumber.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
///===================================================================================
|
||||
/// XmlParser.getStructureItem
|
||||
///===================================================================================
|
||||
/// <summary>
|
||||
/// Parse the data structure defined by the XML
|
||||
/// </summary>
|
||||
/// <param name="nav">navigator object that points to the current XML node we are at</param>
|
||||
/// <param name="xmlStructureItem">data structure that stores all data members of the data structure</param>
|
||||
///===================================================================================
|
||||
public static void GetStructureItem(XPathNavigator nav, XmlStructureItem xmlStructureItem)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (string.Equals(nav.Name, "name", StringComparison.OrdinalIgnoreCase) || string.Equals(nav.Name, "item_name", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
xmlStructureItem.name = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "type", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
xmlStructureItem.type = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "default", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
xmlStructureItem.defaultVal = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "arrayLength", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
xmlStructureItem.arrayLength = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "comment", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
xmlStructureItem.comments.Add(nav.Value.Trim());
|
||||
}
|
||||
else if (string.Equals(nav.Name, "description", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
xmlStructureItem.description = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "bits", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
xmlStructureItem.bits = nav.Value.Trim();
|
||||
}
|
||||
else if (nav.Name.Length > 0)
|
||||
{
|
||||
throw new XmlParsingException("Unknown element \"" + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + "\" on line " + ((IXmlLineInfo)nav).LineNumber.ToString());
|
||||
}
|
||||
|
||||
} while (nav.MoveToNext());
|
||||
}
|
||||
|
||||
///===================================================================================
|
||||
/// XmlParser.getMbitMessage
|
||||
///===================================================================================
|
||||
/// <summary>
|
||||
/// Parse the message defined by the XML
|
||||
/// </summary>
|
||||
/// <param name="nav">navigator object that points to the current XML node we are at</param>
|
||||
/// <param name="nameSpace">the XML file name that defines this message</param>
|
||||
/// <param name="mbitMessages">list of messages defined by XML</param>
|
||||
/// <param name="duplicateMbitMessages">the Dictioanry that stores duplicate messages</param>
|
||||
///===================================================================================
|
||||
public static void GetMbitMessage(XPathNavigator nav, string nameSpace, List<XmlMbitMessage> mbitMessages, Dictionary<string, List<string>> duplicateMbitMessages)
|
||||
{
|
||||
var mbitMsg = new XmlMbitMessage()
|
||||
{
|
||||
nameSpace = nameSpace
|
||||
};
|
||||
|
||||
if (nav.MoveToFirstChild())
|
||||
{
|
||||
do
|
||||
{
|
||||
if (string.Equals(nav.Name, "name", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
mbitMsg.name = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "label", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
mbitMsg.label = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "instrulabel", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
mbitMsg.instruLabel = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "description", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
mbitMsg.description = nav.Value.Trim();
|
||||
}
|
||||
else if (string.Equals(nav.Name, "comment", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
mbitMsg.comments.Add(nav.Value.Trim());
|
||||
}
|
||||
else if (string.Equals(nav.Name, "item", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(nav.Name, "struct_item", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(nav.Name, "msg_item", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (nav.MoveToFirstChild())
|
||||
{
|
||||
XmlStructureItem structureItem = new XmlStructureItem()
|
||||
{
|
||||
nameSpace = nameSpace
|
||||
};
|
||||
GetStructureItem(nav, structureItem);
|
||||
|
||||
mbitMsg.dataItems.Add(structureItem);
|
||||
|
||||
nav.MoveToParent();
|
||||
}
|
||||
}
|
||||
else if (nav.Name.Length > 0)
|
||||
{
|
||||
throw new XmlParsingException("Unknown element \"" + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + "\" on line " + ((IXmlLineInfo)nav).LineNumber.ToString());
|
||||
}
|
||||
|
||||
} while (nav.MoveToNext());
|
||||
|
||||
nav.MoveToParent();
|
||||
|
||||
int index = mbitMessages.FindIndex(f => string.Equals(f.name, mbitMsg.name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
// save file name the defines this message that is a duplicate
|
||||
if (!duplicateMbitMessages.ContainsKey(mbitMsg.name))
|
||||
{
|
||||
duplicateMbitMessages[mbitMsg.name] = new List<string>();
|
||||
}
|
||||
|
||||
int index3 = duplicateMbitMessages[mbitMsg.name].FindIndex(f => string.Equals(f, nameSpace + ".xml", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (index3 < 0)
|
||||
duplicateMbitMessages[mbitMsg.name].Add(nameSpace + ".xml");
|
||||
|
||||
// see if the official message is already in the duplicate list
|
||||
int index2 = duplicateMbitMessages[mbitMsg.name].FindIndex(f => string.Equals(f, mbitMessages[index].nameSpace + ".xml", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// if the official message is not in the duplicate list, we want to save it in the duplicate list
|
||||
if (index2 < 0)
|
||||
{
|
||||
duplicateMbitMessages[mbitMsg.name].Add(mbitMessages[index].nameSpace + ".xml");
|
||||
}
|
||||
|
||||
// the existing message is defined in an xml file other than MsgsMc.xml. At this time, we want the messages in MsgsMc.xml to take precedence over other xml files.
|
||||
// Why is the same message being defined in multiple xml files?
|
||||
if (!string.Equals(mbitMessages[index].nameSpace, "msgsmc", StringComparison.OrdinalIgnoreCase) && string.Equals(nameSpace, "msgsmc", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
mbitMessages.RemoveAt(index);
|
||||
mbitMessages.Add(mbitMsg);
|
||||
}
|
||||
}
|
||||
else
|
||||
mbitMessages.Add(mbitMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// **********************************************************************************************************
|
||||
// XmlParsingException.cs
|
||||
// 6/6/2022
|
||||
// NGI - Next Generation Interceptor
|
||||
//
|
||||
// Contract No. HQ0856-21-C-0003/1022000209
|
||||
//
|
||||
// THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S.
|
||||
// INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
|
||||
//
|
||||
// RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION PROPRIETARY TO RAYTHEON
|
||||
// COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT.
|
||||
// DISCLOSURE TO UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO RAYTHEON
|
||||
// COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS CONTENTS SHALL BE FURNISHED OR DISCLOSED
|
||||
// TO OR COPIED OR USED BY PERSONS OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF
|
||||
// RAYTHEON COMPANY.
|
||||
//
|
||||
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
|
||||
//
|
||||
// DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M,
|
||||
// NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006,
|
||||
// INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3,
|
||||
// DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3,
|
||||
// SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4,
|
||||
// INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
|
||||
//
|
||||
// CONTROLLED BY: MISSILE DEFENSE AGENCY
|
||||
// CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
|
||||
// CUI CATEGORY: CTI
|
||||
// DISTRIBUTION/DISSEMINATION CONTROL: F
|
||||
// POC: Alex Kravchenko (1118268)
|
||||
// **********************************************************************************************************
|
||||
using System;
|
||||
|
||||
namespace Raytheon.Common.Coe
|
||||
{
|
||||
class XmlParsingException : SystemException
|
||||
{
|
||||
///===================================================================================
|
||||
/// CodeGenerator.XmlParsingException
|
||||
///===================================================================================
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="message">description of the exception</param>
|
||||
///===================================================================================
|
||||
public XmlParsingException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user