Refactored the ini code to be more "OO", making it possible to reset a value which was needed to implement an auto-repair for certain settings. This was intiated by Bug #3509754, which hat the reason that somehow the Hotkey name was corrupt.

git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@1721 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
RKrom 2012-03-22 12:00:07 +00:00
commit 4b3f7ba0a5
10 changed files with 507 additions and 440 deletions

View file

@ -221,7 +221,7 @@ namespace Greenshot.IniFile {
Read(CreateIniLocation(configName + FIXED_POSTFIX + INI_EXTENSION));
foreach (IniSection section in sectionMap.Values) {
FillIniSection(section);
section.Fill(PropertiesForSection(section));
}
}
@ -278,8 +278,7 @@ namespace Greenshot.IniFile {
// Store for later save & retrieval
sectionMap.Add(sectionName, section);
FillIniSection(section);
//LOG.Debug("Returning newly mapped section " + sectionName);
section.Fill(PropertiesForSection(section));
}
if (section.IsDirty) {
LOG.DebugFormat("Section {0} is marked dirty, saving!", sectionName);
@ -288,7 +287,7 @@ namespace Greenshot.IniFile {
return section;
}
private static void FillIniSection(IniSection section) {
public static Dictionary<string, string> PropertiesForSection(IniSection section) {
Type iniSectionType = section.GetType();
string sectionName = getSectionName(iniSectionType);
// Get the properties for the section
@ -299,279 +298,7 @@ namespace Greenshot.IniFile {
sections.Add(sectionName, new Dictionary<string, string>());
properties = sections[sectionName];
}
// Iterate over the members and create IniValueContainers
foreach (FieldInfo fieldInfo in iniSectionType.GetFields()) {
if (Attribute.IsDefined(fieldInfo, typeof(IniPropertyAttribute))) {
if (!section.Values.ContainsKey(fieldInfo.Name)) {
IniPropertyAttribute iniPropertyAttribute = (IniPropertyAttribute)fieldInfo.GetCustomAttributes(typeof(IniPropertyAttribute), false)[0];
section.Values.Add(fieldInfo.Name, new IniValue(section, fieldInfo, iniPropertyAttribute));
}
}
}
foreach (PropertyInfo propertyInfo in iniSectionType.GetProperties()) {
if (Attribute.IsDefined(propertyInfo, typeof(IniPropertyAttribute))) {
if (!section.Values.ContainsKey(propertyInfo.Name)) {
IniPropertyAttribute iniPropertyAttribute = (IniPropertyAttribute)propertyInfo.GetCustomAttributes(typeof(IniPropertyAttribute), false)[0];
section.Values.Add(propertyInfo.Name, new IniValue(section, propertyInfo, iniPropertyAttribute));
}
}
}
foreach (string fieldName in section.Values.Keys) {
IniValue iniValue = section.Values[fieldName];
string propertyName = iniValue.Attributes.Name;
string propertyDefaultValue = iniValue.Attributes.DefaultValue;
// Get the value from the ini file, if there is none take the default
if (!properties.ContainsKey(propertyName) && propertyDefaultValue != null) {
// Mark as dirty, we didn't use properties from the file (even defaults from the default file are allowed)
section.IsDirty = true;
//LOG.Debug("Passing default: " + propertyName + "=" + propertyDefaultValue);
}
// Try to get the field value from the properties or use the default value
object fieldValue = null;
try {
fieldValue = CreateValue(section, sectionName, iniValue);
} catch (Exception) {
//LOG.Warn("Couldn't parse field: " + sectionName + "." + propertyName, e);
}
// If still no value, e.g. due to an exception, check if the GetDefault delivers a value
if (fieldValue == null) {
// Use GetDefault to fill the field if none is set
fieldValue = section.GetDefault(propertyName);
}
// Still no value? Log warning
if (fieldValue == null) {
LOG.WarnFormat("Property {0} has no value or default value.", propertyName);
}
// Set the value
try {
iniValue.Value = fieldValue;
} catch (Exception) {
//LOG.Warn("Couldn't set field: " + sectionName + "." + propertyName, e);
}
}
section.AfterLoad();
}
/// <summary>
/// Helper method for creating a value
/// </summary>
/// <param name="section">IniSection</param>
/// <param name="sectionName">string with name of section</param>
/// <param name="iniValue">IniValue</param>
/// <returns></returns>
private static object CreateValue(IniSection section, string sectionName, IniValue iniValue) {
string propertyName = iniValue.Attributes.Name;
string defaultValue = iniValue.Attributes.DefaultValue;
string arraySeparator = iniValue.Attributes.Separator;
// Get the type, or the underlying type for nullables
Type valueType = iniValue.ValueType;
Dictionary<string, string> properties = sections[sectionName];
bool defaultUsed = false;
string propertyValue = null;
object defaultValueFromConfig = null;
if (properties.ContainsKey(propertyName) && properties[propertyName] != null) {
propertyValue = section.PreCheckValue(propertyName, properties[propertyName]);
} else if (defaultValue != null && defaultValue.Trim().Length != 0) {
propertyValue = defaultValue;
defaultUsed = true;
} else {
LOG.DebugFormat("Property {0} has no value or default value, this might be corrected later!", propertyName);
// Check if the developer implemented a default for the property
defaultValueFromConfig = section.GetDefault(propertyName);
if (defaultValueFromConfig != null) {
LOG.DebugFormat("Default for Property {0} implemented!", propertyName);
} else {
if (iniValue.Attributes.ExcludeIfNull) {
return null;
}
}
}
// Now set the value
if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(List<>)) {
object list = Activator.CreateInstance(valueType);
// Logic for List<>
if (propertyValue == null) {
if (defaultValueFromConfig != null) {
return defaultValueFromConfig;
}
return list;
}
string[] arrayValues = propertyValue.Split(new string[] { arraySeparator }, StringSplitOptions.None);
if (arrayValues == null || arrayValues.Length == 0) {
return list;
}
bool addedElements = false;
bool parseProblems = false;
MethodInfo addMethodInfo = valueType.GetMethod("Add");
foreach (string arrayValue in arrayValues) {
if (arrayValue != null && arrayValue.Length > 0) {
object newValue = null;
try {
newValue = ConvertValueToValueType(valueType.GetGenericArguments()[0], arrayValue);
} catch (Exception) {
//LOG.Error("Problem converting " + arrayValue + " to type " + fieldType.FullName, e);
parseProblems = true;
}
if (newValue != null) {
addMethodInfo.Invoke(list, new object[] { newValue });
addedElements = true;
}
}
}
// Try to fallback on a default
if (!addedElements && parseProblems) {
try {
object fallbackValue = ConvertValueToValueType(valueType.GetGenericArguments()[0], defaultValue);
addMethodInfo.Invoke(list, new object[] { fallbackValue });
return list;
} catch (Exception) {
//LOG.Error("Problem converting " + defaultValue + " to type " + fieldType.FullName, e);
}
}
return list;
} else if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) {
// Logic for Dictionary<,>
Type type1 = valueType.GetGenericArguments()[0];
Type type2 = valueType.GetGenericArguments()[1];
//LOG.Info(String.Format("Found Dictionary<{0},{1}>", type1.Name, type2.Name));
object dictionary = Activator.CreateInstance(valueType);
MethodInfo addMethodInfo = valueType.GetMethod("Add");
bool addedElements = false;
foreach (string key in properties.Keys) {
if (key != null && key.StartsWith(propertyName + ".")) {
// What "key" do we need to store it under?
string subPropertyName = key.Substring(propertyName.Length + 1);
string stringValue = properties[key];
object newValue1 = null;
object newValue2 = null;
try {
newValue1 = ConvertValueToValueType(type1, subPropertyName);
} catch (Exception) {
//LOG.Error("Problem converting " + subPropertyName + " to type " + type1.FullName, e);
}
try {
newValue2 = ConvertValueToValueType(type2, stringValue);
} catch (Exception) {
//LOG.Error("Problem converting " + stringValue + " to type " + type2.FullName, e);
}
addMethodInfo.Invoke(dictionary, new object[] { newValue1, newValue2 });
addedElements = true;
}
}
// No need to return something that isn't filled!
if (addedElements) {
return dictionary;
} else if (defaultValueFromConfig != null) {
return defaultValueFromConfig;
}
} if (defaultValueFromConfig != null) {
return defaultValueFromConfig;
} else {
if (valueType.IsGenericType && valueType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) {
// We are dealing with a generic type that is nullable
valueType = Nullable.GetUnderlyingType(valueType);
}
object newValue = null;
try {
newValue = ConvertValueToValueType(valueType, propertyValue);
} catch (Exception) {
newValue = null;
if (!defaultUsed) {
try {
newValue = ConvertValueToValueType(valueType, defaultValue);
} catch (Exception) {
//LOG.Error("Problem converting " + propertyValue + " to type " + fieldType.FullName, e2);
}
} else {
//LOG.Error("Problem converting " + propertyValue + " to type " + fieldType.FullName, e1);
}
}
return newValue;
}
}
private static object ConvertValueToValueType(Type valueType, string valueString) {
if (valueString == null) {
return null;
}
if (valueType == typeof(string)) {
return valueString;
}
TypeConverter converter = TypeDescriptor.GetConverter(valueType);
//LOG.Debug("No convertor for " + fieldType.ToString());
if (valueType == typeof(object) && valueString.Length > 0) {
//LOG.Debug("Parsing: " + valueString);
string[] values = valueString.Split(new Char[] { ':' });
//LOG.Debug("Type: " + values[0]);
//LOG.Debug("Value: " + values[1]);
Type fieldTypeForValue = Type.GetType(values[0], true);
//LOG.Debug("Type after GetType: " + fieldTypeForValue);
return ConvertValueToValueType(fieldTypeForValue, values[1]);
} else if (converter != null) {
return converter.ConvertFromInvariantString(valueString);
} else if (valueType.IsEnum) {
if (valueString.Length > 0) {
try {
return Enum.Parse(valueType, valueString);
} catch (ArgumentException ae) {
//LOG.InfoFormat("Couldn't match {0} to {1}, trying case-insentive match", valueString, fieldType);
foreach (Enum enumValue in Enum.GetValues(valueType)) {
if (enumValue.ToString().Equals(valueString, StringComparison.InvariantCultureIgnoreCase)) {
//LOG.Info("Match found...");
return enumValue;
}
}
throw ae;
}
}
}
return null;
}
private static string ConvertValueToString(Type valueType, object valueObject) {
if (valueObject == null) {
// If there is nothing, deliver nothing!
return "";
}
if (valueType == typeof(object)) {
// object to String, this is the hardest
// Format will be "FQTypename[,Assemblyname]:Value"
// Get the type so we can call ourselves recursive
Type objectType = valueObject.GetType();
// Get the value as string
string ourValue = ConvertValueToString(objectType, valueObject);
// Get the valuetype as string
string valueTypeName = objectType.FullName;
// Find the assembly name and only append it if it's not already in the fqtypename (like System.Drawing)
string assemblyName = objectType.Assembly.FullName;
// correct assemblyName, this also has version information etc.
if (assemblyName.StartsWith("Green")) {
assemblyName = assemblyName.Substring(0, assemblyName.IndexOf(','));
}
return String.Format("{0},{1}:{2}", valueTypeName, assemblyName, ourValue);
} else {
TypeConverter converter = TypeDescriptor.GetConverter(valueType);
if (converter != null) {
return converter.ConvertToInvariantString(valueObject);
}
}
// All other types
return valueObject.ToString();
return properties;
}
private static string getSectionName(Type iniSectionType) {
@ -589,101 +316,12 @@ namespace Greenshot.IniFile {
string iniLocation = CreateIniLocation(configName + INI_EXTENSION);
try {
SaveInternally(iniLocation);
} catch (Exception) {
//LOG.Error("A problem occured while writing the configuration file to: " + iniLocation, e);
} catch (Exception ex) {
LOG.Error("A problem occured while writing the configuration file to: " + iniLocation);
LOG.Error(ex);
}
}
public static void SaveIniSectionToWriter(TextWriter writer, IniSection section, bool onlyProperties) {
section.BeforeSave();
Type classType = section.GetType();
Attribute[] classAttributes = Attribute.GetCustomAttributes(classType);
foreach (Attribute attribute in classAttributes) {
if (attribute is IniSectionAttribute) {
IniSectionAttribute iniSectionAttribute = (IniSectionAttribute)attribute;
if (!onlyProperties) {
writer.WriteLine("; {0}", iniSectionAttribute.Description);
}
writer.WriteLine("[{0}]", iniSectionAttribute.Name);
// Iterate over the members and fill them
List<MemberInfo> members = new List<MemberInfo>();
foreach (FieldInfo fieldInfo in classType.GetFields()) {
members.Add(fieldInfo);
}
foreach (PropertyInfo propertyInfo in classType.GetProperties()) {
members.Add(propertyInfo);
}
foreach (MemberInfo member in members) {
if (Attribute.IsDefined(member, typeof(IniPropertyAttribute))) {
IniPropertyAttribute iniPropertyAttribute = (IniPropertyAttribute)member.GetCustomAttributes(typeof(IniPropertyAttribute), false)[0];
object propertyValue;
Type valueType;
if (member is FieldInfo) {
propertyValue = ((FieldInfo)member).GetValue(section);
valueType = ((FieldInfo)member).FieldType;
} else if (member is PropertyInfo) {
propertyValue = ((PropertyInfo)member).GetValue(section, null);
valueType = ((PropertyInfo)member).PropertyType;
} else {
continue;
}
if (propertyValue == null) {
if (iniPropertyAttribute.ExcludeIfNull) {
continue;
}
propertyValue = iniPropertyAttribute.DefaultValue;
valueType = typeof(string);
}
if (!onlyProperties) {
writer.WriteLine("; {0}", iniPropertyAttribute.Description);
}
if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(List<>)) {
Type specificValueType = valueType.GetGenericArguments()[0];
writer.Write("{0}=", iniPropertyAttribute.Name);
int listCount = (int)valueType.GetProperty("Count").GetValue(propertyValue, null);
// Loop though generic list
for (int index = 0; index < listCount; index++) {
object item = valueType.GetMethod("get_Item").Invoke(propertyValue, new object[] { index });
// Now you have an instance of the item in the generic list
if (index < listCount - 1) {
writer.Write("{0}" + iniPropertyAttribute.Separator, ConvertValueToString(specificValueType, item));
} else {
writer.Write("{0}", ConvertValueToString(specificValueType, item));
}
}
writer.WriteLine();
} else if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) {
// Handle dictionaries.
Type valueType1 = valueType.GetGenericArguments()[0];
Type valueType2 = valueType.GetGenericArguments()[1];
// Get the methods we need to deal with dictionaries.
var keys = valueType.GetProperty("Keys").GetValue(propertyValue, null);
var item = valueType.GetProperty("Item");
var enumerator = keys.GetType().GetMethod("GetEnumerator").Invoke(keys, null);
var moveNext = enumerator.GetType().GetMethod("MoveNext");
var current = enumerator.GetType().GetProperty("Current").GetGetMethod();
// Get all the values.
while ((bool)moveNext.Invoke(enumerator, null)) {
var key = current.Invoke(enumerator, null);
var valueObject = item.GetValue(propertyValue, new object[] { key });
// Write to ini file!
writer.WriteLine("{0}.{1}={2}", iniPropertyAttribute.Name, ConvertValueToString(valueType1, key), ConvertValueToString(valueType2, valueObject));
}
} else {
writer.WriteLine("{0}={1}", iniPropertyAttribute.Name, ConvertValueToString(valueType, propertyValue));
}
}
}
}
section.AfterSave();
}
}
private static void SaveInternally(string iniLocation) {
WatchConfigFile(false);
@ -694,7 +332,7 @@ namespace Greenshot.IniFile {
}
TextWriter writer = new StreamWriter(iniLocation, false, Encoding.UTF8);
foreach (IniSection section in sectionMap.Values) {
SaveIniSectionToWriter(writer, section, false);
section.Write(writer, false);
// Add empty line after section
writer.WriteLine();
section.IsDirty = false;