tools/CommandInfoFile.pm
changeset 0 7f656887cf89
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 #!perl
       
     2 # CommandInfoFile.pm
       
     3 # 
       
     4 # Copyright (c) 2010 Accenture. All rights reserved.
       
     5 # This component and the accompanying materials are made available
       
     6 # under the terms of the "Eclipse Public License v1.0"
       
     7 # which accompanies this distribution, and is available
       
     8 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     9 # 
       
    10 # Initial Contributors:
       
    11 # Accenture - Initial contribution
       
    12 #
       
    13 
       
    14 # Description:
       
    15 # A Perl implementation of the C++ class CCommandInfoFile (in ioutils.dll).
       
    16 
       
    17 # Note, this code is intended to behave the same as the C++ version. It therefore used C++ like ways
       
    18 # doing things rather than Perl-like ways to keep the code as similar are possible.
       
    19 
       
    20 package CommandInfoFile;
       
    21 
       
    22 use strict;
       
    23 use IO::File;
       
    24 use File::Basename;
       
    25 
       
    26 
       
    27 #
       
    28 # Public Interface.
       
    29 #
       
    30 
       
    31 sub New {
       
    32   my $pkg = shift;
       
    33   my $fileName = shift;
       
    34   my $self = _Construct($pkg);
       
    35   $self->_ReadFile($fileName);
       
    36   return $self;
       
    37 }
       
    38 
       
    39 sub Name($) {
       
    40   my $self = shift;
       
    41   return $self->{name};
       
    42 }
       
    43 
       
    44 sub FullName($) {
       
    45   my $self = shift;
       
    46   my $name = shift;
       
    47 
       
    48   if (defined $name) {
       
    49     $name = "$self->{name} $name";
       
    50   }
       
    51   else {
       
    52     $name = $self->{name};
       
    53   }
       
    54 
       
    55   if ($self->{parent}) {
       
    56     $name = $self->{parent}->FullName($name);
       
    57   }
       
    58 
       
    59   return $name;
       
    60 }
       
    61 
       
    62 sub ShortDescription($) {
       
    63   my $self = shift;
       
    64   return $self->{short_description};
       
    65 }
       
    66 
       
    67 sub LongDescription($) {
       
    68   my $self = shift;
       
    69   return $self->{long_description};
       
    70 }
       
    71 
       
    72 sub SeeAlso($) {
       
    73   my $self = shift;
       
    74   return $self->{see_also};
       
    75 }
       
    76 
       
    77 sub Copyright($) {
       
    78   my $self = shift;
       
    79   return $self->{copyright};
       
    80 }
       
    81 
       
    82 sub Arguments($) {
       
    83   my $self = shift;
       
    84   return $self->{arguments};
       
    85 }
       
    86 
       
    87 sub Options($) {
       
    88   my $self = shift;
       
    89   return $self->{options};
       
    90 }
       
    91 
       
    92 sub NumSubCommands($) {
       
    93   my $self = shift;
       
    94   my $numSubCommands = 0;
       
    95   if (defined $self->{sub_commands}) {
       
    96     $numSubCommands = scalar (@{$self->{sub_commands}});
       
    97   }
       
    98   return $numSubCommands;
       
    99 }
       
   100 
       
   101 sub SubCommand($$) {
       
   102   my $self = shift;
       
   103   my $index = shift;
       
   104   die unless (($index >= 0) and ($index < $self->NumSubCommands()));
       
   105   return $self->{sub_commands}->[$index];
       
   106 }
       
   107 
       
   108 
       
   109 #
       
   110 # Private.
       
   111 #
       
   112 
       
   113 sub _ReadFile($$) {
       
   114   my $self = shift;
       
   115   my $fileName = shift;
       
   116 
       
   117   my $file = IO::File->new($fileName) or die "Error: Couldn't open '$fileName' for reading: $!\n";
       
   118   my $pos = 0;
       
   119   my $fileLength = -s $fileName;
       
   120 
       
   121   while ($pos < $fileLength) {
       
   122     if ($self->{process_include} or not defined $self->{current_child}) {
       
   123       $self->_ReadDetails($file, $fileName);
       
   124     }
       
   125     else {
       
   126       $self->{current_child}->_ReadDetails($file, $fileName);
       
   127     }
       
   128     $pos = $file->tell();
       
   129   }
       
   130   close ($file);
       
   131 }
       
   132 
       
   133 sub _ReadDetails($$$) {
       
   134   my $self = shift;
       
   135   my $file = shift;
       
   136   my $fileName = shift;
       
   137 
       
   138   my $pos = $file->tell();
       
   139   TextToNextCommand($file); # Ignore everything before the first '==' command.
       
   140   while (my $line = <$file>) {
       
   141     if ($line =~ /^==name\s+(\S+)/) {
       
   142       $self->{name} = $1;
       
   143     }
       
   144     elsif ($line =~ /^==short-description\s*$/) {
       
   145       $self->{short_description} = TextToNextCommand($file);
       
   146     }
       
   147     elsif ($line =~ /^==long-description\s*$/) {
       
   148       $self->{long_description} = TextToNextCommand($file);
       
   149     }
       
   150     elsif ($line =~ /^==see-also\s*$/) {
       
   151       $self->{see_also} = TextToNextCommand($file);
       
   152     }
       
   153     elsif ($line =~ /^==copyright\s*$/) {
       
   154       $self->{copyright} = TextToNextCommand($file);
       
   155     }
       
   156     elsif ($line =~ /^==argument\s+(.*)$/) {
       
   157       push (@{$self->{arguments}}, ReadArgument($file, $1));
       
   158     }
       
   159     elsif ($line =~ /^==option\s+(.*)$/) {
       
   160       push (@{$self->{options}}, ReadOption($file, $1));
       
   161     }
       
   162     elsif ($line =~ /^==include\s+(.*)$/) {
       
   163       if (not exists $self->{parent}) {
       
   164 	$self->{process_include} = 0;
       
   165 	my $includeFileName = dirname($fileName) . "/$1";
       
   166 	$self->_ReadFile($includeFileName);
       
   167 	last;
       
   168       }
       
   169       else {
       
   170 	# We're a sub-command. Let control return to the root to handle the include.
       
   171 	$self->{parent}->_ProcessInclude($self);
       
   172 	$file->seek($pos, 0);
       
   173 	last;
       
   174       }
       
   175     }
       
   176     elsif ($line =~ /^==sub-command\s+(.*)$/) {
       
   177       if (not exists $self->{parent}) {
       
   178 	my @subCommandNames = split (/\s+/, $1);
       
   179 	$self->_AddSubCommand(\@subCommandNames, $file, $fileName);
       
   180       }
       
   181       else {
       
   182 	# We're a sub-command. Let control return to the root to handle the include.
       
   183 	$self->{parent}->_ProcessNewChild();
       
   184 	$file->seek($pos, 0);
       
   185 	last;
       
   186       }
       
   187     }
       
   188 
       
   189     $pos = $file->tell();
       
   190   }
       
   191 }
       
   192 
       
   193 sub _ProcessNewChild($) {
       
   194   my $self = shift;
       
   195 
       
   196   if ($self->{parent}) {
       
   197     $self->{parent}->_ProcessNewChild();
       
   198   }
       
   199   else {
       
   200     die if ($self->{process_include});
       
   201     undef $self->{current_child};
       
   202   }
       
   203 }
       
   204 
       
   205 sub _ProcessInclude($$) {
       
   206   my $self = shift;
       
   207   my $child = shift;
       
   208 
       
   209   if ($self->{parent}) {
       
   210     $self->{parent}->_ProcessInclude($child);
       
   211   }
       
   212   else {
       
   213     $self->{process_include} = 1;
       
   214     $self->{current_child} = $child;
       
   215   }
       
   216 }
       
   217 
       
   218 sub _AddSubCommand($$$$) {
       
   219   my $self = shift;
       
   220   my $subCommandNames = shift;
       
   221   my $file = shift;
       
   222   my $fileName = shift;
       
   223   my $subCommandName = shift @$subCommandNames;
       
   224 
       
   225   my $found = 0;
       
   226   for (my $i = ($self->NumSubCommands() - 1); $i >= 0; --$i) {
       
   227     if ($self->{sub_commands}->[$i]->{name} eq $subCommandName) {
       
   228       $self->{sub_commands}->[$i]->_AddSubCommand($subCommandNames, $file, $fileName);
       
   229       $found = 1;
       
   230       last;
       
   231     }
       
   232   }
       
   233 
       
   234   die unless ($found or (@$subCommandNames == 0));
       
   235 
       
   236   if (not $found) {
       
   237     my $newCif = _Construct('CommandInfoFile');
       
   238     $newCif->{name} = $subCommandName;
       
   239     $newCif->{parent} = $self;
       
   240     $newCif->_ReadDetails($file, $fileName);
       
   241     push (@{$self->{sub_commands}}, $newCif);
       
   242   }
       
   243 }
       
   244 
       
   245 sub _Construct($) {
       
   246   my $pkg = shift;
       
   247   my $self = {};
       
   248   bless $self, $pkg;
       
   249 
       
   250   push (@{$self->{options}}, {
       
   251 			      type => 'bool',
       
   252 			      short_name => 'h',
       
   253 			      long_name => 'help',
       
   254 			      description => 'Display help.'
       
   255 			     });
       
   256 
       
   257   return $self;
       
   258 }
       
   259 
       
   260 sub ReadArgument($$) {
       
   261   my $file = shift;
       
   262   my @args = split (/\s+/, shift);
       
   263 
       
   264   my $argumentEntry = {};
       
   265 
       
   266   $argumentEntry->{type} = shift @args;
       
   267   $argumentEntry->{name} = shift @args;
       
   268 
       
   269   foreach my $arg (@args) {
       
   270     if ($arg eq 'optional') {
       
   271       $argumentEntry->{flags}->{optional} = 1;
       
   272     }
       
   273     elsif ($arg eq 'multiple') {
       
   274       $argumentEntry->{flags}->{multiple} = 1;
       
   275     }
       
   276     elsif ($arg eq 'last') {
       
   277       $argumentEntry->{flags}->{last} = 1;
       
   278     }
       
   279     else {
       
   280       $argumentEntry->{env_var} = $arg;
       
   281     }
       
   282   }
       
   283 
       
   284   die "Error: Argument missing type\n" unless defined $argumentEntry->{type};
       
   285   die "Error: Argument missing name\n" unless defined $argumentEntry->{name};
       
   286 
       
   287   $argumentEntry->{description} = TextToNextCommand($file);
       
   288   $argumentEntry->{description} =~ s/\s*$//;
       
   289 
       
   290   if ($argumentEntry->{type} eq 'enum') {
       
   291     $argumentEntry->{enum_values} = GetEnumValues($file);
       
   292   }
       
   293 
       
   294   return $argumentEntry;
       
   295 }
       
   296 
       
   297 sub ReadOption($$) {
       
   298   my $file = shift;
       
   299   my @args = split (/\s+/, shift);
       
   300 
       
   301   my $optionEntry = {};
       
   302 
       
   303   $optionEntry->{type} = shift @args;
       
   304   $optionEntry->{short_name} = shift @args;
       
   305   $optionEntry->{long_name} = shift @args;
       
   306 
       
   307   foreach my $arg (@args) {
       
   308     if ($arg eq 'multiple') {
       
   309       $optionEntry->{flags}->{multiple} = 1;
       
   310     }
       
   311     else {
       
   312       $optionEntry->{env_var} = $arg;
       
   313     }
       
   314   }
       
   315 
       
   316   die "Error: Option missing type\n" unless defined $optionEntry->{type};
       
   317   die "Error: Option missing short name\n" unless defined $optionEntry->{short_name};
       
   318   die "Error: Option short name not a single character\n" unless length ($optionEntry->{short_name}) == 1;
       
   319   die "Error: Option missing long name\n" unless defined $optionEntry->{long_name};
       
   320 
       
   321   $optionEntry->{description} = TextToNextCommand($file);
       
   322 
       
   323   if ($optionEntry->{type} eq 'enum') {
       
   324     $optionEntry->{enum_values} = GetEnumValues($file);
       
   325   }
       
   326 
       
   327   return $optionEntry;
       
   328 }
       
   329 
       
   330 sub GetEnumValues($) {
       
   331   my $file = shift;
       
   332 
       
   333   my @values;
       
   334 
       
   335   my $pos = $file->tell();
       
   336   while (my $line = <$file>) {
       
   337     if ($line =~ /^==enum-value\s+(\S+)/) {
       
   338       my $value = $1;
       
   339       my $description = TextToNextCommand($file);
       
   340       push (@values, {
       
   341 		      value => $value,
       
   342 		      description => $description
       
   343 		     });
       
   344     }
       
   345     else {
       
   346       $file->seek ($pos, 0);
       
   347       last;
       
   348     }
       
   349     $pos = $file->tell();
       
   350   }
       
   351 
       
   352   return \@values;
       
   353 }
       
   354 
       
   355 sub TextToNextCommand($) {
       
   356   my $file = shift;
       
   357   my $text = '';
       
   358   my $pos = $file->tell();
       
   359   while (my $line = <$file>) {
       
   360     if ($line =~ /^==/) {
       
   361       $file->seek($pos, 0);
       
   362       last;
       
   363     }
       
   364     else {
       
   365       $text .= $line;
       
   366     }
       
   367     $pos = $file->tell();
       
   368   }
       
   369   return $text;
       
   370 }
       
   371 
       
   372 1;