line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Command::V2; # additional methods to dispatch from a command-line |
2
|
9
|
|
|
9
|
|
195
|
use strict; |
|
9
|
|
|
|
|
10
|
|
|
9
|
|
|
|
|
230
|
|
3
|
9
|
|
|
9
|
|
32
|
use warnings; |
|
9
|
|
|
|
|
11
|
|
|
9
|
|
|
|
|
224
|
|
4
|
|
|
|
|
|
|
|
5
|
9
|
|
|
9
|
|
33
|
use IO::File; |
|
9
|
|
|
|
|
10
|
|
|
9
|
|
|
|
|
1316
|
|
6
|
9
|
|
|
9
|
|
40
|
use List::MoreUtils; |
|
9
|
|
|
|
|
12
|
|
|
9
|
|
|
|
|
121
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# instead of tacking these methods onto general Command::V2 objects |
9
|
|
|
|
|
|
|
# they could be put on the Command::Shell class, which is a wrapper/adaptor Command for translating from |
10
|
|
|
|
|
|
|
# command-line shell to purely functional commands. |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# old entry point |
13
|
|
|
|
|
|
|
# new cmds will call Command::Shell->run("MyClass",@ARGV) |
14
|
|
|
|
|
|
|
# which goes straight into _cmdline_run for now... |
15
|
|
|
|
|
|
|
sub execute_with_shell_params_and_exit { |
16
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
17
|
0
|
0
|
|
|
|
0
|
if (@_) { |
18
|
0
|
|
|
|
|
0
|
die "No params expected for execute_with_shell_params_and_exit()!"; |
19
|
|
|
|
|
|
|
} |
20
|
0
|
|
|
|
|
0
|
my @argv = @ARGV; |
21
|
0
|
|
|
|
|
0
|
@ARGV = (); |
22
|
0
|
|
|
|
|
0
|
my $exit_code = $class->_cmdline_run(@argv); |
23
|
0
|
|
|
|
|
0
|
exit $exit_code; |
24
|
|
|
|
|
|
|
} |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub _cmdline_run { |
27
|
|
|
|
|
|
|
# This automatically parses command-line options and "does the right thing": |
28
|
|
|
|
|
|
|
# TODO: abstract out all dispatchers for commands into a given API |
29
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
30
|
0
|
|
|
|
|
0
|
my @argv = @_; |
31
|
|
|
|
|
|
|
|
32
|
0
|
|
0
|
|
|
0
|
$Command::entry_point_class ||= $class; |
33
|
0
|
|
0
|
|
|
0
|
$Command::entry_point_bin ||= File::Basename::basename($0); |
34
|
|
|
|
|
|
|
|
35
|
0
|
0
|
|
|
|
0
|
if ($ENV{COMP_CWORD}) { |
36
|
0
|
|
|
|
|
0
|
require Getopt::Complete; |
37
|
0
|
|
|
|
|
0
|
my @spec = $class->resolve_option_completion_spec(); |
38
|
0
|
|
|
|
|
0
|
my $options = Getopt::Complete::Options->new(@spec); |
39
|
0
|
|
|
|
|
0
|
$options->handle_shell_completion; |
40
|
0
|
|
|
|
|
0
|
die "error: failed to exit after handling shell completion!"; |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
0
|
|
|
|
|
0
|
my $exit_code; |
44
|
0
|
|
|
|
|
0
|
eval { |
45
|
0
|
|
|
|
|
0
|
$exit_code = $class->_execute_with_shell_params_and_return_exit_code(@argv); |
46
|
0
|
0
|
|
|
|
0
|
UR::Context->commit or die "Failed to commit!: " . UR::Context->error_message(); |
47
|
|
|
|
|
|
|
}; |
48
|
0
|
0
|
|
|
|
0
|
if ($@) { |
49
|
0
|
|
|
|
|
0
|
$class->error_message($@); |
50
|
0
|
0
|
|
|
|
0
|
UR::Context->rollback or die "Failed to rollback changes after failed commit!!!\n"; |
51
|
0
|
0
|
|
|
|
0
|
$exit_code = 255 unless ($exit_code); |
52
|
|
|
|
|
|
|
} |
53
|
0
|
|
|
|
|
0
|
return $exit_code; |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
sub _execute_with_shell_params_and_return_exit_code { |
57
|
1
|
|
|
1
|
|
1047
|
my $class = shift; |
58
|
1
|
|
|
|
|
3
|
my @argv = @_; |
59
|
|
|
|
|
|
|
|
60
|
1
|
|
|
|
|
3
|
my $original_cmdline = join("\0",$0,@argv); |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# make --foo=bar equivalent to --foo bar |
63
|
1
|
0
|
|
|
|
3
|
@argv = map { ($_ =~ /^(--\w+?)\=(.*)/) ? ($1,$2) : ($_) } @argv; |
|
0
|
|
|
|
|
0
|
|
64
|
1
|
|
|
|
|
8
|
my ($delegate_class, $params, $errors) = $class->resolve_class_and_params_for_argv(@argv); |
65
|
|
|
|
|
|
|
|
66
|
1
|
|
|
|
|
1
|
my $exit_code; |
67
|
1
|
50
|
33
|
|
|
5
|
if ($errors and @$errors) { |
68
|
0
|
|
|
|
|
0
|
$delegate_class->dump_status_messages(1); |
69
|
0
|
|
|
|
|
0
|
$delegate_class->dump_warning_messages(1); |
70
|
0
|
|
|
|
|
0
|
$delegate_class->dump_error_messages(1); |
71
|
0
|
|
|
|
|
0
|
for my $error (@$errors) { |
72
|
0
|
|
|
|
|
0
|
$delegate_class->error_message(join(' ', $error->property_names) . ": " . $error->desc); |
73
|
|
|
|
|
|
|
} |
74
|
0
|
|
|
|
|
0
|
$exit_code = 1; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
else { |
77
|
1
|
|
|
|
|
7
|
my $rv = $class->_execute_delegate_class_with_params($delegate_class,$params,$original_cmdline); |
78
|
1
|
|
|
|
|
9
|
$exit_code = $delegate_class->exit_code_for_return_value($rv); |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
1
|
|
|
|
|
3
|
return $exit_code; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub _execute_delegate_class_with_params { |
86
|
|
|
|
|
|
|
# this is called by both the shell dispatcher and http dispatcher for now |
87
|
3
|
|
|
3
|
|
1433
|
my ($class, $delegate_class, $params, $original_cmdline) = @_; |
88
|
|
|
|
|
|
|
|
89
|
3
|
50
|
|
|
|
10
|
unless ($delegate_class) { |
90
|
0
|
|
|
|
|
0
|
$class->dump_status_messages(1); |
91
|
0
|
|
|
|
|
0
|
$class->dump_warning_messages(1); |
92
|
0
|
|
|
|
|
0
|
$class->dump_error_messages(1); |
93
|
0
|
|
|
|
|
0
|
$class->dump_usage_messages(1); |
94
|
0
|
|
|
|
|
0
|
$class->dump_debug_messages(0); |
95
|
0
|
|
|
|
|
0
|
$class->usage_message($class->help_usage_complete_text); |
96
|
0
|
|
|
|
|
0
|
return; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
3
|
|
|
|
|
20
|
$delegate_class->dump_status_messages(1); |
100
|
3
|
|
|
|
|
15
|
$delegate_class->dump_warning_messages(1); |
101
|
3
|
|
|
|
|
14
|
$delegate_class->dump_error_messages(1); |
102
|
3
|
|
|
|
|
13
|
$delegate_class->dump_usage_messages(1); |
103
|
3
|
|
|
|
|
15
|
$delegate_class->dump_debug_messages(0); |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# FIXME There should be a better check for params that are there because they came from the |
106
|
|
|
|
|
|
|
# command line, and params that exist for infrastructural purposes. 'original_command_line' |
107
|
|
|
|
|
|
|
# won't ever be given on the command line and shouldn't count toward the next test. |
108
|
|
|
|
|
|
|
# maybe check the is_input properties... |
109
|
3
|
100
|
|
|
|
10
|
if ( !defined($params) ) { |
110
|
1
|
|
|
|
|
13
|
my $command_name = $delegate_class->command_name; |
111
|
1
|
|
|
|
|
7
|
$delegate_class->status_message($delegate_class->help_usage_complete_text); |
112
|
1
|
|
|
|
|
7
|
$delegate_class->error_message("Please specify valid params for '$command_name'."); |
113
|
1
|
|
|
|
|
3
|
return; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
2
|
50
|
|
|
|
5
|
if ( $params->{help} ) { |
117
|
0
|
|
|
|
|
0
|
$delegate_class->usage_message($delegate_class->help_usage_complete_text); |
118
|
0
|
|
|
|
|
0
|
return 1; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
2
|
50
|
|
|
|
5
|
$params->{'original_command_line'} = $original_cmdline if (defined $original_cmdline); |
122
|
2
|
|
|
|
|
10
|
my $command_object = $delegate_class->create(%$params); |
123
|
|
|
|
|
|
|
|
124
|
2
|
50
|
|
|
|
7
|
unless ($command_object) { |
125
|
|
|
|
|
|
|
# The delegate class should have emitted an error message. |
126
|
|
|
|
|
|
|
# This is just in case the developer is sloppy, and the user will think the task did not fail. |
127
|
0
|
|
|
|
|
0
|
print STDERR "Exiting.\n"; |
128
|
0
|
|
|
|
|
0
|
return; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
2
|
|
|
|
|
7
|
$command_object->dump_status_messages(1); |
132
|
2
|
|
|
|
|
8
|
$command_object->dump_warning_messages(1); |
133
|
2
|
|
|
|
|
4
|
$command_object->dump_error_messages(1); |
134
|
2
|
|
|
|
|
6
|
$command_object->dump_debug_messages($command_object->debug); |
135
|
2
|
100
|
|
|
|
4
|
if ($command_object->debug) { |
136
|
1
|
|
|
|
|
62
|
UR::ModuleBase->dump_debug_messages($command_object->debug); |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
2
|
|
|
|
|
45
|
my $rv = $command_object->execute($params); |
140
|
|
|
|
|
|
|
|
141
|
2
|
50
|
|
|
|
6
|
if ($command_object->__errors__) { |
142
|
0
|
|
|
|
|
0
|
$command_object->delete; |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
2
|
|
|
|
|
4
|
return $rv; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
sub resolve_class_and_params_for_argv { |
149
|
|
|
|
|
|
|
# This is used by execute_with_shell_params_and_exit, but might be used within an application. |
150
|
20
|
|
|
20
|
0
|
19692
|
my $self = shift; |
151
|
20
|
|
|
|
|
47
|
my @argv = @_; |
152
|
|
|
|
|
|
|
|
153
|
20
|
|
|
|
|
64
|
my ($params_hash,@spec) = $self->_shell_args_getopt_specification; |
154
|
20
|
50
|
|
|
|
31
|
unless (grep { /^help\W/ } @spec) { |
|
100
|
|
|
|
|
124
|
|
155
|
20
|
|
|
|
|
28
|
push @spec, "help!"; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
20
|
|
|
|
|
18
|
my @error_tags; |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# Thes nasty GetOptions modules insist on working on |
161
|
|
|
|
|
|
|
# the real @ARGV, while we like a little more flexibility. |
162
|
|
|
|
|
|
|
# Not a problem in Perl. :) (which is probably why it was never fixed) |
163
|
20
|
|
|
|
|
25
|
local @ARGV; |
164
|
20
|
|
|
|
|
34
|
@ARGV = @argv; |
165
|
|
|
|
|
|
|
|
166
|
20
|
|
|
|
|
17
|
do { |
167
|
|
|
|
|
|
|
# GetOptions also likes to emit warnings instead of return a list of errors :( |
168
|
20
|
|
|
|
|
17
|
my @errors; |
169
|
|
|
|
|
|
|
my $rv; |
170
|
|
|
|
|
|
|
{ |
171
|
20
|
|
|
0
|
|
8
|
local $SIG{__WARN__} = sub { push @errors, @_ }; |
|
20
|
|
|
|
|
114
|
|
|
0
|
|
|
|
|
0
|
|
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
## Change the pattern to be '--', '-' followed by a non-digit, or '+'. |
174
|
|
|
|
|
|
|
## This s the effect of treating a negative number as a value of an option. |
175
|
|
|
|
|
|
|
## This means that we won't be allowed to have an option named, say, -1. |
176
|
|
|
|
|
|
|
## But since command modules' properties have to be allowable function names, |
177
|
|
|
|
|
|
|
## and "1" is not a valid function name, it's not really a problem |
178
|
|
|
|
|
|
|
#Getopt::Long::Configure('prefix_pattern=--|-(?!\D)|\+'); |
179
|
20
|
|
|
|
|
65
|
$rv = GetOptions($params_hash,@spec); |
180
|
|
|
|
|
|
|
} |
181
|
20
|
50
|
|
|
|
7972
|
unless ($rv) { |
182
|
0
|
|
|
|
|
0
|
for my $error (@errors) { |
183
|
0
|
|
|
|
|
0
|
$self->error_message($error); |
184
|
|
|
|
|
|
|
} |
185
|
0
|
|
|
|
|
0
|
return($self, undef); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
}; |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# Q: Is there a standard getopt spec for capturing non-option paramters? |
190
|
|
|
|
|
|
|
# Perhaps that's not getting "options" :) |
191
|
|
|
|
|
|
|
# A: Yes. Use '<>'. But we need to process this anyway, so it won't help us. |
192
|
|
|
|
|
|
|
|
193
|
20
|
50
|
|
|
|
57
|
if (my @names = $self->_bare_shell_argument_names) { |
194
|
0
|
|
|
|
|
0
|
for (my $n=0; $n < @ARGV; $n++) { |
195
|
0
|
|
|
|
|
0
|
my $name = $names[$n]; |
196
|
0
|
0
|
|
|
|
0
|
unless ($name) { |
197
|
0
|
|
|
|
|
0
|
$self->error_message("Unexpected bare arguments: @ARGV[$n..$#ARGV]!"); |
198
|
0
|
|
|
|
|
0
|
return($self, undef); |
199
|
|
|
|
|
|
|
} |
200
|
0
|
|
|
|
|
0
|
my $value = $ARGV[$n]; |
201
|
0
|
|
|
|
|
0
|
my $meta = $self->__meta__->property_meta_for_name($name); |
202
|
0
|
0
|
0
|
|
|
0
|
if ($meta->is_many and $n == $#names) { |
203
|
|
|
|
|
|
|
# slurp the rest |
204
|
0
|
|
|
|
|
0
|
$params_hash->{$name} = [@ARGV[$n..$#ARGV]]; |
205
|
0
|
|
|
|
|
0
|
last; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
else { |
208
|
0
|
|
|
|
|
0
|
$params_hash->{$name} = $value; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
20
|
50
|
33
|
|
|
53
|
if (@ARGV and not $self->_bare_shell_argument_names) { |
214
|
|
|
|
|
|
|
## argv but no names |
215
|
0
|
|
|
|
|
0
|
$self->error_message("Unexpected bare arguments: @ARGV!"); |
216
|
0
|
|
|
|
|
0
|
return($self, undef); |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
20
|
|
|
|
|
57
|
for my $key (keys %$params_hash) { |
220
|
|
|
|
|
|
|
# handle any has-many comma-sep values |
221
|
29
|
|
|
|
|
33
|
my $value = $params_hash->{$key}; |
222
|
29
|
50
|
100
|
|
|
126
|
if (ref($value)) { |
|
|
100
|
|
|
|
|
|
223
|
0
|
|
|
|
|
0
|
my @new_value; |
224
|
0
|
|
|
|
|
0
|
for my $v (@$value) { |
225
|
0
|
|
|
|
|
0
|
my @parts = split(/,\s*/,$v); |
226
|
0
|
|
|
|
|
0
|
push @new_value, @parts; |
227
|
|
|
|
|
|
|
} |
228
|
0
|
|
|
|
|
0
|
@$value = @new_value; |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
} elsif ($value eq q('') or $value eq q("")) { |
231
|
|
|
|
|
|
|
# Handle the special values '' and "" to mean undef/NULL |
232
|
4
|
|
|
|
|
4
|
$params_hash->{$key} = ''; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# turn dashes into underscores |
236
|
29
|
|
|
|
|
25
|
my $new_key = $key; |
237
|
|
|
|
|
|
|
|
238
|
29
|
100
|
|
|
|
68
|
next unless ($new_key =~ tr/-/_/); |
239
|
28
|
0
|
33
|
|
|
47
|
if (exists $params_hash->{$new_key} && exists $params_hash->{$key}) { |
240
|
|
|
|
|
|
|
# this corrects a problem where is_many properties badly interact |
241
|
|
|
|
|
|
|
# with bare args leaving two entries in the hash like: |
242
|
|
|
|
|
|
|
# a-bare-opt => [], a_bare_opt => ['with','vals'] |
243
|
0
|
|
|
|
|
0
|
delete $params_hash->{$key}; |
244
|
0
|
|
|
|
|
0
|
next; |
245
|
|
|
|
|
|
|
} |
246
|
28
|
|
|
|
|
51
|
$params_hash->{$new_key} = delete $params_hash->{$key}; |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# futher work is looking for errors, and may display them |
250
|
|
|
|
|
|
|
# if help is set, return now |
251
|
|
|
|
|
|
|
# we might have returned sooner, but having full info available |
252
|
|
|
|
|
|
|
# allows for dynamic help |
253
|
20
|
50
|
|
|
|
40
|
if ($params_hash->{help}) { |
254
|
0
|
|
|
|
|
0
|
return ($self, $params_hash); |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
## |
258
|
20
|
|
|
|
|
19
|
my $params = $params_hash; |
259
|
20
|
|
|
|
|
61
|
my $class = $self->class; |
260
|
|
|
|
|
|
|
|
261
|
20
|
100
|
|
|
|
48
|
if (my @errors = $self->_errors_from_missing_parameters($params)) { |
262
|
17
|
|
|
|
|
84
|
return ($class, $params, \@errors); |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
3
|
50
|
|
|
|
7
|
unless (@_) { |
266
|
0
|
|
|
|
|
0
|
return ($class, $params); |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# should this be moved up into the methods which are only called |
270
|
|
|
|
|
|
|
# directly from the shell, or is it okay everywhere in this module to |
271
|
|
|
|
|
|
|
# presume we're a direct cmdline call? -ssmith |
272
|
|
|
|
|
|
|
local $ENV{UR_COMMAND_DUMP_STATUS_MESSAGES} = (!exists($ENV{UR_COMMAND_DUMP_STATUS_MESSAGES}) |
273
|
3
|
|
33
|
|
|
32
|
or $ENV{UR_COMMAND_DUMP_STATUS_MESSAGES}); |
274
|
|
|
|
|
|
|
|
275
|
3
|
|
|
|
|
21
|
my @params_to_resolve = $self->_params_to_resolve($params); |
276
|
3
|
|
|
|
|
5
|
for my $p (@params_to_resolve) { |
277
|
0
|
|
|
|
|
0
|
my $param_arg_str = join(',', @{$p->{value}}); |
|
0
|
|
|
|
|
0
|
|
278
|
0
|
|
|
|
|
0
|
my $pmeta = $self->__meta__->property($p->{name}); |
279
|
|
|
|
|
|
|
|
280
|
0
|
|
|
|
|
0
|
my @params; |
281
|
0
|
|
|
|
|
0
|
eval { |
282
|
0
|
|
|
|
|
0
|
@params = $self->resolve_param_value_from_cmdline_text($p); |
283
|
|
|
|
|
|
|
}; |
284
|
|
|
|
|
|
|
|
285
|
0
|
0
|
|
|
|
0
|
if ($@) { |
286
|
|
|
|
|
|
|
push @error_tags, UR::Object::Tag->create( |
287
|
|
|
|
|
|
|
type => 'invalid', |
288
|
0
|
|
|
|
|
0
|
properties => [$p->{name}], |
289
|
|
|
|
|
|
|
desc => "Errors while resolving from $param_arg_str: $@", |
290
|
|
|
|
|
|
|
); |
291
|
|
|
|
|
|
|
} |
292
|
0
|
0
|
0
|
|
|
0
|
if (@params and $params[0]) { |
293
|
0
|
0
|
|
|
|
0
|
if ($pmeta->{'is_many'}) { |
294
|
0
|
|
|
|
|
0
|
$params->{$p->{name}} = \@params; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
else { |
297
|
0
|
|
|
|
|
0
|
$params->{$p->{name}} = $params[0]; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
else { |
301
|
|
|
|
|
|
|
push @error_tags, UR::Object::Tag->create( |
302
|
|
|
|
|
|
|
type => 'invalid', |
303
|
0
|
|
|
|
|
0
|
properties => [$p->{name}], |
304
|
|
|
|
|
|
|
desc => "Problem resolving from $param_arg_str.", |
305
|
|
|
|
|
|
|
); |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
3
|
50
|
|
|
|
9
|
if (@error_tags) { |
310
|
0
|
|
|
|
|
0
|
return ($class, undef, \@error_tags); |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
else { |
313
|
3
|
|
|
|
|
29
|
return ($class, $params); |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
sub resolve_option_completion_spec { |
318
|
6
|
|
|
6
|
0
|
11
|
my $class = shift; |
319
|
6
|
|
|
|
|
35
|
my @completion_spec = $class->_shell_args_getopt_complete_specification; |
320
|
9
|
|
|
9
|
|
13157
|
no warnings; |
|
9
|
|
|
|
|
16
|
|
|
9
|
|
|
|
|
8226
|
|
321
|
6
|
50
|
|
|
|
13
|
unless (grep { /^help\W/ } @completion_spec) { |
|
72
|
|
|
|
|
74
|
|
322
|
6
|
|
|
|
|
11
|
push @completion_spec, "help!" => undef; |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
return \@completion_spec |
325
|
6
|
|
|
|
|
14
|
} |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
sub _errors_from_missing_parameters { |
328
|
20
|
|
|
20
|
|
17
|
my ($self, $params) = @_; |
329
|
|
|
|
|
|
|
|
330
|
20
|
|
|
|
|
63
|
my $class_meta = $self->__meta__; |
331
|
|
|
|
|
|
|
|
332
|
20
|
|
|
|
|
448
|
my @all_property_metas = $class_meta->properties(); |
333
|
20
|
|
|
|
|
40
|
my @specified_property_metas = grep { exists $params->{$_->property_name} } @all_property_metas; |
|
260
|
|
|
|
|
360
|
|
334
|
|
|
|
|
|
|
|
335
|
20
|
|
|
|
|
26
|
my %specified_property_metas = map { $_->property_name => $_ } @specified_property_metas; |
|
29
|
|
|
|
|
39
|
|
336
|
20
|
|
|
|
|
24
|
my %set_indirectly; |
337
|
20
|
|
|
|
|
26
|
my @todo = @specified_property_metas; |
338
|
20
|
|
|
|
|
46
|
while (my $property_meta = shift @todo) { |
339
|
29
|
50
|
|
|
|
53
|
if (my $via = $property_meta->via) { |
|
|
50
|
|
|
|
|
|
340
|
0
|
0
|
|
|
|
0
|
if (not $property_meta->is_mutable) { |
341
|
0
|
|
0
|
|
|
0
|
my $list = $set_indirectly{$via} ||= []; |
342
|
0
|
|
|
|
|
0
|
push @$list, $property_meta; |
343
|
|
|
|
|
|
|
} |
344
|
0
|
0
|
|
|
|
0
|
unless ($specified_property_metas{$via}) { |
345
|
0
|
|
|
|
|
0
|
my $via_meta = $specified_property_metas{$via} = $class_meta->property($via); |
346
|
0
|
|
|
|
|
0
|
push @specified_property_metas, $via_meta; |
347
|
0
|
|
|
|
|
0
|
push @todo, $via_meta; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
elsif (my $id_by = $property_meta) { |
351
|
29
|
|
50
|
|
|
128
|
my $list = $set_indirectly{$id_by} ||= []; |
352
|
29
|
|
|
|
|
40
|
push @$list, $property_meta; |
353
|
29
|
50
|
|
|
|
55
|
unless ($specified_property_metas{$id_by}) { |
354
|
29
|
|
|
|
|
71
|
my $id_by_meta = $specified_property_metas{$id_by} = $class_meta->property($id_by); |
355
|
29
|
|
|
|
|
29
|
push @specified_property_metas, $id_by_meta; |
356
|
29
|
|
|
|
|
66
|
push @todo, $id_by_meta; |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# TODO: this should use @all_property_metas, and filter down to is_param and is_input |
362
|
|
|
|
|
|
|
# This old code just ignores things inherited from a base class. |
363
|
|
|
|
|
|
|
# We will need to be careful fixing this because it could add checks to tools which |
364
|
|
|
|
|
|
|
# work currently and lead to unexpected failures. |
365
|
20
|
|
|
|
|
19
|
my @property_names; |
366
|
20
|
50
|
|
|
|
42
|
if (my $has = $class_meta->{has}) { |
367
|
20
|
|
|
|
|
153
|
@property_names = List::MoreUtils::uniq(keys %$has); |
368
|
|
|
|
|
|
|
} |
369
|
20
|
|
|
|
|
47
|
my @property_metas = map { $class_meta->property_meta_for_name($_); } @property_names; |
|
120
|
|
|
|
|
179
|
|
370
|
|
|
|
|
|
|
|
371
|
20
|
|
|
|
|
23
|
my @error_tags; |
372
|
20
|
|
|
|
|
30
|
for my $property_meta (@property_metas) { |
373
|
120
|
|
|
|
|
191
|
my $pn = $property_meta->property_name; |
374
|
|
|
|
|
|
|
|
375
|
120
|
100
|
|
|
|
168
|
next if $property_meta->is_optional; |
376
|
60
|
50
|
|
|
|
86
|
next if $property_meta->implied_by; |
377
|
60
|
50
|
|
|
|
111
|
next if defined $property_meta->default_value; |
378
|
60
|
100
|
|
|
|
91
|
next if defined $params->{$pn}; |
379
|
50
|
50
|
|
|
|
70
|
next if $set_indirectly{$pn}; |
380
|
|
|
|
|
|
|
|
381
|
50
|
50
|
|
|
|
81
|
if (my $via = $property_meta->via) { |
382
|
0
|
0
|
0
|
|
|
0
|
if ($params->{$via} or $set_indirectly{$via}) { |
383
|
0
|
|
|
|
|
0
|
next; |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
|
387
|
50
|
|
|
|
|
53
|
my $arg = $pn; |
388
|
50
|
|
|
|
|
120
|
$arg =~ s/_/-/g; |
389
|
50
|
|
|
|
|
63
|
$arg = "--$arg"; |
390
|
|
|
|
|
|
|
|
391
|
50
|
100
|
66
|
|
|
183
|
if ($property_meta->is_output and not $property_meta->is_input and not $property_meta->is_param) { |
|
|
|
66
|
|
|
|
|
392
|
20
|
50
|
33
|
|
|
57
|
if ($property_meta->_data_type_as_class_name->__meta__->data_source |
|
|
50
|
|
|
|
|
|
393
|
|
|
|
|
|
|
and not $property_meta->_data_type_as_class_name->isa("UR::Value") |
394
|
|
|
|
|
|
|
) { |
395
|
|
|
|
|
|
|
# outputs with a data source do not need a specification |
396
|
|
|
|
|
|
|
# on the cmdline to "store" them after execution |
397
|
0
|
|
|
|
|
0
|
next; |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
elsif ($property_meta->is_calculated) { |
400
|
|
|
|
|
|
|
# outputs that are calculated don't need to be specified on |
401
|
|
|
|
|
|
|
# the command line |
402
|
20
|
|
|
|
|
43
|
next; |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
else { |
405
|
0
|
|
|
|
|
0
|
push @error_tags, UR::Object::Tag->create( |
406
|
|
|
|
|
|
|
type => 'invalid', |
407
|
|
|
|
|
|
|
properties => [$pn], |
408
|
|
|
|
|
|
|
desc => "Output requires specified destination: " . $arg . "." |
409
|
|
|
|
|
|
|
); |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
else { |
413
|
30
|
|
|
|
|
49
|
$DB::single = 1; |
414
|
30
|
|
|
|
|
136
|
push @error_tags, UR::Object::Tag->create( |
415
|
|
|
|
|
|
|
type => 'invalid', |
416
|
|
|
|
|
|
|
properties => [$pn], |
417
|
|
|
|
|
|
|
desc => "Missing required parameter: " . $arg . "." |
418
|
|
|
|
|
|
|
); |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
20
|
|
|
|
|
117
|
return @error_tags; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
sub _params_to_resolve { |
426
|
3
|
|
|
3
|
|
5
|
my ($self, $params) = @_; |
427
|
3
|
|
|
|
|
4
|
my @params_to_resolve; |
428
|
3
|
50
|
|
|
|
9
|
if ($params) { |
429
|
3
|
|
|
|
|
11
|
my $cmeta = $self->__meta__; |
430
|
3
|
|
|
|
|
5
|
my @params_will_require_verification; |
431
|
|
|
|
|
|
|
my @params_may_require_verification; |
432
|
|
|
|
|
|
|
|
433
|
3
|
|
|
|
|
7
|
for my $param_name (keys %$params) { |
434
|
6
|
|
|
|
|
20
|
my $pmeta = $cmeta->property($param_name); |
435
|
6
|
50
|
|
|
|
11
|
unless ($pmeta) { |
436
|
|
|
|
|
|
|
# This message was a die after a next, so I guess it isn't supposed to be fatal? |
437
|
0
|
|
|
|
|
0
|
$self->warning_message("No metadata for property '$param_name'"); |
438
|
0
|
|
|
|
|
0
|
next; |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
|
441
|
6
|
|
|
|
|
16
|
my $param_type = $pmeta->data_type; |
442
|
6
|
50
|
|
|
|
17
|
next unless($self->_can_resolve_type($param_type)); |
443
|
|
|
|
|
|
|
|
444
|
0
|
|
|
|
|
0
|
my $param_arg = $params->{$param_name}; |
445
|
0
|
0
|
|
|
|
0
|
if (my $arg_type = ref($param_arg)) { |
446
|
0
|
0
|
|
|
|
0
|
next if $arg_type eq $param_type; # param is already the right type |
447
|
0
|
0
|
|
|
|
0
|
if ($arg_type ne 'ARRAY') { |
448
|
0
|
|
|
|
|
0
|
$self->error_message("no handler for property '$param_name' with argument type " . ref($param_arg)); |
449
|
0
|
|
|
|
|
0
|
next; |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
} else { |
452
|
0
|
|
|
|
|
0
|
$param_arg = [$param_arg]; |
453
|
|
|
|
|
|
|
} |
454
|
0
|
0
|
|
|
|
0
|
next unless (@$param_arg); |
455
|
|
|
|
|
|
|
|
456
|
0
|
|
|
|
|
0
|
my $resolve_info = { |
457
|
|
|
|
|
|
|
name => $param_name, |
458
|
|
|
|
|
|
|
class => $param_type, |
459
|
|
|
|
|
|
|
value => $param_arg, |
460
|
|
|
|
|
|
|
}; |
461
|
0
|
|
|
|
|
0
|
push(@params_to_resolve, $resolve_info); |
462
|
|
|
|
|
|
|
|
463
|
0
|
|
|
|
|
0
|
my $require_user_verify = $pmeta->{'require_user_verify'}; |
464
|
0
|
0
|
|
|
|
0
|
if ( defined($require_user_verify) ) { |
465
|
0
|
0
|
|
|
|
0
|
push @params_will_require_verification, "'$param_name'" if ($require_user_verify); |
466
|
|
|
|
|
|
|
} else { |
467
|
0
|
|
|
|
|
0
|
push @params_may_require_verification, "'$param_name'"; |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
|
471
|
3
|
|
|
|
|
8
|
my @adverbs = ('will', 'may'); |
472
|
3
|
|
|
|
|
8
|
my @params_adverb_require_verification = ( |
473
|
|
|
|
|
|
|
\@params_will_require_verification, |
474
|
|
|
|
|
|
|
\@params_may_require_verification, |
475
|
|
|
|
|
|
|
); |
476
|
3
|
|
|
|
|
15
|
for (my $i = 0; $i < @adverbs; $i++) { |
477
|
6
|
|
|
|
|
8
|
my $adverb = $adverbs[$i]; |
478
|
6
|
|
|
|
|
6
|
my @param_adverb_require_verification = @{$params_adverb_require_verification[$i]}; |
|
6
|
|
|
|
|
10
|
|
479
|
6
|
50
|
|
|
|
20
|
next unless (@param_adverb_require_verification); |
480
|
|
|
|
|
|
|
|
481
|
0
|
0
|
|
|
|
0
|
if (@param_adverb_require_verification > 1) { |
482
|
0
|
|
|
|
|
0
|
$param_adverb_require_verification[-1] = 'and ' . $param_adverb_require_verification[-1]; |
483
|
|
|
|
|
|
|
} |
484
|
0
|
|
|
|
|
0
|
my $param_str = join(', ', @param_adverb_require_verification); |
485
|
0
|
|
|
|
|
0
|
$self->status_message($param_str . " $adverb require verification..."); |
486
|
|
|
|
|
|
|
} |
487
|
|
|
|
|
|
|
} |
488
|
3
|
|
|
|
|
9
|
return @params_to_resolve; |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
sub _can_resolve_type { |
492
|
6
|
|
|
6
|
|
11
|
my ($self, $type) = @_; |
493
|
|
|
|
|
|
|
|
494
|
6
|
50
|
|
|
|
9
|
return 0 unless($type); |
495
|
|
|
|
|
|
|
|
496
|
6
|
|
|
|
|
9
|
my $non_classes = 0; |
497
|
6
|
50
|
|
|
|
12
|
if (ref($type) ne 'ARRAY') { |
498
|
6
|
|
|
|
|
12
|
$non_classes = $type !~ m/::/; |
499
|
|
|
|
|
|
|
} else { |
500
|
0
|
|
|
|
|
0
|
$non_classes = scalar grep { ! m/::/ } @$type; |
|
0
|
|
|
|
|
0
|
|
501
|
|
|
|
|
|
|
} |
502
|
6
|
|
|
|
|
20
|
return $non_classes == 0; |
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
sub _shell_args_property_meta { |
506
|
58
|
|
|
58
|
|
59
|
my $self = shift; |
507
|
58
|
|
|
|
|
149
|
my $class_meta = $self->__meta__; |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
# Find which property metas match the rules. We have to do it this way |
510
|
|
|
|
|
|
|
# because just calling 'get_all_property_metas()' will product multiple matches |
511
|
|
|
|
|
|
|
# if a property is overridden in a child class |
512
|
58
|
|
|
|
|
197
|
my ($rule, %extra) = UR::Object::Property->define_boolexpr(@_); |
513
|
58
|
|
|
|
|
60
|
my %seen; |
514
|
58
|
|
|
|
|
54
|
my (@positional,@required_input,@required_param,@optional_input,@optional_param, @output); |
515
|
|
|
|
|
|
|
|
516
|
58
|
|
|
|
|
1479
|
my @property_meta = $class_meta->properties(); |
517
|
|
|
|
|
|
|
PROP: |
518
|
58
|
|
|
|
|
111
|
foreach my $property_meta (@property_meta) { |
519
|
749
|
|
|
|
|
2832
|
my $property_name = $property_meta->property_name; |
520
|
|
|
|
|
|
|
|
521
|
749
|
50
|
|
|
|
1364
|
next if $seen{$property_name}++; |
522
|
749
|
100
|
|
|
|
1496
|
next unless $rule->evaluate($property_meta); |
523
|
725
|
100
|
100
|
|
|
1337
|
next unless $property_meta->can("is_param") and ($property_meta->is_param or $property_meta->is_input or $property_meta->is_output); |
|
|
|
66
|
|
|
|
|
524
|
377
|
100
|
|
|
|
709
|
if (%extra) { |
525
|
9
|
|
|
9
|
|
52
|
no warnings; |
|
9
|
|
|
|
|
12
|
|
|
9
|
|
|
|
|
30651
|
|
526
|
6
|
|
|
|
|
9
|
for my $key (keys %extra) { |
527
|
6
|
100
|
|
|
|
18
|
if ($property_meta->$key ne $extra{$key}) { |
528
|
4
|
|
|
|
|
8
|
next PROP; |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
} |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
|
533
|
373
|
50
|
|
|
|
557
|
next if $property_name eq 'id'; |
534
|
373
|
100
|
|
|
|
553
|
next if $property_name eq 'result'; |
535
|
320
|
50
|
|
|
|
390
|
next if $property_name eq 'is_executed'; |
536
|
320
|
50
|
|
|
|
383
|
next if $property_name eq 'original_command_line'; |
537
|
320
|
50
|
|
|
|
469
|
next if $property_name =~ /^_/; |
538
|
|
|
|
|
|
|
|
539
|
320
|
50
|
|
|
|
620
|
next if $property_meta->implied_by; |
540
|
320
|
100
|
|
|
|
479
|
next if $property_meta->is_calculated; |
541
|
|
|
|
|
|
|
# Kept commented out from UR's Command.pm, I believe is_output is a workflow property |
542
|
|
|
|
|
|
|
# and not something we need to exclude (counter to the old comment below). |
543
|
|
|
|
|
|
|
#next if $property_meta->{is_output}; # TODO: This was breaking the G::M::T::Annotate::TranscriptVariants annotator. This should probably still be here but temporarily roll back |
544
|
280
|
50
|
|
|
|
445
|
next if $property_meta->is_transient; |
545
|
280
|
100
|
|
|
|
423
|
next if $property_meta->is_constant; |
546
|
279
|
50
|
66
|
|
|
443
|
if (($property_meta->is_delegated) || (defined($property_meta->data_type) and $property_meta->data_type =~ /::/)) { |
|
|
|
33
|
|
|
|
|
547
|
0
|
0
|
|
|
|
0
|
next unless($self->can('resolve_param_value_from_cmdline_text')); |
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
else { |
550
|
279
|
50
|
|
|
|
445
|
next unless($property_meta->is_mutable); |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
|
553
|
279
|
100
|
|
|
|
653
|
if ($property_meta->{shell_args_position}) { |
|
|
100
|
|
|
|
|
|
554
|
16
|
|
|
|
|
33
|
push @positional, $property_meta; |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
elsif ($property_meta->is_optional) { |
557
|
168
|
100
|
66
|
|
|
534
|
if ($property_meta->is_input or $property_meta->is_output) { |
|
|
50
|
|
|
|
|
|
558
|
15
|
|
|
|
|
27
|
push @optional_input, $property_meta; |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
elsif ($property_meta->is_param) { |
561
|
153
|
|
|
|
|
245
|
push @optional_param, $property_meta; |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
} |
564
|
|
|
|
|
|
|
else { |
565
|
95
|
100
|
66
|
|
|
303
|
if ($property_meta->is_input or $property_meta->is_output) { |
|
|
50
|
|
|
|
|
|
566
|
8
|
|
|
|
|
23
|
push @required_input, $property_meta; |
567
|
|
|
|
|
|
|
} |
568
|
|
|
|
|
|
|
elsif ($property_meta->is_param) { |
569
|
87
|
|
|
|
|
139
|
push @required_param, $property_meta; |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
58
|
|
|
|
|
45
|
my @result; |
575
|
|
|
|
|
|
|
@result = ( |
576
|
46
|
|
|
|
|
120
|
(sort { $a->position_in_module_header cmp $b->position_in_module_header } @required_param), |
577
|
145
|
|
|
|
|
235
|
(sort { $a->position_in_module_header cmp $b->position_in_module_header } @optional_param), |
578
|
0
|
|
|
|
|
0
|
(sort { $a->position_in_module_header cmp $b->position_in_module_header } @required_input), |
579
|
7
|
|
|
|
|
18
|
(sort { $a->position_in_module_header cmp $b->position_in_module_header } @optional_input), |
580
|
58
|
|
|
|
|
188
|
(sort { $a->shell_args_position <=> $b->shell_args_position } @positional), |
|
6
|
|
|
|
|
23
|
|
581
|
|
|
|
|
|
|
); |
582
|
|
|
|
|
|
|
|
583
|
58
|
|
|
|
|
315
|
return @result; |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
sub _shell_arg_name_from_property_meta { |
588
|
137
|
|
|
137
|
|
114
|
my ($self, $property_meta,$singularize) = @_; |
589
|
137
|
50
|
|
|
|
288
|
my $property_name = ($singularize ? $property_meta->singular_name : $property_meta->property_name); |
590
|
137
|
|
|
|
|
114
|
my $param_name = $property_name; |
591
|
137
|
|
|
|
|
220
|
$param_name =~ s/_/-/g; |
592
|
137
|
|
|
|
|
174
|
return $param_name; |
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
sub _shell_arg_getopt_qualifier_from_property_meta { |
596
|
136
|
|
|
136
|
|
103
|
my ($self, $property_meta) = @_; |
597
|
|
|
|
|
|
|
|
598
|
136
|
100
|
|
|
|
206
|
my $many = ($property_meta->is_many ? '@' : ''); |
599
|
136
|
100
|
100
|
|
|
195
|
if (defined($property_meta->data_type) and $property_meta->data_type =~ /Boolean/) { |
600
|
9
|
|
|
|
|
28
|
return '!' . $many; |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
#elsif ($property_meta->is_optional) { |
603
|
|
|
|
|
|
|
# return ':s' . $many; |
604
|
|
|
|
|
|
|
#} |
605
|
|
|
|
|
|
|
else { |
606
|
127
|
|
|
|
|
289
|
return '=s' . $many; |
607
|
|
|
|
|
|
|
} |
608
|
|
|
|
|
|
|
} |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
sub _shell_arg_usage_string_from_property_meta { |
611
|
0
|
|
|
0
|
|
0
|
my ($self, $property_meta) = @_; |
612
|
0
|
|
|
|
|
0
|
my $string = $self->_shell_arg_name_from_property_meta($property_meta); |
613
|
0
|
0
|
|
|
|
0
|
if ($property_meta->{shell_args_position}) { |
614
|
0
|
|
|
|
|
0
|
$string = uc($string); |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
|
617
|
0
|
0
|
|
|
|
0
|
if ($property_meta->{shell_args_position}) { |
618
|
0
|
0
|
|
|
|
0
|
if ($property_meta->is_optional) { |
619
|
0
|
|
|
|
|
0
|
$string = "[$string]"; |
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
} |
622
|
|
|
|
|
|
|
else { |
623
|
0
|
|
|
|
|
0
|
$string = "--$string"; |
624
|
0
|
0
|
0
|
|
|
0
|
if (defined($property_meta->data_type) and $property_meta->data_type =~ /Boolean/) { |
625
|
0
|
|
|
|
|
0
|
$string = "[$string]"; |
626
|
|
|
|
|
|
|
} |
627
|
|
|
|
|
|
|
else { |
628
|
0
|
0
|
|
|
|
0
|
if ($property_meta->is_many) { |
629
|
0
|
|
|
|
|
0
|
$string .= "=?[,?]"; |
630
|
|
|
|
|
|
|
} |
631
|
|
|
|
|
|
|
else { |
632
|
0
|
|
|
|
|
0
|
$string .= '=?'; |
633
|
|
|
|
|
|
|
} |
634
|
0
|
0
|
|
|
|
0
|
if ($property_meta->is_optional) { |
635
|
0
|
|
|
|
|
0
|
$string = "[$string]"; |
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
} |
638
|
|
|
|
|
|
|
} |
639
|
0
|
|
|
|
|
0
|
return $string; |
640
|
|
|
|
|
|
|
} |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
sub _shell_arg_getopt_specification_from_property_meta { |
643
|
100
|
|
|
100
|
|
80
|
my ($self,$property_meta) = @_; |
644
|
100
|
|
|
|
|
138
|
my $arg_name = $self->_shell_arg_name_from_property_meta($property_meta); |
645
|
|
|
|
|
|
|
return ( |
646
|
100
|
|
|
|
|
140
|
$arg_name . $self->_shell_arg_getopt_qualifier_from_property_meta($property_meta), |
647
|
|
|
|
|
|
|
#this prevents defaults from being used for is_many properties |
648
|
|
|
|
|
|
|
#($property_meta->is_many ? ($arg_name => []) : ()) |
649
|
|
|
|
|
|
|
); |
650
|
|
|
|
|
|
|
} |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
sub _shell_arg_getopt_complete_specification_from_property_meta { |
654
|
36
|
|
|
36
|
|
32
|
my ($self,$property_meta) = @_; |
655
|
36
|
|
|
|
|
60
|
my $arg_name = $self->_shell_arg_name_from_property_meta($property_meta); |
656
|
36
|
|
|
|
|
56
|
my $completions = $property_meta->valid_values; |
657
|
36
|
100
|
|
|
|
43
|
if ($completions) { |
658
|
3
|
50
|
|
|
|
8
|
if (ref($completions) eq 'ARRAY') { |
659
|
3
|
|
|
|
|
13
|
$completions = [ @$completions ]; |
660
|
|
|
|
|
|
|
} |
661
|
|
|
|
|
|
|
} |
662
|
|
|
|
|
|
|
else { |
663
|
33
|
|
|
|
|
44
|
my $type = $property_meta->data_type; |
664
|
33
|
|
|
|
|
60
|
my @complete_as_files = ( |
665
|
|
|
|
|
|
|
'File','FilePath','Filesystem','FileSystem','FilesystemPath','FileSystemPath', |
666
|
|
|
|
|
|
|
'Text','String', |
667
|
|
|
|
|
|
|
); |
668
|
33
|
|
|
|
|
38
|
my @complete_as_directories = ( |
669
|
|
|
|
|
|
|
'Directory','DirectoryPath','Dir','DirPath', |
670
|
|
|
|
|
|
|
); |
671
|
33
|
100
|
|
|
|
65
|
if (!defined($type)) { |
672
|
1
|
|
|
|
|
2
|
$completions = 'files'; |
673
|
|
|
|
|
|
|
} |
674
|
|
|
|
|
|
|
else { |
675
|
32
|
|
|
|
|
29
|
for my $pattern (@complete_as_files) { |
676
|
238
|
100
|
66
|
|
|
580
|
if (!$type || $type eq $pattern) { |
677
|
19
|
|
|
|
|
14
|
$completions = 'files'; |
678
|
19
|
|
|
|
|
16
|
last; |
679
|
|
|
|
|
|
|
} |
680
|
|
|
|
|
|
|
} |
681
|
32
|
|
|
|
|
26
|
for my $pattern (@complete_as_directories) { |
682
|
128
|
50
|
33
|
|
|
319
|
if ( $type && $type eq $pattern) { |
683
|
0
|
|
|
|
|
0
|
$completions = 'directories'; |
684
|
0
|
|
|
|
|
0
|
last; |
685
|
|
|
|
|
|
|
} |
686
|
|
|
|
|
|
|
} |
687
|
|
|
|
|
|
|
} |
688
|
|
|
|
|
|
|
} |
689
|
|
|
|
|
|
|
return ( |
690
|
36
|
|
|
|
|
72
|
$arg_name . $self->_shell_arg_getopt_qualifier_from_property_meta($property_meta), |
691
|
|
|
|
|
|
|
$completions, |
692
|
|
|
|
|
|
|
# ($property_meta->is_many ? ($arg_name => []) : ()) |
693
|
|
|
|
|
|
|
); |
694
|
|
|
|
|
|
|
} |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
sub _shell_args_getopt_specification { |
697
|
20
|
|
|
20
|
|
23
|
my $self = shift; |
698
|
20
|
|
|
|
|
20
|
my @getopt; |
699
|
|
|
|
|
|
|
my @params; |
700
|
20
|
|
|
|
|
52
|
for my $meta ($self->_shell_args_property_meta) { |
701
|
100
|
|
|
|
|
149
|
my ($spec, @params_addition) = $self->_shell_arg_getopt_specification_from_property_meta($meta); |
702
|
100
|
|
|
|
|
104
|
push @getopt,$spec; |
703
|
100
|
|
|
|
|
95
|
push @params, @params_addition; |
704
|
|
|
|
|
|
|
} |
705
|
20
|
|
|
|
|
51
|
@getopt = sort @getopt; |
706
|
20
|
|
|
|
|
63
|
return { @params}, @getopt; |
707
|
|
|
|
|
|
|
} |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
sub _shell_args_getopt_complete_specification { |
710
|
6
|
|
|
6
|
|
11
|
my $self = shift; |
711
|
6
|
|
|
|
|
9
|
my @getopt; |
712
|
6
|
|
|
|
|
41
|
for my $meta ($self->_shell_args_property_meta) { |
713
|
36
|
|
|
|
|
102
|
my ($spec, $completions) = $self->_shell_arg_getopt_complete_specification_from_property_meta($meta); |
714
|
36
|
|
|
|
|
55
|
push @getopt, $spec, $completions; |
715
|
|
|
|
|
|
|
} |
716
|
6
|
|
|
|
|
28
|
return @getopt; |
717
|
|
|
|
|
|
|
} |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
sub _bare_shell_argument_names { |
721
|
20
|
|
|
20
|
|
24
|
my $self = shift; |
722
|
20
|
|
|
|
|
59
|
my $meta = $self->__meta__; |
723
|
|
|
|
|
|
|
my @ordered_names = |
724
|
0
|
|
|
|
|
0
|
map { $_->property_name } |
725
|
0
|
|
|
|
|
0
|
sort { $a->{shell_args_position} <=> $b->{shell_args_position} } |
726
|
20
|
|
|
|
|
41
|
grep { $_->{shell_args_position} } |
|
100
|
|
|
|
|
108
|
|
727
|
|
|
|
|
|
|
$self->_shell_args_property_meta(); |
728
|
20
|
|
|
|
|
52
|
return @ordered_names; |
729
|
|
|
|
|
|
|
} |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
# |
732
|
|
|
|
|
|
|
# Logic to turn command-line text into objects for parameter/input values |
733
|
|
|
|
|
|
|
# |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
our %ALTERNATE_FROM_CLASS = (); |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
# This will prevent infinite loops during recursion. |
738
|
|
|
|
|
|
|
our %SEEN_FROM_CLASS = (); |
739
|
|
|
|
|
|
|
our $MESSAGE; |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
sub resolve_param_value_from_cmdline_text { |
742
|
3
|
|
|
3
|
0
|
5
|
my ($self, $param_info) = @_; |
743
|
3
|
|
|
|
|
10
|
my $param_name = $param_info->{name}; |
744
|
3
|
|
|
|
|
5
|
my $param_class = $param_info->{class}; |
745
|
3
|
|
|
|
|
3
|
my @param_args = @{$param_info->{value}}; |
|
3
|
|
|
|
|
6
|
|
746
|
3
|
|
|
|
|
8
|
my $param_str = join(',', @param_args); |
747
|
|
|
|
|
|
|
|
748
|
3
|
50
|
|
|
|
8
|
if (ref($param_class) eq 'ARRAY') { |
749
|
0
|
|
|
|
|
0
|
my @param_class = @$param_class; |
750
|
0
|
0
|
|
|
|
0
|
if (@param_class > 1) { |
751
|
0
|
|
|
|
|
0
|
die 'Multiple data types on command arguments are not supported.'; |
752
|
|
|
|
|
|
|
} else { |
753
|
0
|
|
|
|
|
0
|
$param_class = $param_class[0]; |
754
|
|
|
|
|
|
|
} |
755
|
|
|
|
|
|
|
} |
756
|
|
|
|
|
|
|
|
757
|
3
|
|
|
|
|
6
|
my $param_resolve_message = "Resolving parameter '$param_name' from command argument '$param_str'..."; |
758
|
3
|
|
|
|
|
10
|
my $pmeta = $self->__meta__->property($param_name); |
759
|
3
|
|
|
|
|
5
|
my $require_user_verify = $pmeta->{'require_user_verify'}; |
760
|
|
|
|
|
|
|
|
761
|
3
|
|
|
|
|
6
|
my @results; |
762
|
3
|
|
|
|
|
4
|
my $bx = eval { UR::BoolExpr->resolve_for_string($param_class, $param_str) }; |
|
3
|
|
|
|
|
10
|
|
763
|
3
|
|
|
|
|
5
|
my $bx_error = $@; |
764
|
3
|
100
|
|
|
|
6
|
if ($bx) { |
765
|
2
|
|
|
|
|
14
|
@results = $param_class->get($bx); |
766
|
2
|
50
|
33
|
|
|
9
|
if (@results > 1 && !defined($require_user_verify)) { |
767
|
0
|
|
|
|
|
0
|
$require_user_verify = 1; |
768
|
|
|
|
|
|
|
} |
769
|
|
|
|
|
|
|
} else { |
770
|
1
|
|
|
|
|
2
|
for my $arg (@param_args) { |
771
|
2
|
|
|
|
|
5
|
%SEEN_FROM_CLASS = (); |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
# call resolve_param_value_from_text without a via_method to "bootstrap" recursion |
774
|
2
|
|
|
|
|
8
|
my @arg_results = $self->resolve_param_value_from_text($arg, $param_class); |
775
|
|
|
|
|
|
|
|
776
|
2
|
50
|
33
|
|
|
6
|
if (@arg_results != 1 && !defined($require_user_verify)) { |
777
|
0
|
|
|
|
|
0
|
$require_user_verify = 1; |
778
|
|
|
|
|
|
|
} |
779
|
|
|
|
|
|
|
|
780
|
2
|
|
|
|
|
3
|
push @results, @arg_results; |
781
|
|
|
|
|
|
|
} |
782
|
|
|
|
|
|
|
} |
783
|
3
|
50
|
|
|
|
9
|
if (@results) { |
784
|
|
|
|
|
|
|
# the ALTERNATE_FROM_CLASS stuff leads to non $param_class objects in results |
785
|
3
|
|
|
|
|
17
|
@results = List::MoreUtils::uniq(@results); |
786
|
3
|
|
|
|
|
6
|
@results = grep { $_->isa($param_class) } @results; |
|
6
|
|
|
|
|
21
|
|
787
|
|
|
|
|
|
|
|
788
|
3
|
|
|
|
|
30
|
$self->status_message($param_resolve_message . " found " . @results); |
789
|
|
|
|
|
|
|
} |
790
|
|
|
|
|
|
|
else { |
791
|
0
|
0
|
|
|
|
0
|
if ($bx_error) { |
792
|
0
|
|
|
|
|
0
|
$self->status_message($bx_error); |
793
|
|
|
|
|
|
|
} |
794
|
0
|
|
|
|
|
0
|
$self->status_message($param_resolve_message . " none found."); |
795
|
|
|
|
|
|
|
} |
796
|
|
|
|
|
|
|
|
797
|
3
|
50
|
|
|
|
15
|
return unless (@results); |
798
|
|
|
|
|
|
|
|
799
|
3
|
|
|
|
|
38
|
my $limit_results_method = "_limit_results_for_$param_name"; |
800
|
3
|
50
|
|
|
|
9
|
if ( $self->can($limit_results_method) ) { |
801
|
0
|
|
|
|
|
0
|
@results = $self->$limit_results_method(@results); |
802
|
0
|
0
|
|
|
|
0
|
return unless (@results); |
803
|
|
|
|
|
|
|
} |
804
|
3
|
|
|
|
|
212
|
@results = List::MoreUtils::uniq(@results); |
805
|
3
|
50
|
|
|
|
8
|
if ($require_user_verify) { |
806
|
0
|
0
|
0
|
|
|
0
|
if (!$pmeta->{'is_many'} && @results > 1) { |
807
|
0
|
0
|
|
|
|
0
|
$MESSAGE .= "\n" if ($MESSAGE); |
808
|
0
|
|
|
|
|
0
|
$MESSAGE .= "'$param_name' expects only one result."; |
809
|
|
|
|
|
|
|
|
810
|
0
|
0
|
|
|
|
0
|
if ($ENV{UR_NO_REQUIRE_USER_VERIFY}) { |
811
|
0
|
|
|
|
|
0
|
die "$MESSAGE\n"; |
812
|
|
|
|
|
|
|
} |
813
|
|
|
|
|
|
|
} |
814
|
0
|
|
|
|
|
0
|
@results = $self->_get_user_verification_for_param_value($param_name, @results); |
815
|
|
|
|
|
|
|
} |
816
|
3
|
|
33
|
|
|
10
|
while (!$pmeta->{'is_many'} && @results > 1) { |
817
|
0
|
0
|
|
|
|
0
|
$MESSAGE .= "\n" if ($MESSAGE); |
818
|
0
|
|
|
|
|
0
|
$MESSAGE .= "'$param_name' expects only one result, not many!"; |
819
|
0
|
|
|
|
|
0
|
@results = $self->_get_user_verification_for_param_value($param_name, @results); |
820
|
|
|
|
|
|
|
} |
821
|
|
|
|
|
|
|
|
822
|
3
|
50
|
|
|
|
5
|
if (wantarray) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
823
|
3
|
|
|
|
|
12
|
return @results; |
824
|
|
|
|
|
|
|
} |
825
|
|
|
|
|
|
|
elsif (not defined wantarray) { |
826
|
0
|
|
|
|
|
0
|
return; |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
elsif (@results > 1) { |
829
|
0
|
|
|
|
|
0
|
Carp::confess("Multiple matches found!"); |
830
|
|
|
|
|
|
|
} |
831
|
|
|
|
|
|
|
else { |
832
|
0
|
|
|
|
|
0
|
return $results[0]; |
833
|
|
|
|
|
|
|
} |
834
|
|
|
|
|
|
|
} |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
sub resolve_param_value_from_text { |
837
|
2
|
|
|
2
|
0
|
4
|
my ($self, $param_arg, $param_class, $via_method) = @_; |
838
|
|
|
|
|
|
|
|
839
|
2
|
50
|
|
|
|
4
|
unless ($param_class) { |
840
|
0
|
|
|
|
|
0
|
$param_class = $self->class; |
841
|
|
|
|
|
|
|
} |
842
|
|
|
|
|
|
|
|
843
|
2
|
|
|
|
|
3
|
$SEEN_FROM_CLASS{$param_class} = 1; |
844
|
2
|
|
|
|
|
3
|
my @results; |
845
|
|
|
|
|
|
|
# try getting BoolExpr, otherwise fallback on '_resolve_param_value_from_text_by_name_or_id' parser |
846
|
2
|
|
|
|
|
3
|
eval { @results = $self->_resolve_param_value_from_text_by_bool_expr($param_class, $param_arg); }; |
|
2
|
|
|
|
|
6
|
|
847
|
2
|
50
|
33
|
|
|
13
|
Carp::croak($@) if ($@ and $@ !~ m/Not a valid BoolExpr/); |
848
|
2
|
50
|
33
|
|
|
9
|
if (!@results && !$@) { |
849
|
|
|
|
|
|
|
# no result and was valid BoolExpr then we don't want to break it apart because we |
850
|
|
|
|
|
|
|
# could query enormous amounts of info |
851
|
0
|
|
|
|
|
0
|
return; |
852
|
|
|
|
|
|
|
} |
853
|
|
|
|
|
|
|
# the first param_arg is all param_args to try BoolExpr so skip if it has commas |
854
|
2
|
50
|
33
|
|
|
8
|
if (!@results && $param_arg !~ /,/) { |
855
|
2
|
|
|
|
|
3
|
my @results_by_string; |
856
|
2
|
50
|
|
|
|
6
|
if ($param_class->can('_resolve_param_value_from_text_by_name_or_id')) { |
857
|
0
|
|
|
|
|
0
|
@results_by_string = $param_class->_resolve_param_value_from_text_by_name_or_id($param_arg); |
858
|
|
|
|
|
|
|
} |
859
|
|
|
|
|
|
|
else { |
860
|
2
|
|
|
|
|
101
|
@results_by_string = $self->_resolve_param_value_from_text_by_name_or_id($param_class, $param_arg); |
861
|
|
|
|
|
|
|
} |
862
|
2
|
|
|
|
|
3
|
push @results, @results_by_string; |
863
|
|
|
|
|
|
|
} |
864
|
|
|
|
|
|
|
# if we still don't have any values then try via alternate class |
865
|
2
|
0
|
33
|
|
|
3
|
if (!@results && $param_arg !~ /,/) { |
866
|
0
|
|
|
|
|
0
|
@results = $self->_resolve_param_value_via_related_class_method($param_class, $param_arg, $via_method); |
867
|
|
|
|
|
|
|
} |
868
|
|
|
|
|
|
|
|
869
|
2
|
50
|
|
|
|
4
|
if ($via_method) { |
870
|
0
|
|
|
|
|
0
|
@results = map { $_->$via_method } @results; |
|
0
|
|
|
|
|
0
|
|
871
|
|
|
|
|
|
|
} |
872
|
|
|
|
|
|
|
|
873
|
2
|
50
|
|
|
|
35
|
if (wantarray) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
874
|
2
|
|
|
|
|
4
|
return @results; |
875
|
|
|
|
|
|
|
} |
876
|
|
|
|
|
|
|
elsif (not defined wantarray) { |
877
|
0
|
|
|
|
|
0
|
return; |
878
|
|
|
|
|
|
|
} |
879
|
|
|
|
|
|
|
elsif (@results > 1) { |
880
|
0
|
|
|
|
|
0
|
Carp::confess("Multiple matches found!"); |
881
|
|
|
|
|
|
|
} |
882
|
|
|
|
|
|
|
else { |
883
|
0
|
|
|
|
|
0
|
return $results[0]; |
884
|
|
|
|
|
|
|
} |
885
|
|
|
|
|
|
|
} |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
sub _resolve_param_value_via_related_class_method { |
888
|
0
|
|
|
0
|
|
0
|
my ($self, $param_class, $param_arg, $via_method) = @_; |
889
|
0
|
|
|
|
|
0
|
my @results; |
890
|
|
|
|
|
|
|
my $via_class; |
891
|
0
|
0
|
|
|
|
0
|
if (exists($ALTERNATE_FROM_CLASS{$param_class})) { |
892
|
0
|
|
|
|
|
0
|
$via_class = $param_class; |
893
|
|
|
|
|
|
|
} |
894
|
|
|
|
|
|
|
else { |
895
|
0
|
|
|
|
|
0
|
for my $class (keys %ALTERNATE_FROM_CLASS) { |
896
|
0
|
0
|
|
|
|
0
|
if ($param_class->isa($class)) { |
897
|
0
|
0
|
|
|
|
0
|
if ($via_class) { |
898
|
0
|
|
|
|
|
0
|
$self->error_message("Found additional via_class $class but already found $via_class!"); |
899
|
|
|
|
|
|
|
} |
900
|
0
|
|
|
|
|
0
|
$via_class = $class; |
901
|
|
|
|
|
|
|
} |
902
|
|
|
|
|
|
|
} |
903
|
|
|
|
|
|
|
} |
904
|
0
|
0
|
|
|
|
0
|
if ($via_class) { |
905
|
0
|
|
|
|
|
0
|
my @from_classes = sort keys %{$ALTERNATE_FROM_CLASS{$via_class}}; |
|
0
|
|
|
|
|
0
|
|
906
|
0
|
|
0
|
|
|
0
|
while (@from_classes && !@results) { |
907
|
0
|
|
|
|
|
0
|
my $from_class = shift @from_classes; |
908
|
0
|
|
|
|
|
0
|
my @methods = @{$ALTERNATE_FROM_CLASS{$via_class}{$from_class}}; |
|
0
|
|
|
|
|
0
|
|
909
|
0
|
|
|
|
|
0
|
my $method; |
910
|
0
|
0
|
0
|
|
|
0
|
if (@methods > 1 && !$via_method && !$ENV{UR_NO_REQUIRE_USER_VERIFY}) { |
|
|
|
0
|
|
|
|
|
911
|
0
|
|
|
|
|
0
|
$self->status_message("Trying to find $via_class via $from_class...\n"); |
912
|
0
|
|
|
|
|
0
|
my $method_choices; |
913
|
0
|
|
|
|
|
0
|
for (my $i = 0; $i < @methods; $i++) { |
914
|
0
|
|
|
|
|
0
|
$method_choices .= ($i + 1) . ": " . $methods[$i]; |
915
|
0
|
0
|
|
|
|
0
|
$method_choices .= " [default]" if ($i == 0); |
916
|
0
|
|
|
|
|
0
|
$method_choices .= "\n"; |
917
|
|
|
|
|
|
|
} |
918
|
0
|
|
|
|
|
0
|
$method_choices .= (scalar(@methods) + 1) . ": none\n"; |
919
|
0
|
|
|
|
|
0
|
$method_choices .= "Which method would you like to use?"; |
920
|
0
|
|
|
|
|
0
|
my $response = $self->_ask_user_question($method_choices, 0, '\d+', 1, '#'); |
921
|
0
|
0
|
|
|
|
0
|
if ($response =~ /^\d+$/) { |
|
|
0
|
|
|
|
|
|
922
|
0
|
|
|
|
|
0
|
$response--; |
923
|
0
|
0
|
0
|
|
|
0
|
if ($response == @methods) { |
|
|
0
|
|
|
|
|
|
924
|
0
|
|
|
|
|
0
|
$method = undef; |
925
|
|
|
|
|
|
|
} |
926
|
|
|
|
|
|
|
elsif ($response >= 0 && $response <= $#methods) { |
927
|
0
|
|
|
|
|
0
|
$method = $methods[$response]; |
928
|
|
|
|
|
|
|
} |
929
|
|
|
|
|
|
|
else { |
930
|
0
|
|
|
|
|
0
|
$self->error_message("Response was out of bounds, exiting..."); |
931
|
0
|
|
|
|
|
0
|
exit; |
932
|
|
|
|
|
|
|
} |
933
|
0
|
|
|
|
|
0
|
$ALTERNATE_FROM_CLASS{$via_class}{$from_class} = [$method]; |
934
|
|
|
|
|
|
|
} |
935
|
|
|
|
|
|
|
elsif (!$response) { |
936
|
0
|
|
|
|
|
0
|
$self->status_message("Exiting..."); |
937
|
|
|
|
|
|
|
} |
938
|
|
|
|
|
|
|
} |
939
|
|
|
|
|
|
|
else { |
940
|
0
|
|
|
|
|
0
|
$method = $methods[0]; |
941
|
|
|
|
|
|
|
} |
942
|
0
|
0
|
|
|
|
0
|
unless($SEEN_FROM_CLASS{$from_class}) { |
943
|
|
|
|
|
|
|
#$self->debug_message("Trying to find $via_class via $from_class->$method..."); |
944
|
0
|
|
|
|
|
0
|
@results = eval {$self->resolve_param_value_from_text($param_arg, $from_class, $method)}; |
|
0
|
|
|
|
|
0
|
|
945
|
|
|
|
|
|
|
} |
946
|
|
|
|
|
|
|
} # END for my $from_class (@from_classes) |
947
|
|
|
|
|
|
|
} # END if ($via_class) |
948
|
0
|
|
|
|
|
0
|
return @results; |
949
|
|
|
|
|
|
|
} |
950
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
sub _resolve_param_value_from_text_by_bool_expr { |
952
|
2
|
|
|
2
|
|
4
|
my ($self, $param_class, $arg) = @_; |
953
|
|
|
|
|
|
|
|
954
|
2
|
|
|
|
|
2
|
my @results; |
955
|
2
|
|
|
|
|
2
|
my $bx = eval { |
956
|
2
|
|
|
|
|
7
|
UR::BoolExpr->resolve_for_string($param_class, $arg); |
957
|
|
|
|
|
|
|
}; |
958
|
2
|
50
|
|
|
|
6
|
if ($bx) { |
959
|
0
|
|
|
|
|
0
|
@results = $param_class->get($bx); |
960
|
|
|
|
|
|
|
} |
961
|
|
|
|
|
|
|
else { |
962
|
2
|
|
|
|
|
14
|
die "Not a valid BoolExpr"; |
963
|
|
|
|
|
|
|
} |
964
|
|
|
|
|
|
|
#$self->debug_message("B: $param_class '$arg' " . scalar(@results)); |
965
|
|
|
|
|
|
|
|
966
|
0
|
|
|
|
|
0
|
return @results; |
967
|
|
|
|
|
|
|
} |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
sub _try_get_by_id { |
970
|
2
|
|
|
2
|
|
1
|
my ($self, $param_class, $str) = @_; |
971
|
|
|
|
|
|
|
|
972
|
2
|
|
|
|
|
7
|
my $class_meta = $param_class->__meta__; |
973
|
2
|
|
|
|
|
7
|
my @id_property_names = $class_meta->id_property_names; |
974
|
2
|
50
|
|
|
|
10
|
if (@id_property_names == 0) { |
|
|
50
|
|
|
|
|
|
975
|
0
|
|
|
|
|
0
|
die "Failed to determine ID property names for class ($param_class)."; |
976
|
|
|
|
|
|
|
} elsif (@id_property_names == 1) { |
977
|
2
|
|
50
|
|
|
5
|
my $id_data_type = $class_meta->property_meta_for_name($id_property_names[0])->_data_type_as_class_name || ''; |
978
|
|
|
|
|
|
|
# Validate $str, if possible, to prevent warnings from database if $str does not fit column type. |
979
|
2
|
50
|
|
|
|
14
|
if ($id_data_type->isa('UR::Value::Number')) { # Oracle's Number data type includes floats but we just use integers for numeric IDs |
980
|
0
|
|
|
|
|
0
|
return ($str =~ /^[+-]?\d+$/); |
981
|
|
|
|
|
|
|
} |
982
|
|
|
|
|
|
|
} |
983
|
2
|
|
|
|
|
7
|
return 1; |
984
|
|
|
|
|
|
|
} |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
sub _resolve_param_value_from_text_by_name_or_id { |
987
|
2
|
|
|
2
|
|
3
|
my ($self, $param_class, $str) = @_; |
988
|
2
|
|
|
|
|
3
|
my (@results); |
989
|
2
|
50
|
|
|
|
7
|
if ($self->_try_get_by_id($param_class, $str)) { |
990
|
2
|
|
|
|
|
3
|
@results = eval { $param_class->get($str) }; |
|
2
|
|
|
|
|
7
|
|
991
|
|
|
|
|
|
|
} |
992
|
2
|
50
|
33
|
|
|
10
|
if (!@results && $param_class->can('name')) { |
993
|
2
|
|
|
|
|
18
|
@results = $param_class->get(name => $str); |
994
|
2
|
50
|
|
|
|
6
|
unless (@results) { |
995
|
0
|
|
|
|
|
0
|
@results = $param_class->get("name like" => "$str"); |
996
|
|
|
|
|
|
|
} |
997
|
|
|
|
|
|
|
} |
998
|
|
|
|
|
|
|
|
999
|
2
|
|
|
|
|
5
|
return @results; |
1000
|
|
|
|
|
|
|
} |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
sub _get_user_verification_for_param_value { |
1003
|
0
|
|
|
0
|
|
|
my ($self, $param_name, @list) = @_; |
1004
|
|
|
|
|
|
|
|
1005
|
0
|
|
|
|
|
|
my $n_list = scalar(@list); |
1006
|
0
|
0
|
0
|
|
|
|
if ($n_list > 200 && !$ENV{UR_NO_REQUIRE_USER_VERIFY}) { |
1007
|
0
|
|
|
|
|
|
my $response = $self->_ask_user_question("Would you [v]iew all $n_list item(s) for '$param_name', (p)roceed, or e(x)it?", 0, '[v]|p|x', 'v'); |
1008
|
0
|
0
|
0
|
|
|
|
if(!$response || $response eq 'x') { |
1009
|
0
|
|
|
|
|
|
$self->status_message("Exiting..."); |
1010
|
0
|
|
|
|
|
|
exit; |
1011
|
|
|
|
|
|
|
} |
1012
|
0
|
0
|
|
|
|
|
return @list if($response eq 'p'); |
1013
|
|
|
|
|
|
|
} |
1014
|
|
|
|
|
|
|
|
1015
|
0
|
|
|
|
|
|
my @new_list; |
1016
|
0
|
|
|
|
|
|
while (!@new_list) { |
1017
|
0
|
|
|
|
|
|
@new_list = $self->_get_user_verification_for_param_value_drilldown($param_name, @list); |
1018
|
|
|
|
|
|
|
} |
1019
|
|
|
|
|
|
|
|
1020
|
0
|
|
|
|
|
|
my @ids = map { $_->id } @new_list; |
|
0
|
|
|
|
|
|
|
1021
|
0
|
|
|
|
|
|
$self->status_message("The IDs for your selection are:\n" . join(',', @ids) . "\n\n"); |
1022
|
0
|
|
|
|
|
|
return @new_list; |
1023
|
|
|
|
|
|
|
} |
1024
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
sub _get_user_verification_for_param_value_drilldown { |
1026
|
0
|
|
|
0
|
|
|
my ($self, $param_name, @results) = @_; |
1027
|
0
|
|
|
|
|
|
my $n_results = scalar(@results); |
1028
|
0
|
|
|
|
|
|
my $pad = length($n_results); |
1029
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
# Allow an environment variable to be set to disable the require_user_verify attribute |
1031
|
0
|
0
|
|
|
|
|
return @results if ($ENV{UR_NO_REQUIRE_USER_VERIFY}); |
1032
|
0
|
0
|
|
|
|
|
return if (@results == 0); |
1033
|
|
|
|
|
|
|
|
1034
|
0
|
|
|
|
|
|
my @dnames = map {$_->__display_name__} grep { $_->can('__display_name__') } @results; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1035
|
0
|
0
|
|
|
|
|
my $max_dname_length = @dnames ? length((sort { length($b) <=> length($a) } @dnames)[0]) : 0; |
|
0
|
|
|
|
|
|
|
1036
|
0
|
0
|
|
|
|
|
my @statuses = map {$_->status || 'missing_status'} grep { $_->can('status') } @results; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1037
|
0
|
0
|
|
|
|
|
my $max_status_length = @statuses ? length((sort { length($b) <=> length($a) } @statuses)[0]) : 0; |
|
0
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
|
1039
|
0
|
|
|
|
|
|
my @results_with_display_name_and_class = map { [ $_->__display_name__, $_->class, $_ ] } @results; |
|
0
|
|
|
|
|
|
|
1040
|
0
|
|
|
|
|
|
@results = map { $_->[2] } |
1041
|
0
|
|
|
|
|
|
sort { $a->[1] cmp $b->[1] } |
1042
|
0
|
|
|
|
|
|
sort { $a->[0] cmp $b->[0] } |
|
0
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
@results_with_display_name_and_class; |
1044
|
|
|
|
|
|
|
|
1045
|
0
|
|
|
|
|
|
my @classes = List::MoreUtils::uniq(map {$_->class} @results); |
|
0
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
|
1047
|
0
|
|
|
|
|
|
my $response; |
1048
|
0
|
|
|
|
|
|
my @caller = caller(1); |
1049
|
0
|
|
|
|
|
|
while (!$response) { |
1050
|
0
|
|
|
|
|
|
$self->status_message("\n"); |
1051
|
|
|
|
|
|
|
# TODO: Replace this with lister? |
1052
|
0
|
|
|
|
|
|
for (my $i = 1; $i <= $n_results; $i++) { |
1053
|
0
|
|
|
|
|
|
my $param = $results[$i - 1]; |
1054
|
0
|
|
|
|
|
|
my $num = $self->_pad_string($i, $pad); |
1055
|
0
|
|
|
|
|
|
my $msg = "$num:"; |
1056
|
0
|
|
|
|
|
|
$msg .= ' ' . $self->_pad_string($param->__display_name__, $max_dname_length, 'suffix'); |
1057
|
0
|
|
|
|
|
|
my $status = ' '; |
1058
|
0
|
0
|
|
|
|
|
if ($param->can('status')) { |
1059
|
0
|
|
0
|
|
|
|
$status = $param->status || 'missing_status'; |
1060
|
|
|
|
|
|
|
} |
1061
|
0
|
|
|
|
|
|
$msg .= "\t" . $self->_pad_string($status, $max_status_length, 'suffix'); |
1062
|
0
|
0
|
|
|
|
|
$msg .= "\t" . $param->class if (@classes > 1); |
1063
|
0
|
|
|
|
|
|
$self->status_message($msg); |
1064
|
|
|
|
|
|
|
} |
1065
|
0
|
0
|
|
|
|
|
if ($MESSAGE) { |
1066
|
0
|
|
|
|
|
|
$MESSAGE = "\n" . '*'x80 . "\n" . $MESSAGE . "\n" . '*'x80 . "\n"; |
1067
|
0
|
|
|
|
|
|
$self->status_message($MESSAGE); |
1068
|
0
|
|
|
|
|
|
$MESSAGE = ''; |
1069
|
|
|
|
|
|
|
} |
1070
|
0
|
|
|
|
|
|
my $pretty_values = '(c)ontinue, (h)elp, e(x)it'; |
1071
|
0
|
|
|
|
|
|
my $valid_values = '\*|c|h|x|[-+]?[\d\-\., ]+'; |
1072
|
0
|
0
|
|
|
|
|
if ($caller[3] =~ /_trim_list_from_response/) { |
1073
|
0
|
|
|
|
|
|
$pretty_values .= ', (b)ack'; |
1074
|
0
|
|
|
|
|
|
$valid_values .= '|b'; |
1075
|
|
|
|
|
|
|
} |
1076
|
0
|
|
|
|
|
|
$response = $self->_ask_user_question("Please confirm the above items for '$param_name' or modify your selection.", 0, $valid_values, 'h', $pretty_values.', or specify item numbers to use'); |
1077
|
0
|
0
|
0
|
|
|
|
if (lc($response) eq 'h' || !$self->_validate_user_response_for_param_value_verification($response)) { |
1078
|
0
|
0
|
|
|
|
|
$MESSAGE .= "\n" if ($MESSAGE); |
1079
|
0
|
|
|
|
|
|
$MESSAGE .= |
1080
|
|
|
|
|
|
|
"Help:\n". |
1081
|
|
|
|
|
|
|
"* Specify which elements to keep by listing them, e.g. '1,3,12' would keep\n". |
1082
|
|
|
|
|
|
|
" items 1, 3, and 12.\n". |
1083
|
|
|
|
|
|
|
"* Begin list with a minus to remove elements, e.g. '-1,3,9' would remove\n". |
1084
|
|
|
|
|
|
|
" items 1, 3, and 9.\n". |
1085
|
|
|
|
|
|
|
"* Ranges can be used, e.g. '-11-17, 5' would remove items 11 through 17 and\n". |
1086
|
|
|
|
|
|
|
" remove item 5."; |
1087
|
0
|
|
|
|
|
|
$response = ''; |
1088
|
|
|
|
|
|
|
} |
1089
|
|
|
|
|
|
|
} |
1090
|
0
|
0
|
|
|
|
|
if (lc($response) eq 'x') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1091
|
0
|
|
|
|
|
|
$self->status_message("Exiting..."); |
1092
|
0
|
|
|
|
|
|
exit; |
1093
|
|
|
|
|
|
|
} |
1094
|
|
|
|
|
|
|
elsif (lc($response) eq 'b') { |
1095
|
0
|
|
|
|
|
|
return; |
1096
|
|
|
|
|
|
|
} |
1097
|
|
|
|
|
|
|
elsif (lc($response) eq 'c' | $response eq '*') { |
1098
|
0
|
|
|
|
|
|
return @results; |
1099
|
|
|
|
|
|
|
} |
1100
|
|
|
|
|
|
|
elsif ($response =~ /^[-+]?[\d\-\., ]+$/) { |
1101
|
0
|
|
|
|
|
|
@results = $self->_trim_list_from_response($response, $param_name, @results); |
1102
|
0
|
|
|
|
|
|
return @results; |
1103
|
|
|
|
|
|
|
} |
1104
|
|
|
|
|
|
|
else { |
1105
|
0
|
|
|
|
|
|
die $self->error_message("Conditional exception, should not have been reached!"); |
1106
|
|
|
|
|
|
|
} |
1107
|
|
|
|
|
|
|
} |
1108
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
sub terminal_input_filehandle { |
1110
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1111
|
|
|
|
|
|
|
|
1112
|
0
|
|
|
|
|
|
my $fh = IO::File->new('/dev/tty', 'r'); |
1113
|
0
|
0
|
|
|
|
|
unless ($fh) { |
1114
|
0
|
|
|
|
|
|
Carp::carp("Couldn't open /dev/tty for terminal input: $!\n Using STDIN..."); |
1115
|
0
|
|
|
|
|
|
$fh = *STDIN; |
1116
|
|
|
|
|
|
|
} |
1117
|
0
|
|
|
|
|
|
return $fh; |
1118
|
|
|
|
|
|
|
} |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
sub _ask_user_question { |
1121
|
0
|
|
|
0
|
|
|
my $self = shift; |
1122
|
0
|
|
|
|
|
|
my $question = shift; |
1123
|
0
|
|
|
|
|
|
my $timeout = shift; |
1124
|
0
|
|
0
|
|
|
|
my $valid_values = shift || "yes|no"; |
1125
|
0
|
|
0
|
|
|
|
my $default_value = shift || undef; |
1126
|
0
|
|
0
|
|
|
|
my $pretty_valid_values = shift || $valid_values; |
1127
|
0
|
|
|
|
|
|
$valid_values = lc($valid_values); |
1128
|
0
|
|
|
|
|
|
my $input; |
1129
|
0
|
0
|
|
|
|
|
$timeout = 60 unless(defined($timeout)); |
1130
|
|
|
|
|
|
|
|
1131
|
0
|
|
|
0
|
|
|
local $SIG{ALRM} = sub { print STDERR "Exiting, failed to reply to question '$question' within '$timeout' seconds.\n"; exit; }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1132
|
0
|
|
|
|
|
|
print STDERR "\n$question\n"; |
1133
|
0
|
|
|
|
|
|
print STDERR "Reply with $pretty_valid_values: "; |
1134
|
|
|
|
|
|
|
|
1135
|
0
|
0
|
|
|
|
|
unless ($self->_can_interact_with_user) { |
1136
|
0
|
|
|
|
|
|
print STDERR "\n"; |
1137
|
0
|
|
|
|
|
|
die $self->error_message("Attempting to ask user question but cannot interact with user!"); |
1138
|
|
|
|
|
|
|
} |
1139
|
|
|
|
|
|
|
|
1140
|
0
|
|
|
|
|
|
my $terminal = $self->terminal_input_filehandle(); |
1141
|
|
|
|
|
|
|
|
1142
|
0
|
0
|
|
|
|
|
alarm($timeout) if ($timeout); |
1143
|
0
|
|
|
|
|
|
chomp($input = $terminal->getline()); |
1144
|
0
|
0
|
|
|
|
|
alarm(0) if ($timeout); |
1145
|
|
|
|
|
|
|
|
1146
|
0
|
|
|
|
|
|
print STDERR "\n"; |
1147
|
|
|
|
|
|
|
|
1148
|
0
|
0
|
|
|
|
|
if(lc($input) =~ /^$valid_values$/) { |
|
|
0
|
|
|
|
|
|
1149
|
0
|
|
|
|
|
|
return lc($input); |
1150
|
|
|
|
|
|
|
} |
1151
|
|
|
|
|
|
|
elsif ($default_value) { |
1152
|
0
|
|
|
|
|
|
return $default_value; |
1153
|
|
|
|
|
|
|
} |
1154
|
|
|
|
|
|
|
else { |
1155
|
0
|
|
|
|
|
|
$self->error_message("'$input' is an invalid answer to question '$question'\n\n"); |
1156
|
0
|
|
|
|
|
|
return; |
1157
|
|
|
|
|
|
|
} |
1158
|
|
|
|
|
|
|
} |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
sub _validate_user_response_for_param_value_verification { |
1161
|
0
|
|
|
0
|
|
|
my ($self, $response_text) = @_; |
1162
|
0
|
0
|
|
|
|
|
$response_text = substr($response_text, 1) if ($response_text =~ /^[+-]/); |
1163
|
0
|
|
|
|
|
|
my @response = split(/[\s\,]/, $response_text); |
1164
|
0
|
|
|
|
|
|
for my $response (@response) { |
1165
|
0
|
0
|
|
|
|
|
if ($response =~ /^[xbc*]$/) { |
1166
|
0
|
|
|
|
|
|
return 1; |
1167
|
|
|
|
|
|
|
} |
1168
|
0
|
0
|
|
|
|
|
if ($response !~ /^(\d+)([-\.]+(\d+))?$/) { |
1169
|
0
|
0
|
|
|
|
|
$MESSAGE .= "\n" if ($MESSAGE); |
1170
|
0
|
|
|
|
|
|
$MESSAGE .= "ERROR: Invalid list provided ($response)"; |
1171
|
0
|
|
|
|
|
|
return 0; |
1172
|
|
|
|
|
|
|
} |
1173
|
0
|
0
|
0
|
|
|
|
if ($3 && $1 && $3 < $1) { |
|
|
|
0
|
|
|
|
|
1174
|
0
|
0
|
|
|
|
|
$MESSAGE .= "\n" if ($MESSAGE); |
1175
|
0
|
|
|
|
|
|
$MESSAGE .= "ERROR: Inverted range provided ($1-$3)"; |
1176
|
0
|
|
|
|
|
|
return 0; |
1177
|
|
|
|
|
|
|
} |
1178
|
|
|
|
|
|
|
} |
1179
|
0
|
|
|
|
|
|
return 1; |
1180
|
|
|
|
|
|
|
} |
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
sub _trim_list_from_response { |
1183
|
0
|
|
|
0
|
|
|
my ($self, $response_text, $param_name, @list) = @_; |
1184
|
|
|
|
|
|
|
|
1185
|
0
|
|
|
|
|
|
my $method; |
1186
|
0
|
0
|
|
|
|
|
if ($response_text =~ /^[+-]/) { |
1187
|
0
|
|
|
|
|
|
$method = substr($response_text, 0, 1); |
1188
|
0
|
|
|
|
|
|
$response_text = substr($response_text, 1); |
1189
|
|
|
|
|
|
|
} |
1190
|
|
|
|
|
|
|
else { |
1191
|
0
|
|
|
|
|
|
$method = '+'; |
1192
|
|
|
|
|
|
|
} |
1193
|
|
|
|
|
|
|
|
1194
|
0
|
|
|
|
|
|
my @response = split(/[\s\,]/, $response_text); |
1195
|
0
|
|
|
|
|
|
my %indices; |
1196
|
0
|
0
|
|
|
|
|
@indices{0..$#list} = 0..$#list if ($method eq '-'); |
1197
|
|
|
|
|
|
|
|
1198
|
0
|
|
|
|
|
|
for my $response (@response) { |
1199
|
0
|
|
|
|
|
|
$response =~ /^(\d+)([-\.]+(\d+))?$/; |
1200
|
0
|
|
|
|
|
|
my $low = $1; $low--; |
|
0
|
|
|
|
|
|
|
1201
|
0
|
|
0
|
|
|
|
my $high = $3 || $1; $high--; |
|
0
|
|
|
|
|
|
|
1202
|
0
|
0
|
|
|
|
|
die if ($high < $low); |
1203
|
0
|
0
|
|
|
|
|
if ($method eq '+') { |
1204
|
0
|
|
|
|
|
|
@indices{$low..$high} = $low..$high; |
1205
|
|
|
|
|
|
|
} |
1206
|
|
|
|
|
|
|
else { |
1207
|
0
|
|
|
|
|
|
delete @indices{$low..$high}; |
1208
|
|
|
|
|
|
|
} |
1209
|
|
|
|
|
|
|
} |
1210
|
|
|
|
|
|
|
#$self->debug_message("Indices: " . join(',', sort(keys %indices))); |
1211
|
0
|
|
|
|
|
|
my @new_list = $self->_get_user_verification_for_param_value_drilldown($param_name, @list[sort keys %indices]); |
1212
|
0
|
0
|
|
|
|
|
unless (@new_list) { |
1213
|
0
|
|
|
|
|
|
@new_list = $self->_get_user_verification_for_param_value_drilldown($param_name, @list); |
1214
|
|
|
|
|
|
|
} |
1215
|
0
|
|
|
|
|
|
return @new_list; |
1216
|
|
|
|
|
|
|
} |
1217
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
sub _pad_string { |
1219
|
0
|
|
|
0
|
|
|
my ($self, $str, $width, $pos) = @_; |
1220
|
0
|
0
|
|
|
|
|
$str = '' if ! defined $str; |
1221
|
0
|
|
|
|
|
|
my $padding = $width - length($str); |
1222
|
0
|
0
|
|
|
|
|
$padding = 0 if ($padding < 0); |
1223
|
0
|
0
|
0
|
|
|
|
if ($pos && $pos eq 'suffix') { |
1224
|
0
|
|
|
|
|
|
return $str . ' 'x$padding; |
1225
|
|
|
|
|
|
|
} |
1226
|
|
|
|
|
|
|
else { |
1227
|
0
|
|
|
|
|
|
return ' 'x$padding . $str; |
1228
|
|
|
|
|
|
|
} |
1229
|
|
|
|
|
|
|
} |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
sub _can_interact_with_user { |
1232
|
0
|
|
|
0
|
|
|
my $self = shift; |
1233
|
0
|
0
|
|
|
|
|
if ( -t STDERR ) { |
1234
|
0
|
|
|
|
|
|
return 1; |
1235
|
|
|
|
|
|
|
} |
1236
|
|
|
|
|
|
|
else { |
1237
|
0
|
|
|
|
|
|
return 0; |
1238
|
|
|
|
|
|
|
} |
1239
|
|
|
|
|
|
|
} |
1240
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
1; |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
|