} // public static char LastCharacterOfString /// <summary> /// Returns the character at the Nth position in a string. /// </summary> /// <param name="pstrIn"> /// Specify the string from which to return the last character. /// </param> /// <param name="pintOrdinalPosition"> /// Specify the ordinal position of the desired character. Unlike array /// subscripts, which start at zero, ordinals start at one. /// </param> /// <returns> /// Unless the input string is empty (or null), or pintOrdinalPosition /// is less than 1 or greater than the length of the string, the return /// value is the character at the specified (Nth) position. /// /// If the function cannot return the requested character, the return /// value is SpecialCharacters.NUL, the null character. /// </returns> public static char NthCharacterOfString ( string pstrIn , int pintOrdinalPosition ) { if ( string.IsNullOrEmpty ( pstrIn ) ) return SpecialCharacters.NULL_CHAR; else if ( pintOrdinalPosition <= ArrayInfo.ARRAY_FIRST_ELEMENT || pintOrdinalPosition > pstrIn.Length ) return SpecialCharacters.NULL_CHAR; else return pstrIn [ ArrayInfo.IndexFromOrdinal ( pintOrdinalPosition ) ]; } // public static char NthCharacterOfString
} // private int FixThisItem /// <summary> /// This private static method encapsulates the work of loading an /// embedded text file resource that consists of tab delimited strings, /// and parsing them into pairs of strrings. /// </summary> /// <param name="pstrEmbeddedResourceName"> /// This string is expected to contain the unqualified name of the /// embedded text file resource, which is easy to grab from the Solution /// Explorer. This routine appends the extension, which must be .txt. /// </param> /// <returns> /// The return value is an array of StringFixups.StringFixup objects, /// each consisting of a pair of strings, one being a string for which /// to search, while its companion is the replacement string. /// </returns> private static StringFixups.StringFixup [ ] LoadStringFixups ( string pstrEmbeddedResourceName ) { const string LABEL_ROW = @"JSON VS"; const string TSV_EXTENSION = @".txt"; const int STRING_PER_RESPONSE = ArrayInfo.ARRAY_FIRST_ELEMENT; const int STRING_FOR_JSONCONVERTER = STRING_PER_RESPONSE + ArrayInfo.NEXT_INDEX; const int EXPECTED_FIELD_COUNT = STRING_FOR_JSONCONVERTER + ArrayInfo.NEXT_INDEX; string strEmbeddResourceFileName = string.Concat ( pstrEmbeddedResourceName , TSV_EXTENSION ); string [ ] astrAllMapItems = Readers.LoadTextFileFromEntryAssembly ( strEmbeddResourceFileName ); Parser parser = new Parser ( CSVParseEngine.DelimiterChar.Tab , CSVParseEngine.GuardChar.DoubleQuote , CSVParseEngine.GuardDisposition.Strip ); StringFixups.StringFixup [ ] rFunctionMaps = new StringFixups.StringFixup [ ArrayInfo.IndexFromOrdinal ( astrAllMapItems.Length ) ]; for ( int intI = ArrayInfo.ARRAY_FIRST_ELEMENT ; intI < astrAllMapItems.Length ; intI++ ) { if ( intI == ArrayInfo.ARRAY_FIRST_ELEMENT ) { if ( astrAllMapItems [ intI ] != LABEL_ROW ) { throw new Exception ( string.Format ( Properties.Resources.ERRMSG_CORRUPTED_EMBBEDDED_RESOURCE_LABEL , new string [ ] { strEmbeddResourceFileName , // Format Item 0: internal resource {0} LABEL_ROW , // Format Item 1: Expected value = {1} astrAllMapItems [ intI ] , // Format Item 2: Actual value = {2} Environment.NewLine // Format Item 3: Platform-specific newline } ) ); } // if ( astrAllMapItems[intI] != LABEL_ROW ) } // TRUE (label row sanity check 1 of 2) block, if ( intI == ArrayInfo.ARRAY_FIRST_ELEMENT ) else { string [ ] astrFields = parser.Parse ( astrAllMapItems [ intI ] ); if ( astrFields.Length == EXPECTED_FIELD_COUNT ) { rFunctionMaps [ ArrayInfo.IndexFromOrdinal ( intI ) ] = new StringFixups.StringFixup ( astrFields [ STRING_PER_RESPONSE ] , astrFields [ STRING_FOR_JSONCONVERTER ] ); } // TRUE (anticipated outcome) block, if ( astrFields.Length == EXPECTED_FIELD_COUNT ) else { throw new Exception ( string.Format ( Properties.Resources.ERRMSG_CORRUPTED_EMBEDDED_RESOURCE_DETAIL , new object [ ] { intI , // Format Item 0: Detail record {0} strEmbeddResourceFileName , // Format Item 1: internal resource {1} EXPECTED_FIELD_COUNT , // Format Item 2: Expected field count = {2} astrFields.Length , // Format Item 3: Actual field count = {3} astrAllMapItems [ intI ] , // Format Item 4: Actual record = {4} Environment.NewLine // Format Item 5: Platform-specific newline } ) ); } // FALSE (unanticipated outcome) block, if ( astrFields.Length == EXPECTED_FIELD_COUNT ) } // FALSE (detail row) block, if ( intI == ArrayInfo.ARRAY_FIRST_ELEMENT ) } // for ( int intI = ArrayInfo.ARRAY_FIRST_ELEMENT ; intI < astrAllMapItems.Length ; intI++ ) return rFunctionMaps; } // private static StringFixups.StringFixup [ ] GetSStringFixups
} // private void InitializeOnFirstUse /// <summary> /// This method creates a generic dictionary of ParameterTypeInfo /// objects, keyed by parameter name from an array of strings, each of which /// is the internal name of a parameter. /// </summary> /// <param name="pastrParameterTypeInfoArray"> /// This array of strings is a list of parameter names and property /// values, such as generic ParameterType enumeration values, that is /// read from a TAB delimited text file that is stored in an embeddede /// assembly resource. /// /// The first string is expected to be a label row that maps the fields /// in the remaining strings to their values. /// </param> /// <returns> /// If it succeeds, the return value is a populated dictionary of /// ParameterTypeInfo objects keyed by their internal parameter names. /// Invalid data or a corrupted parameter resource file causes the /// ParameterTypeInfo constructor to throw an InvalidOperationException. /// </returns> /// <exception cref="InvalidOperationException"> /// A detailed InvalidOperationException exception report arises if the /// array of parameters is invalid. /// /// Correct code should never throw this exception. /// </exception> private Dictionary<string , ParameterTypeInfo<T>> GetParameterTypeInfo ( string [ ] pastrParameterTypeInfoArray ) { Dictionary<string , ParameterTypeInfo<T>> rdctParameterTypeInfo = new Dictionary<string , ParameterTypeInfo<T>> ( ArrayInfo.IndexFromOrdinal ( pastrParameterTypeInfoArray.Length ) ); for ( int intPosition = ArrayInfo.ARRAY_SECOND_ELEMENT ; intPosition < pastrParameterTypeInfoArray.Length ; intPosition++ ) { ParameterTypeInfo<T> pti = new ParameterTypeInfo<T> ( pastrParameterTypeInfoArray [ intPosition ] , // string pstrParameterInfoDetail pastrParameterTypeInfoArray [ ArrayInfo.ARRAY_FIRST_ELEMENT ] ); // string pastrParameterInfoColumnNames rdctParameterTypeInfo.Add ( pti.ParameterName , // Key pti ); // Value } // for ( int intPosition = ArrayInfo.ARRAY_SECOND_ELEMENT ; intPosition < pastrParameterTypeInfoArray.Length ; intPosition++ ) return rdctParameterTypeInfo; } // GetParameterTypeInfo
} // public string GetDLLSetting /// <summary> /// Set the like named properties from the linked configuration file. /// </summary> /// <param name="pderivedType"> /// When the derived class constructor calls this method, it must pass in a /// reference to its own Type property. /// </param> /// <returns> /// The return value is the count of properties that were set. /// </returns> /// <remarks> /// This can almost certainly be simplified by enumerating the settings, /// but either way risks a NOT FOUND exception. /// /// This method uses some fairly tricky Reflection gymnastics to map the /// key names in a configuration file to property names on an object. /// </remarks> protected PropertySourceCounts SetPropertiesFromDLLConfiguration ( Type pderivedType ) { const BindingFlags BASELINE_BINDING_FLAGS = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; PropertySourceCounts rutpCounts = new PropertySourceCounts ( ); rutpCounts.Defaulted = MagicNumbers.ZERO; rutpCounts.SpecifiedInConfiguration = MagicNumbers.ZERO; string [ ] astrDefaultErrorMessageColors = DLLSettings.AllKeys; for ( int intJ = ArrayInfo.ARRAY_FIRST_ELEMENT ; intJ < astrDefaultErrorMessageColors.Length ; intJ++ ) { string strPropertyName = null; // This must be visible to the catch block. try { strPropertyName = astrDefaultErrorMessageColors [ intJ ]; string strConfigValueString = DLLSettings [ strPropertyName ].Value; TraceLogger.WriteWithBothTimesLabeledLocalFirst ( string.Format ( @"At iteration {0} in {1}: {2} = {3}" , // Format control string new object [ ] // Array of values to substitute for tokens in format control string { ArrayInfo.OrdinalFromIndex ( intJ ) , // Format Item 0: At iteration {0} MethodBase.GetCurrentMethod ( ).Name , // Format Item 1: in {1} strPropertyName , // Format Item 2: : {2} strConfigValueString // Format Item 3: = {3} } ) ); PropertyInfo piThisProperty = this.GetType ( ).GetProperty ( strPropertyName , // string name = The string containing the name of the property to get BASELINE_BINDING_FLAGS ); // bindingAttr BindingFlags = A bitmask comprised of one or more BindingFlags that specify how the search is conducted if ( piThisProperty != null ) { piThisProperty.SetValue ( this , // Object obj = Object to which to apply setting FromString ( piThisProperty , strConfigValueString ) , // Object value = Value to assign to the property to which piThisProperty refers BASELINE_BINDING_FLAGS | BindingFlags.SetProperty , // invokeAttr invokeAtt = A bitwise combination of the following enumeration members that specify the invocation attribute: InvokeMethod, reateInstance, Static, GetField, SetField, GetProperty, or SetProperty. You must specify a suitable invocation attribute. null , // Binder binder = An object that enables the binding, coercion of argument types, invocation of members, and retrieval of MemberInfo objects through reflection. If binder is null, the default binder is used. null , // Object[] index = Optional index values for indexed properties. This value should be null for non-indexed properties. null ); // CultureInfo culture = The culture for which the resource is to be localized. If the resource is not localized for this culture, the Parent property will be called successively in search of a match. If this value is null, the culture-specific information is obtained from the CurrentUICulture property. rutpCounts.SpecifiedInConfiguration++; // Count properties successfully set; the final value is returned as the function value. } // TRUE (The configuration file has a value for this property.) block, if ( piThisProperty != null ) else { MissingConfigSettings = MissingConfigSettings ?? UnconfiguredDLLSettings.TheOnlyInstance; MissingConfigSettings.Add ( Path.GetFileName ( _strAssemblyLocation ) , strPropertyName , // Format Item 0: property {1} default value strConfigValueString ); // Format Item 1: default value of {2} accepted rutpCounts.Defaulted++; // Count properties that retained their hard coded default values. } // FALSE (A value for this property is absent from the configuration file.) block, if ( piThisProperty != null ) } catch ( Exception exAllKinds ) { // Since the catch block is within the body of the For loop, processing advances to the next item. TraceLogger.WriteWithBothTimesLabeledLocalFirst ( string.Format ( @"{0}.{1} = {2}" , nameof ( Environment ) , nameof ( Environment.StackTrace ) , Environment.StackTrace ) ); if ( RecoveredConfigurationExceptions == null ) { // Create as and when needed. RecoveredConfigurationExceptions = new System.Collections.Generic.List<RecoveredException> ( ); } // if ( RecoveredConfigurationExceptions == null ) RecoveredConfigurationExceptions.Add ( new RecoveredException ( string.Format ( Properties.Resources.ERRMSG_EXCEPTION_NOT_THROWN , SaveErrorReport ( this.GetType ( ) , strPropertyName , exAllKinds ).Message ) , exAllKinds.Source , exAllKinds.StackTrace , exAllKinds.TargetSite.Name ) ); Exception exWrapped = RecoveredConfigurationExceptions [ ArrayInfo.IndexFromOrdinal ( RecoveredConfigurationExceptions.Count ) ]; TraceLogger.WriteWithBothTimesLabeledLocalFirst ( string.Format ( Properties.Resources.TRACEMSG_EXCEPTION_NOT_THROWN_1 , exWrapped.Message ) ); TraceLogger.WriteWithBothTimesLabeledLocalFirst ( string.Format ( Properties.Resources.TRACEMSG_EXCEPTION_NOT_THROWN_2 , exWrapped.Source ?? Common.Properties.Resources.MSG_OBJECT_REFERENCE_IS_NULL ) ); TraceLogger.WriteWithBothTimesLabeledLocalFirst ( string.Format ( Properties.Resources.TRACEMSG_EXCEPTION_NOT_THROWN_3 , exWrapped.TargetSite != null ? exWrapped.TargetSite.Name : Common.Properties.Resources.MSG_OBJECT_REFERENCE_IS_NULL ) ); TraceLogger.WriteWithBothTimesLabeledLocalFirst ( string.Format ( Properties.Resources.TRACEMSG_EXCEPTION_NOT_THROWN_4 , exWrapped.StackTrace ?? Common.Properties.Resources.MSG_OBJECT_REFERENCE_IS_NULL ) ); // -------------------------------------------------------- // Since SaveErrorReport appends the inner exception, this // test is technically redundant, but I chose to leave it, // as a reminder that new exceptions are not necessarily // so decordated. Hence, with the test, this code is a bit // more portable. // -------------------------------------------------------- if ( exWrapped.InnerException != null ) { TraceLogger.WriteWithBothTimesLabeledLocalFirst ( string.Format ( Properties.Resources.TRACEMSG_EXCEPTION_NOT_THROWN_5 , exWrapped.InnerException.Message ?? Common.Properties.Resources.MSG_OBJECT_REFERENCE_IS_NULL ) ); TraceLogger.WriteWithBothTimesLabeledLocalFirst ( string.Format ( Properties.Resources.TRACEMSG_EXCEPTION_NOT_THROWN_6 , exWrapped.InnerException.Source ?? Common.Properties.Resources.MSG_OBJECT_REFERENCE_IS_NULL ) ); TraceLogger.WriteWithBothTimesLabeledLocalFirst ( string.Format ( Properties.Resources.TRACEMSG_EXCEPTION_NOT_THROWN_7 , exWrapped.InnerException.TargetSite.Name ?? Common.Properties.Resources.MSG_OBJECT_REFERENCE_IS_NULL ) ); TraceLogger.WriteWithBothTimesLabeledLocalFirst ( string.Format ( Properties.Resources.TRACEMSG_EXCEPTION_NOT_THROWN_8 , exWrapped.InnerException.StackTrace ?? Common.Properties.Resources.MSG_OBJECT_REFERENCE_IS_NULL ) ); } // TRUE (anticipated outcome) block, if ( exWrapped.InnerException != null ) else { TraceLogger.WriteWithBothTimesLabeledLocalFirst ( Properties.Resources.TRACEMSG_EXCEPTION_NOT_THROWN_9 ); } // FALSE (unanticipated outcome) block, if ( exWrapped.InnerException != null ) } // catch ( Exception exAllKinds ) } // for ( int intJ = ArrayInfo.ARRAY_FIRST_ELEMENT ; intJ < astrDefaultErrorMessageColors.Length ; intJ++ ) return rutpCounts; } // SetPropertiesFromDLLConfiguration