1 | #!/usr/bin/perl
|
---|
2 |
|
---|
3 | use strict;
|
---|
4 | use warnings;
|
---|
5 | use feature 'unicode_strings';
|
---|
6 | use utf8;
|
---|
7 |
|
---|
8 | use Getopt::Long;
|
---|
9 | use XML::DOM;
|
---|
10 |
|
---|
11 | my $mod_root = '../../../binaries/data/mods';
|
---|
12 |
|
---|
13 | my $mod = 'public';
|
---|
14 | my $key;
|
---|
15 | my $change;
|
---|
16 | my $only_print;
|
---|
17 |
|
---|
18 | GetOptions('mod|m=s' => \$mod, 'key|k=s' => \$key, 'change|c=i' => \$change, 'print|p' => \$only_print);
|
---|
19 |
|
---|
20 | my @classes = @ARGV;
|
---|
21 |
|
---|
22 |
|
---|
23 | sub get_filename
|
---|
24 | {
|
---|
25 | my ($mod, $template_name) = @_;
|
---|
26 | $template_name =~ s|^/||; # Remove a leading /
|
---|
27 | return "$mod_root/$mod/simulation/templates/$template_name";
|
---|
28 | }
|
---|
29 |
|
---|
30 | sub array_contains_array
|
---|
31 | {
|
---|
32 | # Check if the first array argument contains all the elements of the second one.
|
---|
33 | # There's probably a more efficient way to do this.
|
---|
34 | my ($array1, $array2) = @_;
|
---|
35 | my @array1 = @$array1;
|
---|
36 | my @array2 = @$array2;
|
---|
37 |
|
---|
38 | foreach my $element (@array2) {
|
---|
39 | if (!grep(/^$element$/, @array1)) {
|
---|
40 | return 0;
|
---|
41 | }
|
---|
42 | }
|
---|
43 | return 1;
|
---|
44 | }
|
---|
45 |
|
---|
46 | sub minimum_one_decimal
|
---|
47 | {
|
---|
48 | # Ensure $num has at least one decimal place.
|
---|
49 | # This is because values in templates always have a decimal place.
|
---|
50 | my ($num) = @_;
|
---|
51 | if ($num == int($num)) {
|
---|
52 | return sprintf('%.01f', $num);
|
---|
53 | }
|
---|
54 | return sprintf('%g\n', $num);
|
---|
55 | }
|
---|
56 |
|
---|
57 | sub get_templates
|
---|
58 | {
|
---|
59 | my ($mod, $subdir) = @_;
|
---|
60 | $subdir = $subdir || '';
|
---|
61 | opendir my $template_dir, "$mod_root/$mod/simulation/templates/$subdir";
|
---|
62 | my @templates = readdir $template_dir;
|
---|
63 | closedir $template_dir;
|
---|
64 | return map { "$subdir/$_"; } grep(/^[^.]/, @templates); # Remove directories that start with a dot.
|
---|
65 | }
|
---|
66 |
|
---|
67 | sub get_template_classes
|
---|
68 | {
|
---|
69 | my ($parser, $template_file) = @_;
|
---|
70 | my @classes = ();
|
---|
71 | my $template = $parser->parsefile($template_file);
|
---|
72 | my $entity = $template->getDocumentElement;
|
---|
73 |
|
---|
74 | # Merge this template's classes with all its inherited classes.
|
---|
75 | my $parent = $entity->getAttributeNode('parent');
|
---|
76 | if ($parent) {
|
---|
77 | @classes = get_template_classes($parser, get_filename($mod, $parent->getNodeValue) . '.xml');
|
---|
78 | }
|
---|
79 |
|
---|
80 | my $identity = $entity->getElementsByTagName('Identity')->item(0);
|
---|
81 | if ($identity) {
|
---|
82 | my $class_tokens = $identity->getElementsByTagName('Classes')->item(0);
|
---|
83 | if ($class_tokens) {
|
---|
84 | push @classes, split(' ', $class_tokens->getFirstChild->getNodeValue); # The firstChild is the text node.
|
---|
85 | }
|
---|
86 | }
|
---|
87 |
|
---|
88 | $template->dispose;
|
---|
89 | return @classes;
|
---|
90 | }
|
---|
91 |
|
---|
92 | sub matching_templates
|
---|
93 | {
|
---|
94 | my ($mod, $templates, $desired_classes) = @_;
|
---|
95 | my @templates = @$templates;
|
---|
96 | my @desired_classes = @$desired_classes;
|
---|
97 | my @matching;
|
---|
98 | my $parser = new XML::DOM::Parser;
|
---|
99 |
|
---|
100 | foreach my $template_name (@templates) {
|
---|
101 | my $template_file = get_filename($mod, $template_name);
|
---|
102 | if (-d $template_file) {
|
---|
103 | my @more_templates = get_templates($mod, $template_name);
|
---|
104 | push @matching, matching_templates($mod, \@more_templates, \@desired_classes);
|
---|
105 | }
|
---|
106 | else {
|
---|
107 | my @classes = get_template_classes($parser, $template_file);
|
---|
108 | if (array_contains_array(\@classes, \@desired_classes)) {
|
---|
109 | push @matching, $template_file;
|
---|
110 | }
|
---|
111 | }
|
---|
112 | }
|
---|
113 | return @matching;
|
---|
114 | }
|
---|
115 |
|
---|
116 | my @all_templates = get_templates($mod);
|
---|
117 | my @matching_files = matching_templates($mod, \@all_templates, \@classes);
|
---|
118 |
|
---|
119 | print join("\n", map { substr $_, 56; } @matching_files), "\n"; # 56 is the length of $mod_root/simulation/templates/
|
---|
120 | exit if $only_print;
|
---|
121 |
|
---|
122 | my $parser = new XML::DOM::Parser;
|
---|
123 | foreach my $template_file (@matching_files) {
|
---|
124 | my $template = $parser->parsefile($template_file);
|
---|
125 | my @query = split '/', $key;
|
---|
126 | my @elements = $template->getElementsByTagName(shift @query);
|
---|
127 | foreach my $element_name (@query) {
|
---|
128 | @elements = map { $_->getElementsByTagName($element_name); } @elements;
|
---|
129 | }
|
---|
130 |
|
---|
131 | foreach my $element (@elements) {
|
---|
132 | my $value = $element->getFirstChild->getNodeValue;
|
---|
133 | $element->getFirstChild->setNodeValue(minimum_one_decimal $value + $change);
|
---|
134 | my $nv = $element->getFirstChild->getNodeValue;
|
---|
135 | }
|
---|
136 |
|
---|
137 | # Can't use printToFile because it messes up Unicode characters.
|
---|
138 | open(my $output_file, '>:utf8', $template_file);
|
---|
139 | print $output_file $template->toString;
|
---|
140 | $template->dispose;
|
---|
141 | }
|
---|
142 |
|
---|
143 | print scalar(@matching_files), " files matched (fewer were actually modified).\n";
|
---|