IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
ImportOptionsBinding.cs
Go to the documentation of this file.
1#nullable enable
2
3using System;
4using System.Collections.Generic;
5using System.Collections.Immutable;
6using System.CommandLine;
7using System.CommandLine.Binding;
8using System.CommandLine.Builder;
9using System.CommandLine.Parsing;
10using System.ComponentModel;
11using System.IO;
12using System.Linq;
13
16
17namespace IKVM.Tools.Importer
18{
19
20 class ImportOptionsBinding : BinderBase<ImportOptions>
21 {
22
23 readonly static char[] LIST_SEPARATOR = [',', ';'];
24 readonly static char[] KVP_SEPARATOR = ['='];
25
26 readonly ImportArgLevel[] _levels;
27 readonly ImportOptions _parent;
28
33 {
34 _levels = nested ?? throw new ArgumentNullException(nameof(nested));
35 _parent = parent;
36 }
37
39 protected override ImportOptions GetBoundValue(BindingContext context)
40 {
41 var command = (ImportCommand)context.ParseResult.CommandResult.Command;
42
43 var options = _parent?.Clone() ?? new ImportOptions();
44
45 if (context.ParseResult.GetValueForArgument(command.InputsArgument) is FileInfo[] _inputs)
46 options.Inputs = AppendArray(options.Inputs, _inputs);
47
48 if (context.ParseResult.GetValueForOption(command.RecurseOption) is string[] _recurse)
49 options.Recurse = AppendArray(options.Recurse, _recurse);
50
51 if (context.ParseResult.GetValueForOption(command.AssemblyAttributesOption) is FileInfo[] _assemblyAttributes)
52 options.AssemblyAttributes = AppendArray(options.AssemblyAttributes, _assemblyAttributes);
53
54 if (GetParsedDictionaryValueForOption<string, FileInfo>(context, command.ResourceOption, null) is IReadOnlyDictionary<string, FileInfo> _resources)
55 options.Resources = AppendDictionaries(options.Resources, _resources);
56
57 if (GetParsedDictionaryValueForOption<string, FileInfo>(context, command.ExternalResourceOption, null) is IReadOnlyDictionary<string, FileInfo> _externalresources)
58 options.ExternalResources = AppendDictionaries(options.ExternalResources, _externalresources);
59
60 if (context.ParseResult.GetValueForOption(command.OutputOption) is FileInfo _output)
61 options.Output = _output;
62
63 if (context.ParseResult.GetValueForOption(command.AssemblyNameOption) is string _assemblyName)
64 options.AssemblyName = _assemblyName;
65
66 if (GetParsedEnumValueForOption(context, command.TargetOption, ImportTarget.Unspecified) is ImportTarget _target and not ImportTarget.Unspecified)
67 options.Target = _target;
68
69 if (GetParsedEnumValueForOption(context, command.PlatformOption, ImportPlatform.Unspecified) is ImportPlatform _platform and not ImportPlatform.Unspecified)
70 options.Platform = _platform;
71
72 if (GetParsedEnumValueForOption(context, command.ApartmentOption, ImportApartment.Unspecified) is ImportApartment _apartment and not ImportApartment.Unspecified)
73 options.Apartment = _apartment;
74
75 if (context.ParseResult.GetValueForOption(command.NoGlobbingOption) is true)
76 options.NoGlobbing = true;
77
78 if (GetParsedValueForOption<string[]?>(context, command.EnableAssertionsOption, null) is string[] _ea)
79 options.EnableAssertions = _ea;
80
81 if (GetParsedValueForOption<string[]?>(context, command.DisableAssertionsOption, null) is string[] _da)
82 options.DisableAssertions = _da;
83
84 if (GetParsedDictionaryValueForOption<string, string>(context, command.PropertiesOption, "") is IReadOnlyDictionary<string, string> _properties)
85 options.Properties = AppendDictionaries(options.Properties, _properties);
86
87 if (context.ParseResult.GetValueForOption(command.RemoveAssertionsOption) is true)
88 options.RemoveAssertions = true;
89
90 if (context.ParseResult.GetValueForOption(command.MainClassOption) is string _main)
91 options.Main = _main;
92
93 if (context.ParseResult.GetValueForOption(command.ReferenceOption) is string[] _references)
94 options.References = AppendArray(options.References, _references);
95
96 if (context.ParseResult.GetValueForOption(command.NoJNIOption) is true)
97 options.NoJNI = true;
98
99 if (context.ParseResult.GetValueForOption(command.ExcludeOption) is FileInfo _exclude)
100 options.Exclude = _exclude;
101
102 if (GetParsedValueForOption<Version?>(context, command.VersionOption, null) is Version _version)
103 options.Version = _version;
104
105 if (GetParsedValueForOption<Version?>(context, command.FileVersionOption, null) is Version _fileversion)
106 options.FileVersion = _fileversion;
107
108 if (context.ParseResult.GetValueForOption(command.Win32IconOption) is FileInfo _win32icon)
109 options.Win32Icon = _win32icon;
110
111 if (context.ParseResult.GetValueForOption(command.Win32ManifestOption) is FileInfo _win32manifest)
112 options.Win32Manifest = _win32manifest;
113
114 if (context.ParseResult.GetValueForOption(command.KeyFileOption) is FileInfo _keyfile)
115 options.KeyFile = _keyfile;
116
117 if (context.ParseResult.GetValueForOption(command.KeyOption) is string _keyoption)
118 options.Key = _keyoption;
119
120 if (context.ParseResult.GetValueForOption(command.DelaySignOption) is true)
121 options.DelaySign = true;
122
123 if (GetParsedEnumValueForOption(context, command.DebugOption, ImportDebug.None) is ImportDebug _debug and not ImportDebug.None)
124 options.Debug = _debug;
125
126 if (context.ParseResult.GetValueForOption(command.DeterministicOption) is false)
127 options.Deterministic = false;
128
129 if (context.ParseResult.GetValueForOption(command.OptimizeOption) is true)
130 options.Optimize = true;
131
132 if (context.ParseResult.GetValueForOption(command.SourcePathOption) is DirectoryInfo _sourcePath)
133 options.SourcePath = _sourcePath;
134
135 if (context.ParseResult.GetValueForOption(command.RemapOption) is FileInfo _remap)
136 options.Remap = _remap;
137
138 if (context.ParseResult.GetValueForOption(command.NoStackTraceInfoOption) is true)
139 options.NoStackTraceInfo = true;
140
141 if (context.ParseResult.GetValueForOption(command.RemoveUnusedPrivateFieldsOption) is true)
142 options.RemoveUnusedPrivateFields = true;
143
144 if (context.ParseResult.GetValueForOption(command.CompressResourcesOption) is true)
145 options.CompressResources = true;
146
147 if (context.ParseResult.GetValueForOption(command.StrictFinalFieldSemanticsOption) is true)
148 options.StrictFinalFieldSemantics = true;
149
150 if (context.ParseResult.GetValueForOption(command.PrivatePackageOption) is string[] _privatepackages)
151 options.PrivatePackages = AppendArray(options.PrivatePackages, _privatepackages);
152
153 if (context.ParseResult.GetValueForOption(command.PublicPackageOption) is string[] _publicpackages)
154 options.PublicPackages = AppendArray(options.PublicPackages, _publicpackages);
155
156 if (context.ParseResult.GetValueForOption(command.NoWarnOption) is Diagnostic[] _nowarn)
157 options.NoWarn = options.NoWarn != null ? AppendArray(options.NoWarn, _nowarn) : _nowarn;
158
159 if (context.ParseResult.GetValueForOption(command.WarnAsErrorOption) is Diagnostic[] _warnaserror)
160 options.WarnAsError = options.WarnAsError != null ? AppendArray(options.WarnAsError, _warnaserror) : _warnaserror;
161
162 if (command.RuntimeOption != null)
163 if (context.ParseResult.GetValueForOption(command.RuntimeOption) is FileInfo _runtime)
164 options.Runtime = _runtime;
165
166 if (context.ParseResult.GetValueForOption(command.ClassLoaderOption) is string _classloader)
167 options.ClassLoader = _classloader;
168
169 if (command.SharedClassLoaderOption != null)
170 if (context.ParseResult.GetValueForOption(command.SharedClassLoaderOption) is true)
171 options.SharedClassLoader = true;
172
173 if (context.ParseResult.GetValueForOption(command.BaseAddressOption) is string _baseaddress)
174 options.BaseAddress = _baseaddress;
175
176 if (context.ParseResult.GetValueForOption(command.FileAlignOption) is string _filealign)
177 options.FileAlign = _filealign;
178
179 if (context.ParseResult.GetValueForOption(command.NoPeerCrossReferenceOption) is true)
180 options.NoPeerCrossReference = true;
181
182 if (context.ParseResult.GetValueForOption(command.NoStdLibOption) is true)
183 options.NoStdLib = true;
184
185 if (context.ParseResult.GetValueForOption(command.LibraryOption) is DirectoryInfo[] _libs)
186 options.Libraries = AppendArray(options.Libraries, _libs);
187
188 if (context.ParseResult.GetValueForOption(command.NoAutoSerializationOption) is true)
189 options.NoAutoSerialization = true;
190
191 if (context.ParseResult.GetValueForOption(command.HighEntropyVAOption) is true)
192 options.HighEntropyVA = true;
193
194 if (context.ParseResult.GetValueForOption(command.ProxyOption) is string[] _proxies)
195 options.Proxies = _proxies;
196
197 if (context.ParseResult.GetValueForOption(command.AllowNonVirtualCallsOption) is true)
198 options.AllowNonVirtualCalls = true;
199
200 if (context.ParseResult.GetValueForOption(command.StaticOption) is true)
201 options.Static = true;
202
203 if (context.ParseResult.GetValueForOption(command.NoJarStubsOption) is true)
204 options.NoJarStubs = true;
205
206 if (context.ParseResult.GetValueForOption(command.WarningLevel4Option) is true)
207 options.WarningLevel4Option = true;
208
209 if (context.ParseResult.GetValueForOption(command.NoParameterReflectionOption) is true)
210 options.NoParameterReflection = true;
211
212 if (command.BootstrapOption != null)
213 if (context.ParseResult.GetValueForOption(command.BootstrapOption) is true)
214 options.Bootstrap = true;
215
216 if (command.LogOption != null)
217 if (context.ParseResult.GetValueForOption(command.LogOption) is string _log)
218 options.Log = _log;
219
220 // for each nested level, run a command to capture the options, but otherwise does nothing
221 foreach (var level in _levels)
222 {
223 var nestedCommand = new ImportCommand(false);
224 var nestedOptions = new List<ImportOptions>(level.Args.Count);
225 nestedCommand.SetHandler(nestedOptions.Add, new ImportOptionsBinding(level.Nested.ToArray(), options));
226 new CommandLineBuilder(nestedCommand).Build().Invoke(level.Args.ToArray());
227 options.Nested = nestedOptions.ToArray();
228 }
229
230 return options;
231 }
232
240 T[] AppendArray<T>(T[] a, T[] b)
241 {
242 var tmp = new T[a.Length + b.Length];
243 a.CopyTo(tmp, 0);
244 b.CopyTo(tmp, a.Length);
245 return tmp;
246 }
247
256 IReadOnlyDictionary<TKey, TValue> AppendDictionaries<TKey, TValue>(IReadOnlyDictionary<TKey, TValue> first, IReadOnlyDictionary<TKey, TValue> second)
257 where TKey : notnull
258 {
259 var d = new Dictionary<TKey, TValue>();
260
261 foreach (var kvp in first)
262 d.Add(kvp.Key, kvp.Value);
263
264 foreach (var kvp in second)
265 d[kvp.Key] = kvp.Value;
266
267 return d;
268 }
269
278 T GetParsedValueForOption<T>(BindingContext context, Option<string?> option, T defaultValue)
279 {
280 return context.ParseResult.GetValueForOption(option) is string v ? ParseStringValue<T>(v) ?? defaultValue : defaultValue;
281 }
282
290 string[]? ParseStringToStringArrayForOption(BindingContext context, Option<string?> option, char[] separator, string[]? defaultValue)
291 {
292 return context.ParseResult.GetValueForOption(option) is string v ? v.Split(separator, StringSplitOptions.RemoveEmptyEntries) : defaultValue;
293 }
294
302 int[]? ParseDiagnosticIdListForOption(BindingContext context, Option<string?> option, char[] separator, int[]? defaultValue)
303 {
304 if (context.ParseResult.GetValueForOption(option) is string v)
305 return (int[]?)v.Split(separator, StringSplitOptions.RemoveEmptyEntries).Select(SafeParseDiagnosticId).Where(i => i != null).OfType<int>().ToArray();
306 else
307 return defaultValue;
308 }
309
315 int? SafeParseDiagnosticId(string value)
316 {
317 if (int.TryParse(value, out var i))
318 return i;
319
320 if (value.StartsWith("IKVM", StringComparison.OrdinalIgnoreCase))
321 return SafeParseDiagnosticId(value["IKVM".Length..]);
322
323 return null;
324 }
325
334 T GetParsedEnumValueForOption<T>(BindingContext context, Option<string?> option, T defaultValue)
335 where T : notnull, Enum
336 {
337 return context.ParseResult.GetValueForOption(option) is string v ? (T)Enum.Parse(typeof(T), v, true) : defaultValue;
338 }
339
349 IReadOnlyDictionary<TKey, TValue> GetParsedDictionaryValueForOption<TKey, TValue>(BindingContext context, Option<string[]> option, TValue? defaultValue)
350 where TKey : notnull
351 {
352 return context.ParseResult.GetValueForOption(option) is string[] v ? ParseDictionaryValues<TKey, TValue>(context, option, v, defaultValue) : ImmutableDictionary<TKey, TValue>.Empty;
353 }
354
364 IReadOnlyDictionary<TKey, TValue> ParseDictionaryValues<TKey, TValue>(BindingContext context, Option option, string[] values, TValue? defaultValue)
365 where TKey : notnull
366 {
367 var dict = new Dictionary<TKey, TValue>();
368
369 foreach (var kvp in values.Select(i => ParseDictionaryValue<TKey, TValue>(context, i)))
370 {
371 if (dict.ContainsKey(kvp.Key))
372 throw new ToolErrorException($"Option name '{option.Name}' contains multiple values for key '{kvp.Key}'.");
373
374 dict[kvp.Key] = kvp.Value ?? defaultValue ?? throw new InvalidOperationException();
375 }
376
377 return dict;
378 }
379
388 KeyValuePair<TKey, TValue?> ParseDictionaryValue<TKey, TValue>(BindingContext context, string opt)
389 where TKey : notnull
390 {
391 var c = opt.Split(KVP_SEPARATOR, 2, StringSplitOptions.None);
392
393 // parse key value
394 var k = ParseStringValue<TKey>(c[0].Trim());
395 if (k == null)
396 throw new InvalidOperationException();
397
398 if (c.Length == 2)
399 return new KeyValuePair<TKey, TValue?>(k, ParseStringValue<TValue>(c[1]));
400
401 if (c.Length == 1)
402 return new KeyValuePair<TKey, TValue?>(k, default);
403
404 throw new InvalidOperationException();
405 }
406
413 T? ParseStringValue<T>(string source)
414 {
415 if (source is T t)
416 return t;
417
418 if (typeof(T) == typeof(FileInfo))
419 return string.IsNullOrWhiteSpace(source) == false ? (T)(object)new FileInfo(source) : default;
420
421 if (typeof(T) == typeof(DirectoryInfo))
422 return string.IsNullOrWhiteSpace(source) == false ? (T)(object)new DirectoryInfo(source) : default;
423
424 if (typeof(T) == typeof(Version))
425 return (T)(object)Version.Parse(source);
426
427 if (typeof(T) == typeof(string[]))
428 return (T)(object)source.Split(LIST_SEPARATOR, StringSplitOptions.RemoveEmptyEntries);
429
430 // fall back to type converter
431 if (TypeDescriptor.GetConverter(typeof(T)) is TypeConverter c && c.CanConvertFrom(typeof(string)))
432 return (T?)c.ConvertFromString(source);
433
434 throw new InvalidOperationException();
435 }
436
437 }
438
439}
override ImportOptions GetBoundValue(BindingContext context)
ImportOptionsBinding(ImportArgLevel[] nested, ImportOptions parent)
Initializes a new instance.