} // InitialzeInstance /// <summary> /// Parse the detail items, of which two are currently defined, into the /// properties of a new ASCIICharacterDisplayInfo instance, which can be /// fully initialized by any of its three public constructors, depending /// on what properties have values. /// </summary> /// <param name="pintNextSlot"> /// Argument pintNextSlot is the subscript of the _asciiTable element to /// store the current character. /// /// The _asciiTable array contains 256 elements, which happens to be the /// number of ASCII characters. Since characters are numbered from zero /// through 255, the ASCII code is the obvious index for the array. /// /// Instance member _asciiTable is ab array of ASCIICharacterDisplayInfo /// objects that is initialized with the details read from the XML /// document in which they are stored. The XML document is stored in the /// DLL as n custom resource. /// </param> /// <param name="pxmlCharacterInfo"> /// Each character is represented as a XmlNode; this method processes the /// detail items on one such node. /// </param> /// <param name="puintNodeCode"> /// This is the ASCII code, which the calling routine derives by parsing /// its first child node, which is required to store the ASCII code. /// /// Since this routine processes an embedded XML document, we can afford /// to impose a rigid schema. /// </param> /// <param name="pintChildRank"> /// Each invocation of this method processes one child node on the XmlNode /// supplied as its pxmlCharacterInfo argument. The calling routine keeps /// track of the number of children, and calls it once for each child. /// </param> private void ParseDetailItem ( int pintNextSlot , XmlNode pxmlCharacterInfo , uint puintNodeCode , int pintChildRank ) { const string ASCII_DISPLAY_ALTERNATIVE_NODE_NAME = @"Display"; const string ASCII_DISPLAY_COMMENT = @"Comment"; XmlNode xmlDetailItem = pxmlCharacterInfo.ChildNodes [ pintChildRank ]; if ( xmlDetailItem.Name == ASCII_DISPLAY_ALTERNATIVE_NODE_NAME ) _asciiTable [ pintNextSlot ] = new ASCIICharacterDisplayInfo ( puintNodeCode , xmlDetailItem.InnerXml ); else if ( xmlDetailItem.Name == ASCII_DISPLAY_COMMENT ) _asciiTable [ pintNextSlot ] = new ASCIICharacterDisplayInfo ( puintNodeCode , null , xmlDetailItem.InnerXml ); else throw new InvalidOperationException ( Properties.Resources.ERRMSG_INVALID_NODE_IN_ASCII_TABLE + pxmlCharacterInfo.InnerXml ); } // ParseDetailItem
} // AllASCIICharacters #endregion // Instance Properties #region Private Instance Methods /// <summary> /// Since the class is a singleton, I separated the initializer from the /// constructor, as has been my custom. /// </summary> private void InitialzeInstance ( ) { const int ASCII_CHARACTER_COUNT = 256; const string ASCII_TABLE_SOURCE = @"ASCII_Character_Display_Table.TSV"; // ---------------------------------------------------------------- // The expected row count is one greater than the number of ASCII // characters because the first row in the table contains column // labels. // // As a sanity check, the label row is compared against a constant, // LABEL_ROW. // ---------------------------------------------------------------- const int EXPECTED_ROW_COUNT = ASCII_CHARACTER_COUNT + ArrayInfo.ORDINAL_FROM_INDEX; //const int COL_CODE = 0; const int COL_CHARTYPE = 1; const int COL_SUBTYPE = 2; const int COL_CHAR = 3; const int COL_DESCRIPTION = 4; const int COL_HTML_NAME = 5; const int COL_DISPLAY = 6; const int COL_COMMENT = 7; const int COL_EXPECTED_COUNT = 8; const string LABEL_ROW = "Code\tCharType\tSubtype\tCHAR\tDESCRIPTION\tHTML Name\tDisplay\tComment"; string [ ] astrASCIITable = Readers.LoadTextFileFromCallingAssembly ( ASCII_TABLE_SOURCE ); if ( astrASCIITable.Length == EXPECTED_ROW_COUNT ) { _asciiTable = new ASCIICharacterDisplayInfo [ ASCII_CHARACTER_COUNT ]; for ( int intJ = ArrayInfo.ARRAY_FIRST_ELEMENT ; intJ < EXPECTED_ROW_COUNT ; intJ++ ) { if ( intJ == ArrayInfo.ARRAY_FIRST_ELEMENT ) { if ( astrASCIITable [ ArrayInfo.ARRAY_FIRST_ELEMENT ] != LABEL_ROW ) { // Verify that the label row is as expected. throw new Exception ( string.Format ( Properties.Resources.ERRMSG_BAD_LABEL_ROW , // Format Control String: Internal Table Error: The label row is invalid.{2}Expected label row = {0}{2}Actaul label row = {1} LABEL_ROW , // Format Item 0: Expected label row = {0} astrASCIITable [ ArrayInfo.ARRAY_FIRST_ELEMENT ] , // Format Item 1: Actaul label row = {1} Environment.NewLine ) ); // Format Item 2: Line break between message parts } // if ( astrASCIITable [ ArrayInfo.ARRAY_FIRST_ELEMENT ] != LABEL_ROW ) } // TRUE (This is the label row.) block, if ( intJ == ArrayInfo.ARRAY_FIRST_ELEMENT ) else { string [ ] astrFields = astrASCIITable [ intJ ].Split ( SpecialCharacters.TAB_CHAR ); if ( astrFields.Length == COL_EXPECTED_COUNT ) { int intCharacterCode = ArrayInfo.IndexFromOrdinal ( intJ ); _asciiTable [ intCharacterCode ] = new ASCIICharacterDisplayInfo ( ( uint ) intCharacterCode , // uint puintCode ( char ) intCharacterCode , // char pchrCharacter ( ASCIICharacterDisplayInfo.CharacterType ) Enum.Parse ( // CharacterType penmCharacterType typeof ( ASCIICharacterDisplayInfo.CharacterType ) , // Type enumType astrFields [ COL_CHARTYPE ] , // string value true ) , // bool ignoreCase ( ASCIICharacterDisplayInfo.CharacterSubtype ) Enum.Parse ( // CharacterType penmCharacterType typeof ( ASCIICharacterDisplayInfo.CharacterSubtype ) , // Type enumType astrFields [ COL_SUBTYPE ] , // string value true ) , // bool ignoreCase astrFields [ COL_CHAR ] , // string pstrCHAR astrFields [ COL_DESCRIPTION ] , // string pstrDescription astrFields [ COL_HTML_NAME ] , // string pstrHTMLName astrFields [ COL_DISPLAY ] , // string pstrAlternateText astrFields [ COL_COMMENT ] ); // string pstrComment } // TRUE (anticipated outcome) block, if ( astrFields.Length == COL_EXPECTED_COUNT ) else { throw new Exception ( string.Format ( Properties.Resources.ERRMSG_BAD_DETAIL_ROW ,// Format Control String: Internal Table Error: Detaill row {0} is invalid.{3}Expected field count = {1}{3}Actaul field count = {2} intJ , // Format Item 0: Detaill row {0} is invalid. COL_EXPECTED_COUNT , // Format Item 1: Expected field count = {1} astrFields.Length , // Format Item 2: Actaul field count = {2} Environment.NewLine ) ); // Format Item 3: Line Break } // FALSE (unanticipated outcome) block, if ( astrFields.Length == COL_EXPECTED_COUNT ) } // FALSE (This is a detail row.) block, if ( intJ == ArrayInfo.ARRAY_FIRST_ELEMENT ) } // for ( int intJ = ArrayInfo.ARRAY_FIRST_ELEMENT ; intJ < EXPECTED_ROW_COUNT ; intJ++ ) } // TRUE (anticipated outcome) block, if ( astrASCIITable.Length == EXPECTED_ROW_COUNT ) else { throw new Exception ( string.Format ( Properties.Resources.ERRMSG_UNEXPECTED_ROW_COUNT , // Format Control String: Internal Table Error: The ASCII Table should contain {0} rows. Instead, it contains {1} rows. EXPECTED_ROW_COUNT , // Format Item 0: The ASCII Table should contain {0} rows. astrASCIITable.Length ) ); // Format Item 1: Instead, it contains {1} rows. } // FALSE (unanticipated outcome) block, if ( astrASCIITable.Length == EXPECTED_ROW_COUNT ) } // InitialzeInstance
} // public override string ToString #endregion // Base Class Method Overrides #region Public Static Methods /// <summary> /// Create a ASCIICharacterDisplayInfo instance to represent a specified /// ASCII character, and call its ToString method to return all three /// representations of it (Printable, Hexadecimal, and Decimal, in that /// order. /// </summary> /// <param name="pchr"> /// Specify the character for which to render the three representations. /// </param> /// <returns> /// Return the output of ToString on the ASCIICharacterDisplayInfo. /// </returns> public static string DisplayCharacterInfo ( char pchr ) { ASCIICharacterDisplayInfo aSCIICharacterDisplayInfo = new ASCIICharacterDisplayInfo ( ( uint ) pchr ); return aSCIICharacterDisplayInfo.ToString ( ); } // public static string DisplayCharacterInfo
} // AllASCIICharacters #endregion // Instance Properties #region Private Instance Methods /// <summary> /// Since the class is a singleton, I separated the initializer from the /// constructor, as has been my custom. /// </summary> private void InitialzeInstance ( ) { const int ARRAY_INVALID_INDEX = -1; const string ASCII_CODE_NODE_NAME = @"Code"; // ---------------------------------------------------------------- // The file is named ASCII_Character_Display_Table.xml in the file // system, but the default namespace name, in this case, WizardWrx, // prepends the name when the bound resource is created. // ---------------------------------------------------------------- const string ASCII_TABLE_SOURCE = @"WizardWrx.ASCII_Character_Display_Table.xml"; const int NODE_COUNT_CODE_BY_ITSELF = 1; const int NODE_COUNT_CODE_WITH_ALTERNATE_OR_COMMENT = 2; const int NODE_COUNT_CODE_WITH_ALTERNATE_AND_COMMENT = 3; const int NODE_INDEX_OF_CODE = 0; const int NODE_INDEX_OF_DETAIL_ITEM_1 = 1; const int NODE_INDEX_OF_DETAIL_ITEM_2 = 2; const int REAL_ROOT_NODE_INDEX = 2; XmlDocument xmlASCIITable = new XmlDocument ( ); xmlASCIITable.Load ( System.Reflection.Assembly.GetExecutingAssembly ( ).GetManifestResourceStream ( ASCII_TABLE_SOURCE ) ); XmlNode xmlRealParent = xmlASCIITable.ChildNodes [ REAL_ROOT_NODE_INDEX ]; int intNextSlot = ARRAY_INVALID_INDEX; _asciiTable = new ASCIICharacterDisplayInfo [ xmlRealParent.ChildNodes.Count ]; foreach ( XmlNode xmlCharacter in xmlRealParent.ChildNodes ) { int intGrandChildren = xmlCharacter.ChildNodes.Count; ++intNextSlot; XmlNode xmlCode = xmlCharacter.ChildNodes [ NODE_INDEX_OF_CODE ]; if ( xmlCode.Name != ASCII_CODE_NODE_NAME ) throw new InvalidOperationException ( Properties.Resources.ERRMSG_INVALID_NODE_IN_ASCII_TABLE + xmlCharacter.InnerXml ); uint uintNodeCode; if ( uint.TryParse ( xmlCode.InnerXml , out uintNodeCode ) ) { // It's a valid integer. switch ( intGrandChildren ) { case NODE_COUNT_CODE_BY_ITSELF: _asciiTable [ intNextSlot ] = new ASCIICharacterDisplayInfo ( uintNodeCode ); break; // case NODE_COUNT_CODE_BY_ITSELF case NODE_COUNT_CODE_WITH_ALTERNATE_OR_COMMENT: ParseDetailItem ( intNextSlot , xmlCharacter , uintNodeCode , NODE_INDEX_OF_DETAIL_ITEM_1 ); break; // case NODE_COUNT_CODE_WITH_ALTERNATE case NODE_COUNT_CODE_WITH_ALTERNATE_AND_COMMENT: for ( int intNodeIndex = NODE_INDEX_OF_DETAIL_ITEM_1 ; intNodeIndex < NODE_INDEX_OF_DETAIL_ITEM_2 ; intNodeIndex++ ) ParseDetailItem ( intNextSlot , xmlCharacter , uintNodeCode , intNodeIndex ); break; // case NODE_COUNT_CODE_WITH_ALTERNATE_AND_COMMENT } // switch ( intGrandChildren ) } // TRUE (expected outcome) block, if ( uint.TryParse ( xmlCode.InnerXml , out uintNodeCode ) ) else { // If I can't use this node, I am unprepared to trest the rest of them. throw new InvalidOperationException ( Properties.Resources.ERRMSG_INVALID_NODE_IN_ASCII_TABLE + xmlCharacter.InnerXml ); } // FALSE (UNexpected outcome) block, if ( uint.TryParse ( xmlCode.InnerXml , out uintNodeCode ) ) } // foreach ( XmlNode xmlCharacter in xmlRealParent.ChildNodes ) } // InitialzeInstance