CfgLine.pm 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. # ------------------------------------------------------------------------------
  2. # NAME
  3. # Fcm::CfgLine
  4. #
  5. # DESCRIPTION
  6. # This class is used for grouping the settings in each line of a FCM
  7. # configuration file.
  8. #
  9. # COPYRIGHT
  10. # (C) Crown copyright Met Office. All rights reserved.
  11. # For further details please refer to the file COPYRIGHT.txt
  12. # which you should have received as part of this distribution.
  13. # ------------------------------------------------------------------------------
  14. package Fcm::CfgLine;
  15. @ISA = qw(Fcm::Base);
  16. # Standard pragma
  17. use warnings;
  18. use strict;
  19. # Standard modules
  20. use File::Basename;
  21. # In-house modules
  22. use Fcm::Base;
  23. use Fcm::Config;
  24. use Fcm::Util;
  25. # List of property methods for this class
  26. my @scalar_properties = (
  27. 'bvalue', # line value, in boolean
  28. 'comment', # (in)line comment
  29. 'error', # error message for incorrect usage while parsing the line
  30. 'label', # line label
  31. 'line', # content of the line
  32. 'number', # line number in source file
  33. 'parsed', # has this line been parsed (by the extract/build system)?
  34. 'prefix', # optional prefix for line label
  35. 'slabel', # label without the optional prefix
  36. 'src', # name of source file
  37. 'value', # line value
  38. 'warning', # warning message for deprecated usage
  39. );
  40. # Useful variables
  41. our $COMMENT_RULER = '-' x 78;
  42. # ------------------------------------------------------------------------------
  43. # SYNOPSIS
  44. # @cfglines = Fcm::CfgLine->comment_block (@comment);
  45. #
  46. # DESCRIPTION
  47. # This method returns a list of Fcm::CfgLine objects representing a comment
  48. # block with the comment string @comment.
  49. # ------------------------------------------------------------------------------
  50. sub comment_block {
  51. my @return = (
  52. Fcm::CfgLine->new (comment => $COMMENT_RULER),
  53. (map {Fcm::CfgLine->new (comment => $_)} @_),
  54. Fcm::CfgLine->new (comment => $COMMENT_RULER),
  55. Fcm::CfgLine->new (),
  56. );
  57. return @return;
  58. }
  59. # ------------------------------------------------------------------------------
  60. # SYNOPSIS
  61. # $obj = Fcm::CfgLine->new (%args);
  62. #
  63. # DESCRIPTION
  64. # This method constructs a new instance of the Fcm::CfgLine class. See above
  65. # for allowed list of properties. (KEYS should be in uppercase.)
  66. # ------------------------------------------------------------------------------
  67. sub new {
  68. my $this = shift;
  69. my %args = @_;
  70. my $class = ref $this || $this;
  71. my $self = Fcm::Base->new (%args);
  72. for (@scalar_properties) {
  73. $self->{$_} = exists $args{uc ($_)} ? $args{uc ($_)} : undef;
  74. $self->{$_} = $args{$_} if exists $args{$_};
  75. }
  76. bless $self, $class;
  77. return $self;
  78. }
  79. # ------------------------------------------------------------------------------
  80. # SYNOPSIS
  81. # $value = $obj->X;
  82. # $obj->X ($value);
  83. #
  84. # DESCRIPTION
  85. # Details of these properties are explained in @scalar_properties.
  86. # ------------------------------------------------------------------------------
  87. for my $name (@scalar_properties) {
  88. no strict 'refs';
  89. *$name = sub {
  90. my $self = shift;
  91. if (@_) {
  92. $self->{$name} = $_[0];
  93. if ($name eq 'line' or $name eq 'label') {
  94. $self->{slabel} = undef;
  95. } elsif ($name eq 'line' or $name eq 'value') {
  96. $self->{bvalue} = undef;
  97. }
  98. }
  99. # Default value for property
  100. if (not defined $self->{$name}) {
  101. if ($name =~ /^(?:comment|error|label|line|prefix|src|value)$/) {
  102. # Blank
  103. $self->{$name} = '';
  104. } elsif ($name eq 'slabel') {
  105. if ($self->prefix and $self->label_starts_with ($self->prefix)) {
  106. $self->{$name} = $self->label_from_field (1);
  107. } else {
  108. $self->{$name} = $self->label;
  109. }
  110. } elsif ($name eq 'bvalue') {
  111. if (defined ($self->value)) {
  112. $self->{$name} = ($self->value =~ /^(\s*|false|no|off|0*)$/i)
  113. ? 0 : $self->value;
  114. }
  115. }
  116. }
  117. return $self->{$name};
  118. }
  119. }
  120. # ------------------------------------------------------------------------------
  121. # SYNOPSIS
  122. # @fields = $obj->label_fields ();
  123. # @fields = $obj->slabel_fields ();
  124. #
  125. # DESCRIPTION
  126. # These method returns a list of fields in the (s)label.
  127. # ------------------------------------------------------------------------------
  128. for my $name (qw/label slabel/) {
  129. no strict 'refs';
  130. my $sub_name = $name . '_fields';
  131. *$sub_name = sub {
  132. return (split (/$Fcm::Config::DELIMITER_PATTERN/, $_[0]->$name));
  133. }
  134. }
  135. # ------------------------------------------------------------------------------
  136. # SYNOPSIS
  137. # $string = $obj->label_from_field ($index);
  138. # $string = $obj->slabel_from_field ($index);
  139. #
  140. # DESCRIPTION
  141. # These method returns the (s)label from field $index onwards.
  142. # ------------------------------------------------------------------------------
  143. for my $name (qw/label slabel/) {
  144. no strict 'refs';
  145. my $sub_name = $name . '_from_field';
  146. *$sub_name = sub {
  147. my ($self, $index) = @_;
  148. my $method = $name . '_fields';
  149. my @fields = $self->$method;
  150. return join ($Fcm::Config::DELIMITER, @fields[$index .. $#fields]);
  151. }
  152. }
  153. # ------------------------------------------------------------------------------
  154. # SYNOPSIS
  155. # $flag = $obj->label_starts_with (@fields);
  156. # $flag = $obj->slabel_starts_with (@fields);
  157. #
  158. # DESCRIPTION
  159. # These method returns a true if (s)label starts with the labels in @fields
  160. # (ignore case).
  161. # ------------------------------------------------------------------------------
  162. for my $name (qw/label slabel/) {
  163. no strict 'refs';
  164. my $sub_name = $name . '_starts_with';
  165. *$sub_name = sub {
  166. my ($self, @fields) = @_;
  167. my $return = 1;
  168. my $method = $name . '_fields';
  169. my @all_fields = $self->$method;
  170. for my $i (0 .. $#fields) {
  171. next if lc ($fields[$i]) eq lc ($all_fields[$i] || '');
  172. $return = 0;
  173. last;
  174. }
  175. return $return;
  176. }
  177. }
  178. # ------------------------------------------------------------------------------
  179. # SYNOPSIS
  180. # $flag = $obj->label_starts_with_cfg (@fields);
  181. # $flag = $obj->slabel_starts_with_cfg (@fields);
  182. #
  183. # DESCRIPTION
  184. # These method returns a true if (s)label starts with the configuration file
  185. # labels in @fields (ignore case).
  186. # ------------------------------------------------------------------------------
  187. for my $name (qw/label slabel/) {
  188. no strict 'refs';
  189. my $sub_name = $name . '_starts_with_cfg';
  190. *$sub_name = sub {
  191. my ($self, @fields) = @_;
  192. for my $field (@fields) {
  193. $field = $self->cfglabel ($field);
  194. }
  195. my $method = $name . '_starts_with';
  196. return $self->$method (@fields);
  197. }
  198. }
  199. # ------------------------------------------------------------------------------
  200. # SYNOPSIS
  201. # $mesg = $obj->format_error ();
  202. #
  203. # DESCRIPTION
  204. # This method returns a string containing a formatted error message for
  205. # anything reported to the current line.
  206. # ------------------------------------------------------------------------------
  207. sub format_error {
  208. my ($self) = @_;
  209. my $mesg = '';
  210. $mesg .= $self->format_warning;
  211. if ($self->error or not $self->parsed) {
  212. $mesg = 'ERROR: ' . $self->src . ': LINE ' . $self->number . ':' . "\n";
  213. if ($self->error) {
  214. $mesg .= ' ' . $self->error;
  215. } else {
  216. $mesg .= ' ' . $self->label . ': label not recognised.';
  217. }
  218. }
  219. return $mesg;
  220. }
  221. # ------------------------------------------------------------------------------
  222. # SYNOPSIS
  223. # $mesg = $obj->format_warning ();
  224. #
  225. # DESCRIPTION
  226. # This method returns a string containing a formatted warning message for
  227. # any warning reported to the current line.
  228. # ------------------------------------------------------------------------------
  229. sub format_warning {
  230. my ($self) = @_;
  231. my $mesg = '';
  232. if ($self->warning) {
  233. $mesg = 'WARNING: ' . $self->src . ': LINE ' . $self->number . ':' . "\n";
  234. $mesg .= ' ' . $self->warning;
  235. }
  236. return $mesg;
  237. }
  238. # ------------------------------------------------------------------------------
  239. # SYNOPSIS
  240. # $line = $obj->print_line ([$space]);
  241. #
  242. # DESCRIPTION
  243. # This method returns a configuration line using $self->label, $self->value
  244. # and $self->comment. The value in $self->line is re-set. If $space is set
  245. # and is a positive integer, it sets the spacing between the label and the
  246. # value in the line. The default is 1.
  247. # ------------------------------------------------------------------------------
  248. sub print_line {
  249. my ($self, $space) = @_;
  250. # Set space between label and value, default to 1 character
  251. $space = 1 unless $space and $space =~ /^[1-9]\d*$/;
  252. my $line = '';
  253. # Add label and value, if label is set
  254. if ($self->label) {
  255. $line .= $self->label . ' ' x $space;
  256. $line .= $self->value if defined $self->value;
  257. }
  258. # Add comment if necessary
  259. my $comment = $self->comment;
  260. $comment =~ s/^\s*//;
  261. if ($comment) {
  262. $comment = '# ' . $comment if $comment !~ /^#/;
  263. $line .= ' ' if $line;
  264. $line .= $comment;
  265. }
  266. return $self->line ($line);
  267. }
  268. # ------------------------------------------------------------------------------
  269. 1;
  270. __END__