-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathParserMetadata.cs
More file actions
211 lines (182 loc) · 8.09 KB
/
ParserMetadata.cs
File metadata and controls
211 lines (182 loc) · 8.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
namespace CShargs
{
internal class ParserMetadata
{
private Type userType_;
private List<IRule> rules_;
public ParserConfigAttribute Config { get; private set; }
public readonly Dictionary<string, OptionMetadata> OptionsByShort = new();
public readonly Dictionary<string, OptionMetadata> OptionsByLong = new();
public readonly Dictionary<string, OptionMetadata> VerbOptions = new();
public readonly Dictionary<string, OptionMetadata> OptionsByProperty = new();
public readonly Dictionary<OptionMetadata, IEnumerable<OptionMetadata>> Dependants = new();
public ParserMetadata(Type userType)
{
this.userType_ = userType;
}
public void LoadAtrributes()
{
Config = userType_.IsDefined(typeof(ParserConfigAttribute)) ?
userType_.GetCustomAttribute<ParserConfigAttribute>() :
new();
createOptionsMetadata();
injectUseWithReferences();
rules_ = extractRules();
registerAliases();
}
public void CheckRules(ICollection<OptionMetadata> parsedOptions)
{
foreach (var rule in rules_) {
rule.Check(parsedOptions);
}
}
private void createOptionsMetadata()
{
createOptionsMetadataProperties();
createOptionMetadataCustom();
}
private void createOptionsMetadataProperties()
{
var properties = userType_.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var property in properties) {
var attributes = property.GetCustomAttributes().Where(attr => attr is IOptionAttribute).ToArray();
if (attributes.Length > 1) {
throw new ConfigurationException("One property cannot be annotated with multiple attributes.");
}
if (attributes.Length == 1) {
if (property.SetMethod.IsPrivate) {
throw new ConfigurationException($"Option property '{property.Name}' cannot have private setter.");
}
var attrib = (IOptionAttribute)attributes[0];
var option = createOption(property, attrib);
if (option is VerbOption verbOption) {
registerVerbOption(verbOption, option.LongName);
}
else {
registerOptionByLongName(option, attrib.LongName);
if (attrib.ShortName != '\0') {
registerOptionByShortName(option, attrib.ShortName);
}
}
}
}
}
private void createOptionMetadataCustom()
{
var methods = userType_.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var method in methods) {
var attributes = method.GetCustomAttributes().Where(attr => attr is CustomOptionAttribute).ToArray();
if (attributes.Length > 1) {
throw new ConfigurationException("One property cannot be annotated with multiple attributes.");
}
if (attributes.Length == 1) {
var attrib = (CustomOptionAttribute)attributes[0];
var option = createOption(method, attrib);
registerOptionByLongName(option, attrib.LongName);
if (attrib.ShortName != '\0') {
registerOptionByShortName(option, attrib.ShortName);
}
}
}
}
private OptionMetadata createOption(MemberInfo member, IOptionAttribute optionAttribute)
{
var option = optionAttribute.CreateMetadata(this, member);
OptionsByProperty.Add(member.Name, option);
return option;
}
private void registerVerbOption(VerbOption option, string name)
{
Debug.Assert(name != null);
if (!option.Property.PropertyType.IsAssignableTo(typeof(Parser))) {
throw new ConfigurationException($"Verb option '{name}' must be a descendant of {typeof(Parser)}.");
}
if (VerbOptions.ContainsKey(name)) {
throw new ConfigurationException($"Verb option name '{name}' is duplicate.");
}
if (Config.OptionFlags.HasFlag(OptionFlags.LongCaseInsensitive)) {
name = name.ToUpper();
}
VerbOptions.Add(name, option);
}
private void registerOptionByLongName(OptionMetadata option, string longName)
{
Debug.Assert(longName != null);
if (OptionsByLong.ContainsKey(longName)) {
throw new ConfigurationException($"Option name {longName} is duplicate.");
}
if (Config.OptionFlags.HasFlag(OptionFlags.LongCaseInsensitive)) {
longName = longName.ToUpper();
}
OptionsByLong.Add(longName, option);
}
private void registerOptionByShortName(OptionMetadata option, char shortName)
{
Debug.Assert(shortName != '\0');
if (OptionsByShort.ContainsKey(shortName.ToString())) {
throw new ConfigurationException($"Option name {shortName} is duplicate.");
}
if (Config.OptionFlags.HasFlag(OptionFlags.ShortCaseInsensitive)) {
shortName = char.ToUpper(shortName);
}
OptionsByShort.Add(shortName.ToString(), option);
}
private void injectUseWithReferences()
{
foreach (var option in OptionsByProperty.Values) {
if (option.UseWithName != null) {
option.UseWith = OptionsByProperty[option.UseWithName];
}
}
}
private List<IRule> extractRules()
{
List<IRule> result = new();
foreach (var option in OptionsByProperty.Values) {
var rules = option.ExtractRules();
result.AddRange(rules);
}
var groupAttributes = userType_.GetCustomAttributes<OptionGroupAttribute>();
foreach (var groupAttribute in groupAttributes) {
var group = new GroupRule(groupAttribute, OptionsByProperty);
result.Add(group);
}
return result;
}
private void registerAliases()
{
var attributes = userType_.GetCustomAttributes<AliasOptionAttribute>();
foreach (var attribute in attributes) {
var targetOptions = new List<OptionMetadata>();
foreach (string target in attribute.Targets) {
if (OptionsByProperty.TryGetValue(target, out var option)) {
if (attribute.Targets.Count == 1 || option is FlagOption) {
targetOptions.Add(option);
} else {
throw new ConfigurationException(
$"Cant make alias '{attribute.Alias}' with multiple targts for non-flag option '{target}'");
}
} else {
throw new ConfigurationException($"Alias '{attribute.Alias}' for an unknown option '{target}'.");
}
}
var alias = new AliasOption(this, attribute, targetOptions);
if (attribute.LongName != null) {
registerOptionByLongName(alias, attribute.LongName);
}
else {
registerOptionByShortName(alias, attribute.ShortName);
}
}
}
public IEnumerable<GroupRule> GetGroups()
{
return rules_.OfType<GroupRule>();
}
}
}