using System; using System.Collections.Generic; using System.IO; using SabreTools.Library.Data; namespace SabreTools.Library.Help { public class Feature { #region Private instance variables private List _flags; private string _description; private FeatureType _featureType; private Dictionary _features; private List _additionalNotes; private bool _foundOnce = false; // Specific value types private bool _valueBool = false; private string _valueString = null; private List _valueList = null; #endregion #region Constructors public Feature() { _flags = new List(); _description = null; _featureType = FeatureType.Flag; _features = new Dictionary(); _additionalNotes = new List(); } public Feature(string flag, string description, FeatureType featureType, List additionalNotes) { List flags = new List(); flags.Add(flag); _flags = flags; _description = description; _featureType = featureType; _features = new Dictionary(); _additionalNotes = additionalNotes; } public Feature(List flags, string description, FeatureType featureType, List additionalNotes) { _flags = flags; _description = description; _featureType = featureType; _features = new Dictionary(); _additionalNotes = additionalNotes; } #endregion #region Accessors /// /// Directly address a given subfeature /// public Feature this[string name] { get { return _features[name]; } set { _features[name] = value; } } /// /// Add a new feature for this feature /// /// Name of the feature to add /// public void AddFeature(string name, Feature feature) { if (_features == null) { _features = new Dictionary(); } lock(_features) { if (!_features.ContainsKey(name)) { _features.Add(name, feature); } else { _features[name] = feature; } } } /// /// Add a new flag for this feature /// /// Flag to add for this feature public void AddFlag(string flag) { if (_flags == null) { _flags = new List(); } lock (_flags) { _flags.Add(flag); } } /// /// Add a set of new flags for this feature /// /// List of flags to add to this feature public void AddFlags(List flags) { if (_flags == null) { _flags = new List(); } lock (_flags) { _flags.AddRange(flags); } } /// /// Add a new additional note to this feature /// /// Note to add for this feature public void AddNote(string note) { if (_additionalNotes == null) { _additionalNotes = new List(); } lock (_additionalNotes) { _additionalNotes.Add(note); } } /// /// Add a set of new notes for this feature /// /// List of notes to add to this feature public void AddNotes(List notes) { if (_additionalNotes == null) { _additionalNotes = new List(); } lock (_additionalNotes) { _additionalNotes.AddRange(notes); } } /// /// Returns if a flag exists for the current feature /// /// Name of the flag to check /// True if the flag was found, false otherwise public bool ContainsFlag(string name) { bool success = false; // Loop through the flags foreach (string flag in _flags) { if (flag == name) { success = true; break; } else if (flag.TrimStart('-') == name) { success = true; break; } } return success; } /// /// Returns if the feature contains a flag that starts with the given character /// /// Character to check against /// True if the flag was found, false otherwise public bool StartsWith(char c) { bool success = false; // Loop through the flags foreach (string flag in _flags) { if (flag.TrimStart('-').ToLowerInvariant()[0] == c) { success = true; break; } } return success; } #endregion #region Instance Methods /// /// Output this feature only /// /// Positive number representing number of spaces to put in front of the feature /// Positive number representing the column where the description should start public List Output(int pre = 0, int midpoint = 0) { // Create the output list List outputList = new List(); // Build the output string first string output = ""; // Add the pre-space first string prespace = ""; for (int i = 0; i < pre; i++) { prespace += " "; } output += prespace; // Now add all flags output += String.Join(", ", _flags); // If we have a midpoint set, check to see if the string needs padding if (midpoint > 0 && output.Length < midpoint) { while (output.Length < midpoint) { output += " "; } } else { output += " "; } // Append the description output += _description; // Now append it to the list outputList.Add(output); return outputList; } /// /// Output this feature and all subfeatures /// /// Level of indentation for this feature /// Positive number representing number of spaces to put in front of the feature /// Positive number representing the column where the description should start public List OutputRecursive(int tabLevel, int pre = 0, int midpoint = 0) { // Create the output list List outputList = new List(); // Build the output string first string output = ""; // Normalize based on the tab level int preAdjusted = pre; int midpointAdjusted = midpoint; if (tabLevel > 0) { preAdjusted += 4 * tabLevel; midpointAdjusted += 4 * tabLevel; } // Add the pre-space first string prespace = ""; for (int i = 0; i < preAdjusted; i++) { prespace += " "; } output += prespace; // Now add all flags output += String.Join(", ", _flags); // If we have a midpoint set, check to see if the string needs padding if (midpoint > 0 && output.Length < midpointAdjusted) { while (output.Length < midpointAdjusted) { output += " "; } } else { output += " "; } // Append the description output += _description; // Now append it to the list outputList.Add(output); // Now let's append all subfeatures foreach (string feature in _features.Keys) { outputList.AddRange(_features[feature].OutputRecursive(tabLevel + 1, pre, midpoint)); } // Finally, let's append all additional notes if (_additionalNotes != null && _additionalNotes.Count > 0) { foreach (string note in _additionalNotes) { outputList.Add(prespace + note); } } return outputList; } /// /// Validate whether a flag is valid for this feature or not /// /// Input to check against /// True if just this feature should be checked, false if all subfeatures are checked as well /// True if the flag was valid, false otherwise public bool ValidateInput(string input, bool exact = false) { bool valid = false; // Determine what we should be looking for switch (_featureType) { // If we have a flag, make sure it doesn't have an equal sign in it case FeatureType.Flag: valid = !input.Contains("=") && _flags.Contains(input); if (valid) { _valueBool = true; _foundOnce = true; } break; // If we have an input, make sure it has an equals sign in it case FeatureType.List: valid = input.Contains("=") && _flags.Contains(input.Split('=')[0]); if (valid) { if (_valueList == null) { _valueList = new List(); } _valueList.Add(input.Split('=')[1]); } break; case FeatureType.String: valid = input.Contains("=") && _flags.Contains(input.Split('=')[0]); if (valid) { _valueString = input.Split('=')[1]; _foundOnce = true; } break; } // If we haven't found a valid flag and we're not looking for just this feature, check to see if any of the subfeatures are valid if (!valid && !exact) { foreach (string feature in _features.Keys) { valid = _features[feature].ValidateInput(input); // If we've found a valid feature, we break out if (valid) { break; } } } // If we've already found this flag before and we don't allow duplicates, set valid to false if (valid && _foundOnce && _featureType != FeatureType.List) { valid = false; } // If we're not valid at this point, we want to check if this flag is a file or a folder if (!valid) { valid = File.Exists(input) || Directory.Exists(input); } return valid; } /// /// Get the proper value associated with this feature /// /// Value associated with this feature public object GetValue() { switch (_featureType) { case FeatureType.Flag: return _valueBool; case FeatureType.List: return _valueList; case FeatureType.String: return _valueString; } return null; } /// /// Returns if this feature has a valid value or not /// /// True if the feature is enabled, false otherwise public bool IsEnabled() { object obj = GetValue(); switch (_featureType) { case FeatureType.Flag: return (bool)obj; case FeatureType.List: return obj != null; case FeatureType.String: return obj != null; } return false; } #endregion } }