BadScript 2
Loading...
Searching...
No Matches
InstanceBuilder.cs
Go to the documentation of this file.
1// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.
2
3using System;
4using System.Collections.Generic;
5using System.Globalization;
6using System.Linq;
7using System.Reflection;
8
10
11using CSharpx;
12
14
15namespace CommandLine.Core
16{
17 internal static class InstanceBuilder
18 {
19 public static ParserResult<T> Build<T>(Maybe<Func<T>> factory,
20 Func<IEnumerable<string>, IEnumerable<OptionSpecification>,
21 Result<IEnumerable<Token>, Error>> tokenizer,
22 IEnumerable<string> arguments,
23 StringComparer nameComparer,
24 bool ignoreValueCase,
25 CultureInfo parsingCulture,
26 bool autoHelp,
27 bool autoVersion,
28 IEnumerable<ErrorType> nonFatalErrors)
29 {
30 return Build(factory,
31 tokenizer,
32 arguments,
33 nameComparer,
34 ignoreValueCase,
35 parsingCulture,
36 autoHelp,
37 autoVersion,
38 false,
39 nonFatalErrors
40 );
41 }
42
43 public static ParserResult<T> Build<T>(Maybe<Func<T>> factory,
44 Func<IEnumerable<string>, IEnumerable<OptionSpecification>,
45 Result<IEnumerable<Token>, Error>> tokenizer,
46 IEnumerable<string> arguments,
47 StringComparer nameComparer,
48 bool ignoreValueCase,
49 CultureInfo parsingCulture,
50 bool autoHelp,
51 bool autoVersion,
52 bool allowMultiInstance,
53 IEnumerable<ErrorType> nonFatalErrors)
54 {
55 Type typeInfo = factory.MapValueOrDefault(f => f()
56 .GetType(),
57 typeof(T)
58 );
59
60 IEnumerable<SpecificationProperty> specProps = typeInfo
61 .GetSpecifications(pi =>
63 .Create(Specification
64 .FromProperty(pi),
65 pi,
66 Maybe.Nothing<object>()
67 )
68 )
69 .Memoize();
70
71 IEnumerable<Specification> specs = from pt in specProps select pt.Specification;
72
73 IEnumerable<OptionSpecification> optionSpecs = specs
74 .ThrowingValidate(SpecificationGuards.Lookup)
75 .OfType<OptionSpecification>()
76 .Memoize();
77
78 Func<T> makeDefault = () =>
79 typeof(T).IsMutable()
80 ? factory.MapValueOrDefault(f => f(), () => Activator.CreateInstance<T>())
81 : ReflectionHelper.CreateDefaultImmutableInstance<T>((from p in specProps
82 select p.Specification.ConversionType)
83 .ToArray()
84 );
85
86 Func<IEnumerable<Error>, ParserResult<T>> notParsed =
87 errs => new NotParsed<T>(makeDefault()
88 .GetType()
89 .ToTypeInfo(),
90 errs
91 );
92
93 IEnumerable<string> argumentsList = arguments.Memoize();
94
95 Func<ParserResult<T>> buildUp = () =>
96 {
97 Result<IEnumerable<Token>, Error> tokenizerResult = tokenizer(argumentsList, optionSpecs);
98
99 IEnumerable<Token> tokens = tokenizerResult.SucceededWith()
100 .Memoize();
101
102 Tuple<IEnumerable<KeyValuePair<string, IEnumerable<string>>>, IEnumerable<string>, IEnumerable<Token>>
103 partitions = TokenPartitioner.Partition(tokens,
104 name =>
106 optionSpecs,
107 nameComparer
108 )
109 );
110 IEnumerable<KeyValuePair<string, IEnumerable<string>>> optionsPartition = partitions.Item1.Memoize();
111 IEnumerable<string> valuesPartition = partitions.Item2.Memoize();
112 IEnumerable<Token> errorsPartition = partitions.Item3.Memoize();
113
114 Result<IEnumerable<SpecificationProperty>, Error> optionSpecPropsResult =
115 OptionMapper.MapValues(from pt in specProps where pt.Specification.IsOption() select pt,
116 optionsPartition,
117 (vals, type, isScalar, isFlag) =>
119 type,
120 isScalar,
121 isFlag,
122 parsingCulture,
123 ignoreValueCase
124 ),
125 nameComparer
126 );
127
128 Result<IEnumerable<SpecificationProperty>, Error> valueSpecPropsResult =
129 ValueMapper.MapValues(from pt in specProps
130 where pt.Specification.IsValue()
131 orderby ((ValueSpecification)pt.Specification).Index
132 select pt,
133 valuesPartition,
134 (vals, type, isScalar) =>
136 type,
137 isScalar,
138 false,
139 parsingCulture,
140 ignoreValueCase
141 )
142 );
143
144 IEnumerable<MissingValueOptionError> missingValueErrors = from token in errorsPartition
145 select
146 new MissingValueOptionError(optionSpecs
147 .Single(o =>
148 token.Text
149 .MatchName(o
150 .ShortName,
151 o.LongName,
152 nameComparer
153 )
154 )
155 .FromOptionSpecification()
156 );
157
158 IEnumerable<SpecificationProperty> specPropsWithValue =
159 optionSpecPropsResult.SucceededWith()
160 .Concat(valueSpecPropsResult.SucceededWith())
161 .Memoize();
162
163 List<Error> setPropertyErrors = new List<Error>();
164
165 //build the instance, determining if the type is mutable or not.
166 T instance;
167
168 if (typeInfo.IsMutable())
169 {
170 instance = BuildMutable(factory, specPropsWithValue, setPropertyErrors);
171 }
172 else
173 {
174 instance = BuildImmutable(typeInfo, factory, specProps, specPropsWithValue, setPropertyErrors);
175 }
176
177 IEnumerable<Error> validationErrors =
178 specPropsWithValue.Validate(SpecificationPropertyRules.Lookup(tokens, allowMultiInstance));
179
180 IEnumerable<Error> allErrors =
181 tokenizerResult.SuccessMessages()
182 .Concat(missingValueErrors)
183 .Concat(optionSpecPropsResult.SuccessMessages())
184 .Concat(valueSpecPropsResult.SuccessMessages())
185 .Concat(validationErrors)
186 .Concat(setPropertyErrors)
187 .Memoize();
188
189 IEnumerable<Error> warnings = from e in allErrors where nonFatalErrors.Contains(e.Tag) select e;
190
191 return allErrors.Except(warnings)
192 .ToParserResult(instance);
193 };
194
195 IEnumerable<Error> preprocessorErrors = (
196 argumentsList.Any()
197 ? arguments.Preprocess(PreprocessorGuards
198 .Lookup(nameComparer, autoHelp, autoVersion)
199 )
200 : Enumerable.Empty<Error>()
201 ).Memoize();
202
203 ParserResult<T> result = argumentsList.Any()
204 ? preprocessorErrors.Any()
205 ? notParsed(preprocessorErrors)
206 : buildUp()
207 : buildUp();
208
209 return result;
210 }
211
212 private static T BuildMutable<T>(Maybe<Func<T>> factory,
213 IEnumerable<SpecificationProperty> specPropsWithValue,
214 List<Error> setPropertyErrors)
215 {
216 T mutable = factory.MapValueOrDefault(f => f(), () => Activator.CreateInstance<T>());
217
218 setPropertyErrors.AddRange(mutable.SetProperties(specPropsWithValue,
219 sp => sp.Value.IsJust(),
220 sp => sp.Value.FromJustOrFail()
221 )
222 );
223
224 setPropertyErrors.AddRange(mutable.SetProperties(specPropsWithValue,
225 sp => sp.Value.IsNothing() &&
226 sp.Specification.DefaultValue.IsJust(),
227 sp => sp.Specification.DefaultValue.FromJustOrFail()
228 )
229 );
230
231 setPropertyErrors.AddRange(mutable.SetProperties(specPropsWithValue,
232 sp => sp.Value.IsNothing() &&
233 sp.Specification.TargetType == TargetType.Sequence &&
234 sp.Specification.DefaultValue.MatchNothing(),
235 sp => sp.Property.PropertyType.GetTypeInfo()
236 .GetGenericArguments()
237 .Single()
238 .CreateEmptyArray()
239 )
240 );
241
242 return mutable;
243 }
244
245 private static T BuildImmutable<T>(Type typeInfo,
246 Maybe<Func<T>> factory,
247 IEnumerable<SpecificationProperty> specProps,
248 IEnumerable<SpecificationProperty> specPropsWithValue,
249 List<Error> setPropertyErrors)
250 {
251 ConstructorInfo ctor = typeInfo.GetTypeInfo()
252 .GetConstructor(specProps.Select(sp => sp.Property.PropertyType)
253 .ToArray()
254 );
255
256 if (ctor == null)
257 {
258 throw new
259 InvalidOperationException($"Type {typeInfo.FullName} appears to be immutable, but no constructor found to accept values."
260 );
261 }
262
263 try
264 {
265 object[] values =
266 (from prms in ctor.GetParameters()
267 join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToLower() into spv
268 from sp in spv.DefaultIfEmpty()
269 select
270 sp == null
271 ? specProps.First(s => string.Equals(s.Property.Name,
272 prms.Name,
273 StringComparison.CurrentCultureIgnoreCase
274 )
275 )
276 .Property.PropertyType.GetDefaultValue()
277 : sp.Value.GetValueOrDefault(sp.Specification.DefaultValue
278 .GetValueOrDefault(sp.Specification.ConversionType
279 .CreateDefaultForImmutable()
280 )
281 )).ToArray();
282
283 T immutable = (T)ctor.Invoke(values);
284
285 return immutable;
286 }
287 catch (Exception)
288 {
289 string[] ctorArgs = specPropsWithValue
290 .Select(x => x.Property.Name.ToLowerInvariant())
291 .ToArray();
292
293 throw GetException(ctorArgs);
294 }
295
296 Exception GetException(string[] s)
297 {
298 string ctorSyntax = s != null
299 ? " Constructor Parameters can be ordered as: " + $"'({string.Join(", ", s)})'"
300 : string.Empty;
301
302 string msg =
303 $"Type {typeInfo.FullName} appears to be Immutable with invalid constructor. Check that constructor arguments have the same name and order of their underlying Type. {ctorSyntax}";
304 InvalidOperationException invalidOperationException = new InvalidOperationException(msg);
305
306 return invalidOperationException;
307 }
308 }
309 }
310}
The Maybe type models an optional value. A value of type Maybe a either contains a value of type a (r...
Definition Maybe.cs:33
static ParserResult< T > Build< T >(Maybe< Func< T > > factory, Func< IEnumerable< string >, IEnumerable< OptionSpecification >, Result< IEnumerable< Token >, Error > > tokenizer, IEnumerable< string > arguments, StringComparer nameComparer, bool ignoreValueCase, CultureInfo parsingCulture, bool autoHelp, bool autoVersion, IEnumerable< ErrorType > nonFatalErrors)
static T BuildMutable< T >(Maybe< Func< T > > factory, IEnumerable< SpecificationProperty > specPropsWithValue, List< Error > setPropertyErrors)
static T BuildImmutable< T >(Type typeInfo, Maybe< Func< T > > factory, IEnumerable< SpecificationProperty > specProps, IEnumerable< SpecificationProperty > specPropsWithValue, List< Error > setPropertyErrors)
static Result< IEnumerable< SpecificationProperty >, Error > MapValues(IEnumerable< SpecificationProperty > propertyTuples, IEnumerable< KeyValuePair< string, IEnumerable< string > > > options, Func< IEnumerable< string >, Type, bool, bool, Maybe< object > > converter, StringComparer comparer)
static readonly IEnumerable< Tuple< Func< Specification, bool >, string > > Lookup
static IEnumerable< Func< IEnumerable< SpecificationProperty >, IEnumerable< Error > > > Lookup(IEnumerable< Token > tokens)
static Tuple< IEnumerable< KeyValuePair< string, IEnumerable< string > > >, IEnumerable< string >, IEnumerable< Token > > Partition(IEnumerable< Token > tokens, Func< string, Maybe< TypeDescriptor > > typeLookup)
static Maybe< object > ChangeType(IEnumerable< string > values, Type conversionType, bool scalar, bool isFlag, CultureInfo conversionCulture, bool ignoreValueCase)
static Maybe< TypeDescriptor > FindTypeDescriptorAndSibling(string name, IEnumerable< OptionSpecification > specifications, StringComparer comparer)
Definition TypeLookup.cs:13
static Result< IEnumerable< SpecificationProperty >, Error > MapValues(IEnumerable< SpecificationProperty > specProps, IEnumerable< string > values, Func< IEnumerable< string >, Type, bool, Maybe< object > > converter)
Base type of all errors.
Definition Error.cs:110
static object CreateDefaultImmutableInstance(Type type, Type[] constructorTypes)
Models an error generated when an option lacks its value.
Definition Error.cs:374
It contains a sequence of CommandLine.Error.
Models a parser result. When inherited by CommandLine.Parsed<T>, it contains an instance of type T w...
Represents the result of a computation.