// ********************************************************************************************************** // 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 _commonElements = new Dictionary(StringComparer.InvariantCultureIgnoreCase); // store messages defined in xml files private readonly List _mbitMessages = new List(); // store duplicate messages for diagnostic purposes private readonly Dictionary> _duplicateMbitMessages = new Dictionary>(); // store each enumeration type and its associate key/value pairs private readonly Dictionary _xmlEnums = new Dictionary(StringComparer.InvariantCultureIgnoreCase); // store duplicate enumerations for diagnostic purposes private readonly Dictionary> _duplicateEnums = new Dictionary>(); // store each structure type and its associated data members private readonly Dictionary _xmlStructures = new Dictionary(StringComparer.InvariantCultureIgnoreCase); // store duplicate structures for diagnostic purposes private readonly Dictionary> _duplicateStructures = new Dictionary>(); // 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 _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 _xmlConstantsLookUpByNameSpace = null; // store duplicate constants for diagnostic purposes private readonly Dictionary> _duplicateConstants = new Dictionary>(); // store names of files that contain data types that we need to generate public List m_dataTypeFilesToBeGenerated = new List(); /// /// Parse XML files from the path folder /// /// /// public bool ParseXmlFiles(string path) { bool isSuccessful = true; string xmlNamespace = string.Empty; List constantList = new List(); if (!Directory.Exists(path)) { _logger.Error($"Path {path} not found"); isSuccessful = false; return isSuccessful; } string[] files = Directory.GetFiles(path, "*.xml", SearchOption.AllDirectories); List dataTypeFiles = new List(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 comments = new List(); 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> 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> 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> 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> 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; } } }