       1 package FileIO::FingerprintsFPFileIO;
       2 #
       3 # $RCSfile: FingerprintsFPFileIO.pm,v $
       4 # $Date: 2015/02/28 20:48:43 $
       5 # $Revision: 1.19 $
       6 #
       7 # Author: Manish Sud <msud@san.rr.com>
       8 #
       9 # Copyright (C) 2015 Manish Sud. All rights reserved.
      10 #
      11 # This file is part of MayaChemTools.
      12 #
      13 # MayaChemTools is free software; you can redistribute it and/or modify it under
      14 # the terms of the GNU Lesser General Public License as published by the Free
      15 # Software Foundation; either version 3 of the License, or (at your option) any
      16 # later version.
      17 #
      18 # MayaChemTools is distributed in the hope that it will be useful, but without
      19 # any warranty; without even the implied warranty of merchantability of fitness
      20 # for a particular purpose.  See the GNU Lesser General Public License for more
      21 # details.
      22 #
      23 # You should have received a copy of the GNU Lesser General Public License
      24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
      25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
      26 # Boston, MA, 02111-1307, USA.
      27 #
      28
      29 use strict;
      30 use Carp;
      31 use Exporter;
      32 use Scalar::Util ();
      33 use TextUtil ();
      34 use FileUtil ();
      35 use TimeUtil ();
      36 use Fingerprints::FingerprintsStringUtil ();
      37 use PackageInfo ();
      38 use FileIO::FileIO;
      39
      40 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
      41
      42 @ISA = qw(FileIO::FileIO Exporter);
      43 @EXPORT = qw();
      44 @EXPORT_OK = qw(IsFingerprintsFPFile);
      45
      46 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
      47
      48 # Setup class variables...
      49 my($ClassName);
      50 _InitializeClass();
      51
      52 # Class constructor...
      53 sub new {
      54   my($Class, %NamesAndValues) = @_;
      55
      56   # Initialize object...
      57   my $This = $Class->SUPER::new();
      58   bless $This, ref($Class) || $Class;
      59   $This->_InitializeFingerprintsFPFileIO();
      60
      61   $This->_InitializeFingerprintsFPFileIOProperties(%NamesAndValues);
      62
      63   return $This;
      64 }
      65
      66 # Initialize object data...
      67 #
      68 sub _InitializeFingerprintsFPFileIO {
      69   my($This) = @_;
      70
      71   # Fingerprints string data format during read/write...
      72   #
      73   # For file read:
      74   #
      75   # AutoDetect  - automatically detect format of fingerprints string
      76   # FingerprintsBitVectorString - Bit vector fingerprints string format
      77   # FingerprintsVectorString - Vector fingerprints string format
      78   #
      79   # Default value: AutoDetect
      80   #
      81   # For file write:
      82   #
      83   # FingerprintsBitVectorString - Bit vector fingerprints string format
      84   # FingerprintsVectorString - Vector fingerprints string format
      85   #
      86   # Default value: undef
      87   #
      88   $This->{FingerprintsStringMode} = undef;
      89
      90   # For file read:
      91   #
      92   #   o Fingerprints bit-vector and vector object for current fingerprints string
      93   #
      94   # For file write:
      95   #
      96   #   o Fingerprints bit-vector and vector object for current fingerprints string
      97   #   o Any supported fingerprints object: PathLengthFingerprints, ExtendedConnectivity, and so on.
      98   #
      99   $This->{FingerprintsObject} = undef;
     100
     101   # Fingeprints string for current line during read/write...
     102   $This->{FingerprintsString} = undef;
     103
     104   # Partial fingeprints string corresponding to what's on the current line for current
     105   # line during read/write...
     106   $This->{PartialFingerprintsString} = undef;
     107
     108   # Required header data keys and values during read/write...
     109   @{$This->{RequiredHeaderDataKeys}} = ();
     110   %{$This->{RequiredHeaderDataKeysAndValues}} = ();
     111
     112   # First data line read/write...
     113   $This->{FirstDataLineIO} = 1;
     114
     115   # Current fingerprints string data line number during read/write...
     116   $This->{LineNum} = 0;
     117
     118   # FP line data during read/write...
     119   $This->{DataLine} = undef;
     120
     121   # Initialize parameters for read...
     122   $This->_InitializeFingerprintsFPFileIORead();
     123
     124   # Initialize parameters for write...
     125   $This->_InitializeFingerprintsFPFileIOWrite();
     126
     127   return $This;
     128 }
     129
     130 # Initialize class ...
     131 sub _InitializeClass {
     132   #Class name...
     133   $ClassName = __PACKAGE__;
     134
     135 }
     136
     137 # Initialize object data for reading fingerprints FP file...
     138 #
     139 sub _InitializeFingerprintsFPFileIORead {
     140   my($This) = @_;
     141
     142   # Header data keys and values...
     143   #
     144   @{$This->{HeaderDataKeys}} = ();
     145   %{$This->{HeaderDataKeysAndValues}} = ();
     146   %{$This->{CannonicalHeaderDataKeysAndValues}} = ();
     147
     148   # By default, the fingerprints data is assumed to be valid and no validation is
     149   # performed before generating fingerprints objects...
     150   #
     151   $This->{ValidateData} = 1;
     152
     153   # Level of detail to print during validation of data for invalid or missing data...
     154   $This->{DetailLevel} = 1;
     155
     156   # Number of missing and invalid fingerprints string data lines...
     157   $This->{NumOfLinesWithMissingData} = 0;
     158   $This->{NumOfLinesWithInvalidData} = 0;
     159
     160   # Compound ID for current fingerprints string...
     161   $This->{CompoundID} = undef;
     162
     163   # Status of data in fingerprints FP file...
     164   $This->{ValidFileData} = 0;
     165   $This->{ValidRequiredHeaderDataKeys} = 0;
     166   $This->{ValidFingerprintsStringMode} = 0;
     167
     168   return $This;
     169 }
     170
     171 # Initialize object data for writing fingerprints FP file...
     172 #
     173 sub _InitializeFingerprintsFPFileIOWrite {
     174   my($This) = @_;
     175
     176   # Fingerprints bit vector string format...
     177   #
     178   # Possible values: BinaryString or HexadecimalString [Default]
     179   #
     180   # Default BitStringFormat is set during first write using Fingerprints::FingerprintsStringUtil::GetDefaultBitStringFormat.
     181   #
     182   $This->{BitStringFormat} = undef;
     183
     184   # Bits order in fingerprints bit vector string...
     185   #
     186   # Ascending - First bit in each byte as the lowest bit [Default]
     187   # Descending - First bit in each byte as the highest bit
     188   #
     189   # Default BitsOrder is set during first write using Fingerprints::FingerprintsStringUtil::GetDefaultBitsOrder.
     190   #
     191   $This->{BitsOrder} = undef;
     192
     193   # Fingerprints vector string format...
     194   #
     195   # Possible values: IDsAndValuesString, IDsAndValuesPairsString, ValuesAndIDsString, ValuesAndIDsPairsString, ValuesString
     196   #
     197   # Default VectorStringFormat is set during first write using Fingerprints::FingerprintsStringUtil::GetDefaultVectorStringFormat.
     198   # For fingerprints vector object containing vector NumericalValues, it corresponds to IDsAndValuesString; otherwise,
     199   # it's set to ValuesString.
     200   #
     201   $This->{VectorStringFormat} = undef;
     202
     203   # Overwriting existing file...
     204   $This->{Overwrite} = 0;
     205
     206   return $This;
     207 }
     208
     209 # Initialize object values...
     210 sub _InitializeFingerprintsFPFileIOProperties {
     211   my($This, %NamesAndValues) = @_;
     212
     213   # All other property names and values along with all Set/Get<PropertyName> methods
     214   # are implemented on-demand using ObjectProperty class.
     215
     216   my($Name, $Value, $MethodName);
     217   while (($Name, $Value) = each  %NamesAndValues) {
     218     $MethodName = "Set${Name}";
     219     $This->$MethodName($Value);
     220   }
     221
     222   if (!exists $NamesAndValues{Name}) {
     223     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying file name...";
     224   }
     225
     226   # Make sure it's a fingerprints file...
     227   $Name = $NamesAndValues{Name};
     228   if (!$This->IsFingerprintsFPFile($Name)) {
     229     croak "Error: ${ClassName}->New: Object can't be instantiated: File, $Name, doesn't appear to be fingerprints format...";
     230   }
     231
     232   if ($This->GetMode() =~ /^Read$/i) {
     233     $This->_InitializeFingerprintsFPFileIOReadProperties(%NamesAndValues);
     234   }
     235   elsif ($This->GetMode() =~ /^(Write|Append)$/i) {
     236     $This->_InitializeFingerprintsFPFileIOWriteProperties(%NamesAndValues);
     237   }
     238
     239   return $This;
     240 }
     241
     242 # Initialize object properties for reading fingerprints FP file...
     243 #
     244 sub _InitializeFingerprintsFPFileIOReadProperties {
     245   my($This, %NamesAndValues) = @_;
     246
     247   # Set default value for FingerprintsStringMode...
     248   if (!$This->{FingerprintsStringMode}) {
     249     $This->{FingerprintsStringMode} = 'AutoDetect';
     250   }
     251
     252   $This->_PrepareForReadingFingerprintsFPFileData();
     253
     254   return $This;
     255 }
     256
     257 # Initialize object properties for writing fingerprints FP file...
     258 #
     259 sub _InitializeFingerprintsFPFileIOWriteProperties {
     260   my($This, %NamesAndValues) = @_;
     261
     262   # Check FingerprintsStringMode value...
     263   if (!exists $NamesAndValues{FingerprintsStringMode}) {
     264     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying FingerprintsStringMode...";
     265   }
     266
     267   if ($This->{FingerprintsStringMode} !~ /^(FingerprintsBitVectorString|FingerprintsVectorString)$/i) {
     268     croak "Error: ${ClassName}->New: Object can't be instantiated: FingerprintsStringMode value, $This->{FingerprintsStringMode}, is not valid; Supported values for write/append: FingerprintsBitVectorString or FingerprintsVectorString...";
     269   }
     270
     271   $This->_PrepareForWritingFingerprintsFPFileData();
     272
     273   return $This;
     274 }
     275
     276 # Set FingerprintsStringMode...
     277 #
     278 sub SetFingerprintsStringMode {
     279   my($This, $Value) = @_;
     280
     281   # AutoDetect - automatically detect format of fingerprints string
     282   # FingerprintsBitVectorString - Bit vector fingerprints string format
     283   # FingerprintsVectorString - Vector fingerprints string format
     284
     285   if ($Value !~ /^(AutoDetect|FingerprintsBitVectorString|FingerprintsVectorString)$/i) {
     286     croak "Error: ${ClassName}->SetFingerprintsStringMode: FingerprintsStringMode value, $Value, is not valid; Supported values: AutoDetect, FingerprintsBitVectorString or FingerprintsVectorString...";
     287   }
     288
     289   $This->{FingerprintsStringMode} = $Value;
     290
     291   return $This;
     292 }
     293
     294 # Set DetailLevel...
     295 #
     296 sub SetDetailLevel {
     297   my($This, $Value) = @_;
     298
     299   if (!TextUtil::IsPositiveInteger($Value)) {
     300     croak "Error: ${ClassName}->SetDetailLevel: DetailLevel value, $Value, is not valid; Supported values: > 0...";
     301   }
     302
     303   $This->{DetailLevel} = $Value;
     304
     305   return $This;
     306 }
     307
     308 # Set BitStringFormat...
     309 #
     310 sub SetBitStringFormat {
     311   my($This, $Value) = @_;
     312
     313   if ($Value !~ /^(BinaryString|HexadecimalString)$/i) {
     314     croak "Error: ${ClassName}->SetBitStringFormat: BitStringFormat value, $Value, is not valid; Supported values: BinaryString or HexadecimalString...";
     315   }
     316
     317   $This->{BitStringFormat} = $Value;
     318
     319   return $This;
     320 }
     321
     322 # Set BitsOrder...
     323 #
     324 sub SetBitsOrder {
     325   my($This, $Value) = @_;
     326
     327   # Ascending - First bit in each byte as the lowest bit
     328   # Descending - First bit in each byte as the highest bit
     329   #
     330   if ($Value !~ /^(Ascending|Descending)$/i) {
     331     croak "Error: ${ClassName}->SetBitsOrder: FingerprintsStringMode value, $Value, is not valid; Supported values: Ascending or Descending...";
     332   }
     333
     334   $This->{BitsOrder} = $Value;
     335
     336   return $This;
     337 }
     338
     339 # Set compound ID...
     340 #
     341 sub SetCompoundID {
     342   my($This, $Value) = @_;
     343
     344   if ($Value =~ / /) {
     345     $Value =~ s/ //g;
     346     carp "Warning: ${ClassName}->SetCompoundID: Spaces are not allowed in compound ID; They have been removed...";
     347   }
     348
     349   $This->{CompoundID} = $Value;
     350
     351   return $This;
     352 }
     353
     354 # Set VectorStringFormat...
     355 #
     356 sub SetVectorStringFormat {
     357   my($This, $Value) = @_;
     358
     359   # Possible values: IDsAndValuesString, IDsAndValuesPairsString, ValuesAndIDsString, ValuesAndIDsPairsString
     360
     361   if ($Value !~ /^(IDsAndValuesString|IDsAndValuesPairsString|ValuesAndIDsString|ValuesAndIDsPairsString|ValuesString)$/i) {
     362     croak "Error: ${ClassName}->SetVectorStringFormat: FingerprintsStringMode value, $Value, is not valid; Supported values: IDsAndValuesString, IDsAndValuesPairsString, ValuesAndIDsString, ValuesAndIDsPairsString, or ValuesString...";
     363   }
     364
     365   $This->{VectorStringFormat} = $Value;
     366
     367   return $This;
     368 }
     369
     370 # Get header data keys or number of header data keys in header data block...
     371 #
     372 sub GetHeaderDataKeys {
     373   my($This) = @_;
     374
     375   return wantarray ? @{$This->{HeaderDataKeys}} : scalar @{$This->{HeaderDataKeys}};
     376 }
     377
     378 # Set header data keys...
     379 #
     380 sub SetHeaderDataKeys {
     381   my($This, @Keys) = @_;
     382
     383   croak "Error: ${ClassName}->SetHeaderDataKeys: Can't set HeaderDataKeys: Not allowed...";
     384
     385   return $This;
     386 }
     387
     388 # Get header data keys and values hash...
     389 #
     390 sub GetHeaderDataKeysAndValues {
     391   my($This) = @_;
     392
     393   return %{$This->{HeaderDataKeysAndValues}};
     394 }
     395
     396 # Set header data keys and values hash...
     397 #
     398 sub SetHeaderDataKeysAndValues {
     399   my($This, %KeysAndValues) = @_;
     400
     401   croak "Error: ${ClassName}->SetHeaderDataKeysAndValues: Can't set HeaderDataKeysAndValues: Not allowed...";
     402
     403   return $This;
     404 }
     405
     406 # Get required header data keys or number of header data keys in header data block...
     407 #
     408 sub GetRequiredHeaderDataKeys {
     409   my($This) = @_;
     410
     411   return wantarray ? @{$This->{RequiredHeaderDataKeys}} : scalar @{$This->{RequiredHeaderDataKeys}};
     412 }
     413
     414 # Set required header data keys...
     415 #
     416 sub SetRequiredHeaderDataKeys {
     417   my($This, @Keys) = @_;
     418
     419   croak "Error: ${ClassName}->SetRequiredHeaderDataKeys: Can't set RequiredHeaderDataKeys: Not allowed...";
     420
     421   return $This;
     422 }
     423
     424 # Get required header data keys and values hash...
     425 #
     426 sub GetRequiredHeaderDataKeysAndValues {
     427   my($This) = @_;
     428
     429   return %{$This->{RequiredHeaderDataKeysAndValues}};
     430 }
     431
     432 # Set required header data keys and values hash...
     433 #
     434 sub SetRequiredHeaderDataKeysAndValues {
     435   my($This, %KeysAndValues) = @_;
     436
     437   croak "Error: ${ClassName}->SetRequiredHeaderDataKeysAndValues: Can't set RequiredHeaderDataKeysAndValues: Not allowed...";
     438
     439   return $This;
     440 }
     441
     442 # Get fingerprints object for current data line...
     443 #
     444 sub GetFingerprints {
     445   my($This) = @_;
     446
     447   return $This->{FingerprintsObject};
     448 }
     449
     450 # Set fingerprints object for current data line...
     451 #
     452 sub SetFingerprints {
     453   my($This, $FingerprintsObject) = @_;
     454
     455   $This->{FingerprintsObject} = $FingerprintsObject;
     456
     457   return $This;
     458 }
     459
     460 # Get fingerprints string  for current data line...
     461 #
     462 sub GetFingerprintsString {
     463   my($This) = @_;
     464
     465   return $This->{FingerprintsString} ? $This->{FingerprintsString} : 'None';
     466 }
     467
     468 # Set fingerprints string for current data line...
     469 #
     470 sub SetFingerprintsString {
     471   my($This, $FingerprintsString) = @_;
     472
     473   $This->{FingerprintsString} = $FingerprintsString;
     474
     475   return $This;
     476 }
     477
     478 # Get partial fingerprints string  for current data line...
     479 #
     480 sub GetPartialFingerprintsString {
     481   my($This) = @_;
     482
     483   return $This->{PartialFingerprintsString} ? $This->{PartialFingerprintsString} : 'None';
     484 }
     485
     486 # Set partial fingerprints string for current data line...
     487 #
     488 sub SetPartialFingerprintsString {
     489   my($This, $PartialFingerprintsString) = @_;
     490
     491   $This->{PartialFingerprintsString} = $PartialFingerprintsString;
     492
     493   return $This;
     494 }
     495
     496 # Does fingerprints FP file contain valid data?
     497 #
     498 sub IsFingerprintsFileDataValid {
     499   my($This) = @_;
     500
     501   return $This->{ValidFileData} ? 1 : 0;
     502 }
     503
     504 # Does current data line contains valid fingerprints object data?
     505 #
     506 sub IsFingerprintsDataValid {
     507   my($This) = @_;
     508
     509   return defined $This->{FingerprintsObject} ? 1 : 0;
     510 }
     511
     512 # Check presence of a header data key...
     513 #
     514 sub IsHeaderDataKeyPresent {
     515   my($This, $Key) = @_;
     516   my($CannonicalKey);
     517
     518   $CannonicalKey = lc $Key;
     519
     520   return exists $This->{CannonicalHeaderDataKeysAndValues}{$CannonicalKey} ? 1 : 0;
     521 }
     522
     523 # Get value of header data key...
     524 #
     525 sub GetHeaderDataKeyValue {
     526   my($This, $Key) = @_;
     527   my($CannonicalKey);
     528
     529   $CannonicalKey = lc $Key;
     530
     531   return exists $This->{CannonicalHeaderDataKeysAndValues}{$CannonicalKey} ?  $This->{CannonicalHeaderDataKeysAndValues}{$CannonicalKey} : undef;
     532 }
     533
     534 #
     535 # Read next available fingerprints line, process it and generate appropriate fingerprints
     536 # objects...
     537 #
     538 sub Read {
     539   my($This) = @_;
     540
     541   # Read data line...
     542   if (!$This->_ReadDataLine()) {
     543     return undef;
     544   }
     545
     546   # No need to process invalid FP file with invalid data...
     547   if (!$This->{ValidFileData}) {
     548     if ($This->{ValidateData}) {
     549       $This->{NumOfLinesWithMissingData} += 1;
     550     }
     551     return $This;
     552   }
     553
     554   # Perform data validation...
     555   if ($This->{ValidateData}) {
     556     if (!$This->_ValidateReadDataLine()) {
     557       return $This;
     558     }
     559   }
     560
     561   # Check again to handle problematic data for non-validated data lines...
     562   if (!$This->{FingerprintsString}) {
     563     return $This;
     564   }
     565
     566   # Generate fingeprints object...
     567   $This->_GenerateFingerprintsObject();
     568
     569   # Setup fingerprints compound ID for fingerprints string...
     570   $This->_GenerateCompoundID();
     571
     572   return $This;
     573 }
     574
     575 # Read next available fingerprints line, process it and generate appropriate fingerprints
     576 # objects...
     577 #
     578 sub Next {
     579   my($This) = @_;
     580
     581   return $This->Read();
     582 }
     583
     584 # Read fingerprints data line line...
     585 #
     586 sub _ReadDataLine {
     587   my($This) = @_;
     588
     589   # Initialize data for current line...
     590   $This->_InitializeReadDataLine();
     591
     592   if ($This->{FirstDataLineIO}) {
     593     # Get first data line...
     594     $This->_ProcessFirstDataLineRead();
     595   }
     596   else {
     597     # Get next data line...
     598     $This->{LineNum} += 1;
     599     $This->{DataLine} = TextUtil::GetTextLine($This->{FileHandle});
     600   }
     601
     602   # Is it end of file?
     603   if (!$This->{DataLine}) {
     604     return 0;
     605   }
     606
     607   # Process data line to retrieve compound ID and fingerprints string information...
     608   $This->_ProcessDataLineRead();
     609
     610   return 1;
     611 }
     612
     613 # Process data line to retrieve compound ID and fingerprints string information...
     614 #
     615 sub _ProcessDataLineRead {
     616   my($This) = @_;
     617   my($CompoundID, $PartialFingerprintsString);
     618
     619   ($CompoundID, $PartialFingerprintsString) = $This->{DataLine} =~ /^(.*?)[ ]+(.*?)$/;
     620
     621   if (!(defined($CompoundID) && defined($PartialFingerprintsString))) {
     622     return $This;
     623   }
     624
     625   $This->{CompoundID} = $CompoundID;
     626   $This->{PartialFingerprintsString} = $PartialFingerprintsString;
     627
     628   # Set up fingerprints string...
     629   $This->_GenerateFingerprintsStringFromPartialFingerprintsString();
     630
     631   return $This;
     632 }
     633
     634 # Initialize data line for reading...
     635 #
     636 sub _InitializeReadDataLine {
     637   my($This) = @_;
     638
     639   $This->{CompoundID} = undef;
     640   $This->{DataLine} = undef;
     641
     642   $This->{FingerprintsObject} = undef;
     643
     644   $This->{FingerprintsString} = undef;
     645   $This->{PartialFingerprintsString} = undef;
     646
     647   return $This;
     648 }
     649
     650 # Validate fingerprints string data line...
     651 #
     652 sub _ValidateReadDataLine {
     653   my($This) = @_;
     654
     655   # Check for missing data...
     656   if (!($This->{CompoundID} && $This->{PartialFingerprintsString})) {
     657     # Missing data...
     658     $This->{NumOfLinesWithMissingData} += 1;
     659     if ($This->{DetailLevel} >= 3) {
     660       carp "Warning: ${ClassName}->_ValidateReadDataLine: Data line number $This->{LineNum} contains no fingerprints data: $This->{DataLine}...";
     661     }
     662     elsif ($This->{DetailLevel} >= 2) {
     663       carp "Warning: ${ClassName}->_ValidateReadDataLine: Data line number $This->{LineNum} contains no fingerprints data...";
     664     }
     665     return 0;
     666   }
     667
     668   # Check for invalid data...
     669   my($InvalidFingerprintsData);
     670
     671   $InvalidFingerprintsData = 0;
     672   if ($This->{FingerprintsString}) {
     673     $InvalidFingerprintsData = Fingerprints::FingerprintsStringUtil::AreFingerprintsStringValuesValid($This->{FingerprintsString}) ? 0 : 1;
     674   }
     675   else {
     676     $InvalidFingerprintsData = 1;
     677   }
     678
     679   if ($InvalidFingerprintsData) {
     680     $This->{NumOfLinesWithInvalidData} += 1;
     681     if ($This->{DetailLevel} >= 3) {
     682       carp "Warning: ${ClassName}->_ValidateReadDataLine: Data line number $This->{LineNum} contains invalid fingerprints data: $This->{DataLine}...";
     683     }
     684     elsif ($This->{DetailLevel} >= 2) {
     685       carp "Warning: ${ClassName}->_ValidateReadDataLine: Data line number $This->{LineNum} contains invalid fingerprints data...";
     686     }
     687     return 0;
     688   }
     689
     690   return 1;
     691 }
     692
     693 # Setup fingerprints compound ID for fingerprints string...
     694 sub _GenerateCompoundID {
     695   my($This) = @_;
     696
     697   # Set fingerprints ID...
     698   if ($This->{FingerprintsObject}) {
     699     $This->{FingerprintsObject}->SetID($This->{CompoundID});
     700   }
     701
     702   return $This;
     703 }
     704
     705 # Process first read...
     706 #
     707 sub _ProcessFirstDataLineRead {
     708   my($This) = @_;
     709   my($Line);
     710
     711   $This->{FirstDataLineIO} = 0;
     712
     713   # Skip over header data lines and collect first data line...
     714
     715   LINE: while ($Line = TextUtil::GetTextLine($This->{FileHandle})) {
     716     $This->{LineNum} += 1;
     717
     718     # Is it a header data line?
     719     if ($Line =~ /^#/) {
     720       next LINE;
     721     }
     722     $This->{DataLine} = $Line;
     723     last LINE;
     724   }
     725
     726   return $This;
     727 }
     728
     729 # Get ready for reading fingerprints FP file...
     730 #
     731 sub _PrepareForReadingFingerprintsFPFileData {
     732   my($This) = @_;
     733
     734   # Retrieve FP file data headers information....
     735   $This->_RetrieveFPFileDataHeaders();
     736
     737   # Validate header data keys and values information...
     738   $This->_ValidateReadHeaderDataKeysAndValues();
     739
     740   # Validate fingeprints string mode information...
     741   if ($This->{ValidRequiredHeaderDataKeys}) {
     742     $This->_ValidateReadFingerprintsStringMode();
     743   }
     744
     745   # Set status of FP file data...
     746   $This->{ValidFileData} = ($This->{ValidRequiredHeaderDataKeys} && $This->{ValidFingerprintsStringMode}) ? 1 : 0;
     747
     748   return $This;
     749 }
     750
     751 # Retrieve information about fingerprints date header in FP file...
     752 #
     753 sub _RetrieveFPFileDataHeaders {
     754   my($This) = @_;
     755   my($FPFile, $Line, $Index, $KeyValuePair, $Key, $Value, $KeyValueDelimiter, $KeyValuePairDelimiter, @LineKeyValuePairs);
     756
     757   $FPFile = $This->{Name};
     758
     759   if (!(-e $FPFile)) {
     760     croak "Error: ${ClassName}->_RetrieveFPFileDataHeaders: File, $FPFile, doesn't exist...";
     761   }
     762
     763   if (!open FPFILE, "$FPFile") {
     764     croak "Error: ${ClassName}->_RetrieveFPFileDataHeaders: Couldn't open input FP file $FPFile: $! ...";
     765   }
     766
     767   # Process header key/value pair data...
     768   #
     769   $KeyValueDelimiter = '=';
     770   $KeyValuePairDelimiter = ';';
     771
     772   @{$This->{HeaderDataKeys}} = ();
     773   %{$This->{HeaderDataKeysAndValues}} = ();
     774   %{$This->{CannonicalHeaderDataKeysAndValues}} = ();
     775
     776   LINE: while ($Line = TextUtil::GetTextLine(\*FPFILE)) {
     777     # Is it a key/value pairs line?
     778     if ($Line !~ /^#/) {
     779       last LINE;
     780     }
     781
     782     # Take out starting hash mark before processing key/value pairs...
     783     $Line =~ s/^#//;
     784     if (TextUtil::IsEmpty($Line)) {
     785       next LINE;
     786     }
     787
     788     @LineKeyValuePairs = ();
     789
     790     for $KeyValuePair (split "$KeyValuePairDelimiter", $Line) {
     791       ($Key, $Value) = split "$KeyValueDelimiter", $KeyValuePair;
     792
     793       $Key = defined($Key) ? TextUtil::RemoveLeadingAndTrailingWhiteSpaces($Key) : '';
     794       $Value = defined($Value) ? TextUtil::RemoveLeadingAndTrailingWhiteSpaces($Value) : '';
     795
     796       if (TextUtil::IsEmpty($Key) || TextUtil::IsEmpty($Value)) {
     797         carp "Warning: ${ClassName}->_RetrieveFPFileDataHeaders: Data header line containing \"Key = Value\" pairs is not valid: It must contain even number of \"Key = Value\" pairs with valid values. Ignoring data header line: \"$Line\"...";
     798         next LINE;
     799       }
     800       push @{$This->{HeaderDataKeys}}, $Key;
     801       push @LineKeyValuePairs, ($Key, $Value);
     802     }
     803
     804     for ($Index = 0; $Index < $#LineKeyValuePairs; $Index += 2) {
     805       $Key = $LineKeyValuePairs[$Index]; $Value = $LineKeyValuePairs[$Index + 1];
     806
     807       $This->{HeaderDataKeysAndValues}{$Key} = $Value;
     808       $This->{CannonicalHeaderDataKeysAndValues}{lc($Key)} = $Value;
     809     }
     810   }
     811   close FPFILE;
     812
     813   return $This;
     814 }
     815
     816 # Validate header data and keys...
     817 #
     818 sub _ValidateReadHeaderDataKeysAndValues {
     819   my($This) = @_;
     820   my($FingerprintsStringType, $Key, $Value, @RequiredHeaderDataKeys);
     821
     822   $This->{ValidRequiredHeaderDataKeys} = 0;
     823   @{$This->{RequiredHeaderDataKeys}} = ();
     824
     825   # Is FingerprintsStringType key is present?
     826   if (!$This->IsHeaderDataKeyPresent('FingerprintsStringType')) {
     827     carp "carp: ${ClassName}->_ValidateReadHeaderDataKeysAndValues: FingerprintsStringType data header key is missing in fingerprints file...";
     828     return 0;
     829   }
     830   $FingerprintsStringType = $This->GetHeaderDataKeyValue('FingerprintsStringType');
     831
     832   # Are all required data header keys present?
     833   #
     834   @RequiredHeaderDataKeys = ();
     835
     836   if ($FingerprintsStringType =~ /^(FingerprintsBitVector|FingerprintsVector)$/i) {
     837     push @RequiredHeaderDataKeys, $This->_GetRequiredHeaderDataKeys($FingerprintsStringType);
     838   }
     839   else {
     840     carp "Warning: ${ClassName}->_ValidateReadHeaderDataKeysAndValues: FingerprintsStringType data header key value, $FingerprintsStringType, is not valid. SUpported values: FingerprintsBitVector or FingerprintsVector...";
     841     return 0;
     842   }
     843
     844   for $Key (@RequiredHeaderDataKeys) {
     845     if (!$This->IsHeaderDataKeyPresent($Key)) {
     846       croak "Error: ${ClassName}->_ValidateReadHeaderDataKeysAndValues: Requires data header key, $Key, is missing in fingerprints file...";
     847     }
     848   }
     849
     850   push @{$This->{RequiredHeaderDataKeys}}, @RequiredHeaderDataKeys;
     851
     852   # Are all required data header key values valid?
     853   #
     854   if (!$This->_ValidateRequiredHeaderDataKeyValues()) {
     855     return 0;
     856   }
     857
     858   # Process required header key values...
     859   #
     860   $This->_ProcessRequiredHeaderDataKeyValues();
     861
     862   $This->{ValidRequiredHeaderDataKeys} = 1;
     863
     864   return 1;
     865 }
     866
     867 # Validate data header key values....
     868 #
     869 sub _ValidateRequiredHeaderDataKeyValues {
     870   my($This) = @_;
     871   my($Key, $Value);
     872
     873   for $Key (@{$This->{RequiredHeaderDataKeys}}) {
     874     $Value = $This->GetHeaderDataKeyValue($Key);
     875     KEY: {
     876       if ($Key =~ /^FingerprintsStringType$/i) {
     877         if ($Value !~ /^(FingerprintsBitVector|FingerprintsVector)$/i) {
     878           carp "Warning: ${ClassName}->_ValidateRequiredHeaderDataKeyValues: Required $Key data header key value, $Value, is not valid. Supported values: FingerprintsBitVector or FingerprintsVector...";
     879           return 0;
     880         }
     881         last KEY;
     882       }
     883       if ($Key =~ /^Size$/i) {
     884         if (!TextUtil::IsPositiveInteger($Value)) {
     885           carp "Warning: ${ClassName}->_ValidateRequiredHeaderDataKeyValues: Required $Key data header key value, $Value, is not valid. Supported values: > 0...";
     886           return 0;
     887         }
     888         last KEY;
     889       }
     890       if ($Key =~ /^BitStringFormat$/i) {
     891         if ($Value !~ /^(BinaryString|HexadecimalString)$/i) {
     892           carp "Warning: ${ClassName}->_ValidateRequiredHeaderDataKeyValues: Required $Key data header key value, $Value, is not valid. Supported values: BinaryString or HexadecimalString ...";
     893           return 0;
     894         }
     895         last KEY;
     896       }
     897       if ($Key =~ /^BitsOrder$/i) {
     898         if ($Value !~ /^(Ascending|Descending)$/i) {
     899           carp "Warning: ${ClassName}->_ValidateRequiredHeaderDataKeyValues: Required $Key data header key value, $Value, is not valid. Supported values: Ascending or Descending...";
     900           return 0;
     901         }
     902         last KEY;
     903       }
     904       if ($Key =~ /^VectorStringFormat$/i) {
     905         if ($Value !~ /^(IDsAndValuesString|IDsAndValuesPairsString|ValuesAndIDsString|ValuesAndIDsPairsString|ValuesString)$/i) {
     906           carp "Warning: ${ClassName}->_ValidateRequiredHeaderDataKeyValues: Required $Key data header key value, $Value, is not valid. Supported values: IDsAndValuesString, IDsAndValuesPairsString, ValuesAndIDsString, ValuesAndIDsPairsString, or ValuesString ...";
     907           return 0;
     908         }
     909         last KEY;
     910       }
     911       if ($Key =~ /^VectorValuesType$/i) {
     912         if ($Value !~ /^(OrderedNumericalValues|NumericalValues|AlphaNumericalValues)$/i) {
     913           carp "Warning: ${ClassName}->_ValidateRequiredHeaderDataKeyValues: Required $Key data header key value, $Value, is not valid. Supported values: OrderedNumericalValues, NumericalValues or AlphaNumericalValues...";
     914           return 0;
     915         }
     916         last KEY;
     917       }
     918       if ($Key =~ /^Description$/i) {
     919         if (TextUtil::IsEmpty($Value)) {
     920           carp "Warning: ${ClassName}->_ValidateRequiredHeaderDataKeyValues: Required $Key data header key value is not valid. Supported value: A no-empty text string...";
     921           return 0;
     922         }
     923         last KEY;
     924       }
     925       carp "Warning: ${ClassName}->_ValidateRequiredHeaderDataKeyValues: Required $Key data header key is not not supported...";
     926       return 0;
     927     }
     928   }
     929
     930   return 1;
     931 }
     932
     933 # Process required header key valeues for access during complete fingerprints
     934 # string generation from a partial fingerprints string specified on fingerprints
     935 # line...
     936 #
     937 sub _ProcessRequiredHeaderDataKeyValues {
     938   my($This) = @_;
     939   my($Key, $Value, @Keys);
     940
     941   %{$This->{RequiredHeaderDataKeysAndValues}} = ();
     942
     943   for $Key (@{$This->{RequiredHeaderDataKeys}}) {
     944     $Value = $This->GetHeaderDataKeyValue($Key);
     945     $This->{RequiredHeaderDataKeysAndValues}{$Key} = $Value;
     946   }
     947
     948   # Setup prefixes for generating fingerprints strings...
     949   $This->{FingerprintsBitVectorStringPrefix} = '';
     950   $This->{FingerprintsVectorStringPrefix1} = '';
     951   $This->{FingerprintsVectorStringPrefix2} = '';
     952
     953   if ($This->{RequiredHeaderDataKeysAndValues}{FingerprintsStringType} =~ /^FingerprintsBitVector$/i) {
     954     @Keys = qw(FingerprintsStringType Description Size BitStringFormat BitsOrder);
     955     $This->{FingerprintsBitVectorStringPrefix} = $This->_GenerateFingerprintsPrefixUsingKeys(@Keys);
     956   }
     957   elsif ($This->{RequiredHeaderDataKeysAndValues}{FingerprintsStringType} =~ /^FingerprintsVector$/i) {
     958     @Keys = qw(FingerprintsStringType Description);
     959     $This->{FingerprintsVectorStringPrefix1} = $This->_GenerateFingerprintsPrefixUsingKeys(@Keys);
     960
     961     @Keys = qw(VectorValuesType VectorStringFormat);
     962     $This->{FingerprintsVectorStringPrefix2} = $This->_GenerateFingerprintsPrefixUsingKeys(@Keys);
     963   }
     964
     965   return $This;
     966 }
     967
     968 # Generate fingerprints prefix using header keys data...
     969 #
     970 sub _GenerateFingerprintsPrefixUsingKeys {
     971   my($This, @Keys) = @_;
     972   my($Delimiter, $Key, @Values);
     973
     974   $Delimiter = Fingerprints::FingerprintsStringUtil::GetFingeprintsStringDelimiter();
     975
     976   @Values = ();
     977   for $Key (@Keys) {
     978     push @Values, $This->{RequiredHeaderDataKeysAndValues}{$Key};
     979   }
     980
     981   return join($Delimiter, @Values)
     982 }
     983
     984 # Get required header data keys...
     985 #
     986 sub _GetRequiredHeaderDataKeys {
     987   my($This, $FingerprintsStringType) = @_;
     988   my(@RequiredKeys);
     989
     990   @RequiredKeys = ();
     991
     992   if ($FingerprintsStringType =~ /FingerprintsBitVector$/i) {
     993     push @RequiredKeys, qw(FingerprintsStringType Description Size BitStringFormat BitsOrder);
     994   }
     995   elsif ($FingerprintsStringType =~ /^FingerprintsVector/i) {
     996     push @RequiredKeys, qw(FingerprintsStringType Description VectorStringFormat VectorValuesType);
     997   }
     998   else {
     999     carp "Warning: ${ClassName}->GetRequiredHeaderDataKeys: FingerprintsStringType value, $FingerprintsStringType, is not valid. Supported values: FingerprintsBitVector or FingerprintsVector...";
    1000   }
    1001
    1002   return @RequiredKeys;
    1003 }
    1004
    1005 # Validate fingerprints string mode information...
    1006 #
    1007 sub _ValidateReadFingerprintsStringMode {
    1008   my($This) = @_;
    1009   my($FingerprintsStringType, $FingerprintsStringDescription, $FingerprintsBitVectorStringMode, $FingerprintsVectorStringMode, $FirstFingerprintsStringType, $FirstFingerprintsStringDescription);
    1010
    1011   $This->{ValidFingerprintsStringMode} = 0;
    1012   $This->{FingerprintsBitVectorStringMode} = 0;
    1013   $This->{FingerprintsVectorStringMode} = 0;
    1014
    1015   $This->{FirstFingerprintsStringType} = '';
    1016   $This->{FirstFingerprintsStringDescription} = '';
    1017
    1018   $FingerprintsBitVectorStringMode = 0;
    1019   $FingerprintsVectorStringMode = 0;
    1020
    1021   $FirstFingerprintsStringType = '';
    1022   $FirstFingerprintsStringDescription = '';
    1023
    1024   $FingerprintsStringType = $This->GetHeaderDataKeyValue('FingerprintsStringType');
    1025   $FingerprintsStringDescription = $This->GetHeaderDataKeyValue('Description');
    1026
    1027   if ($This->{FingerprintsStringMode} =~ /^FingerprintsBitVectorString$/i) {
    1028     if ($FingerprintsStringType !~ /^FingerprintsBitVector$/i) {
    1029       carp "Warning: ${ClassName}->_ValidateReadFingerprintsStringMode: Fingerprints string data type, $FingerprintsStringType, doesn't correspond to, FingerprintsBitVectorString, specified using \"FingerprintsStringMode\"...";
    1030       return 0;
    1031     }
    1032     $FingerprintsBitVectorStringMode = 1;
    1033     $FirstFingerprintsStringType = 'FingerprintsBitVector';
    1034     $FirstFingerprintsStringDescription = $FingerprintsStringDescription;
    1035   }
    1036   elsif ($This->{FingerprintsStringMode} =~ /^FingerprintsVectorString$/i) {
    1037     if ($FingerprintsStringType !~ /^FingerprintsVector$/i) {
    1038       carp "Warning: ${ClassName}->_ValidateReadFingerprintsStringMode: Fingerprints string data type, $FingerprintsStringType, doesn't correspond to, FingerprintsVectorString, specified using \"FingerprintsStringMode\"...";
    1039       return 0;
    1040     }
    1041     $FingerprintsVectorStringMode = 1;
    1042     $FirstFingerprintsStringType = 'FingerprintsVector';
    1043     $FirstFingerprintsStringDescription = $FingerprintsStringDescription;
    1044   }
    1045   else {
    1046     # AutoDetect mode...
    1047     if ($FingerprintsStringType =~ /^FingerprintsBitVector$/i) {
    1048       $FingerprintsBitVectorStringMode = 1;
    1049     }
    1050     elsif ($FingerprintsStringType =~ /^FingerprintsVector$/i) {
    1051       $FingerprintsVectorStringMode = 1;
    1052     }
    1053     else {
    1054       carp "Warning: ${ClassName}->_ValidateReadFingerprintsStringMode: Fingerprints string data type, $FingerprintsStringType, identified during, AutoDetect, value of \"FingerprintsStringMode\" is not valid; Supported fingerprints types: FingerprintBitVector or FingerprintsVector...";
    1055       return 0;
    1056     }
    1057     $FirstFingerprintsStringType = $FingerprintsStringType;
    1058     $FirstFingerprintsStringDescription = $FingerprintsStringDescription;
    1059   }
    1060
    1061   $This->{ValidFingerprintsStringMode} = 1;
    1062
    1063   $This->{FingerprintsBitVectorStringMode} = $FingerprintsBitVectorStringMode;
    1064   $This->{FingerprintsVectorStringMode} = $FingerprintsVectorStringMode;
    1065
    1066   $This->{FirstFingerprintsStringType} = $FirstFingerprintsStringType;
    1067   $This->{FirstFingerprintsStringDescription} = $FirstFingerprintsStringDescription;
    1068
    1069   return 1;
    1070 }
    1071
    1072 # Write fingerprints string generated from specified fingerprints - fingerprints-bit vector or
    1073 # fingerprints vector - object and other data to FP file...
    1074 #
    1075 sub WriteFingerprints {
    1076   my($This, $FingerprintsObject, $CompoundID) = @_;
    1077
    1078   # Initialize data for current line...
    1079   $This->_InitializeWriteDataLine();
    1080
    1081   # Set fingerprints object and compound ID...
    1082   $This->{FingerprintsObject} = $FingerprintsObject;
    1083   $This->SetCompoundID($CompoundID);
    1084
    1085   # Generate fingerprints string...
    1086   $This->_GenerateFingerprintsString();
    1087
    1088   # Generate partial fingerprints string...
    1089   $This->_GeneratePartialFingerprintsStringFromFingerprintsString();
    1090
    1091   # Write data line..
    1092   $This->_WriteDataLine();
    1093
    1094   return $This;
    1095 }
    1096
    1097 # Write fingerprints string and other data to FP file...
    1098 #
    1099 # Notes:
    1100 #   o FingerprintsStringMode, BitStringFormat, BitsOrder, VectorStringFormat values
    1101 #     are ignored during writing of fingerprints and it's written to the file as it is.
    1102 #   o FingerprintsString is a regular fingerprints string as oppose to a partial fingerprints
    1103 #     string.
    1104 #
    1105 sub WriteFingerprintsString {
    1106   my($This, $FingerprintsString, $CompoundID) = @_;
    1107
    1108   # Initialize data for current line...
    1109   $This->_InitializeWriteDataLine();
    1110
    1111   # Set fingerprints string and compound ID...
    1112   $This->{FingerprintsString} = $FingerprintsString;
    1113   $This->SetCompoundID($CompoundID);
    1114
    1115   # Generate fingerprints object...
    1116   $This->_GenerateFingerprintsObject();
    1117
    1118   # Generate partial fingerprints string...
    1119   $This->_GeneratePartialFingerprintsStringFromFingerprintsString();
    1120
    1121   # Write data line..
    1122   $This->_WriteDataLine();
    1123
    1124   return $This;
    1125 }
    1126
    1127 # Initialize data line for reading...
    1128 #
    1129 sub _InitializeWriteDataLine {
    1130   my($This) = @_;
    1131
    1132   $This->{DataLine} = undef;
    1133   $This->{CompoundID} = undef;
    1134
    1135   $This->{FingerprintsObject} = undef;
    1136
    1137   $This->{FingerprintsString} = undef;
    1138   $This->{PartialFingerprintsString} = undef;
    1139
    1140   return $This;
    1141 }
    1142
    1143 # Write fingerprints data line line...
    1144 #
    1145 sub _WriteDataLine {
    1146   my($This) = @_;
    1147   my($FileHandle, $Line);
    1148
    1149   if ($This->{FirstDataLineIO}) {
    1150     $This->_ProcessFirstDataLineWrite();
    1151   }
    1152
    1153   # Write data compound ID along with partial fingerprints string...
    1154   $Line = $This->{CompoundID} . ' ' . $This->{PartialFingerprintsString};
    1155
    1156   $This->{LineNum} += 1;
    1157   $FileHandle = $This->{FileHandle};
    1158   print $FileHandle "$Line\n";
    1159
    1160   $This->{DataLine} = $Line;
    1161
    1162   return $This;
    1163 }
    1164
    1165 # Process first write...
    1166 #
    1167 sub _ProcessFirstDataLineWrite {
    1168   my($This) = @_;
    1169   my($Line, $FileHandle);
    1170
    1171   $This->{FirstDataLineIO} = 0;
    1172
    1173   if ($This->GetMode() =~ /^Write$/i) {
    1174     # Skip it for append mode...
    1175     $This->_WritePackageAndTimeStampHeaderKeys();
    1176     $This->_WriteRequiredHeaderDataKeys();
    1177   }
    1178
    1179   return $This;
    1180 }
    1181
    1182 # Write out package and time stamp information...
    1183 #
    1184 sub _WritePackageAndTimeStampHeaderKeys {
    1185   my($This) = @_;
    1186   my($FileHandle, $Key, $Value);
    1187
    1188   $FileHandle = $This->{FileHandle};
    1189
    1190   # Package information...
    1191   $This->{LineNum} += 1;
    1192   $Key = "Package"; $Value = PackageInfo::GetPackageName() . " " . PackageInfo::GetVersionNumber();
    1193   print $FileHandle "# $Key = $Value\n";
    1194
    1195   $This->{LineNum} += 1;
    1196   $Key = "Release Date"; $Value = PackageInfo::GetReleaseDate();
    1197   print $FileHandle "# $Key = $Value\n";
    1198
    1199   # Timestamp information...
    1200   $This->{LineNum} += 1;
    1201   print $FileHandle "#\n";
    1202
    1203   $This->{LineNum} += 1;
    1204   $Key = "TimeStamp"; $Value = TimeUtil::FPFileTimeStamp();
    1205   print $FileHandle "# $Key = $Value\n";
    1206
    1207   return $This;
    1208 }
    1209
    1210 # Write out required header data keys...
    1211 #
    1212 sub _WriteRequiredHeaderDataKeys {
    1213   my($This) = @_;
    1214   my($FileHandle, $Key, $Value);
    1215
    1216   $FileHandle = $This->{FileHandle};
    1217
    1218   $This->_GenerateWriteRequiredHeaderDataKeys();
    1219
    1220   $This->{LineNum} += 1;
    1221   print $FileHandle "#\n";
    1222
    1223   for $Key (@{$This->{RequiredHeaderDataKeys}}) {
    1224     $Value = $This->{RequiredHeaderDataKeysAndValues}{$Key};
    1225
    1226     $This->{LineNum} += 1;
    1227     print $FileHandle "# $Key = $Value\n";
    1228
    1229     if ($Key =~ /^FingerprintsStringType$/i) {
    1230       $This->{LineNum} += 1;
    1231       print $FileHandle "#\n";
    1232     }
    1233   }
    1234
    1235   $This->{LineNum} += 1;
    1236   print $FileHandle "#\n";
    1237
    1238   return $This;
    1239 }
    1240
    1241 sub _GenerateWriteRequiredHeaderDataKeys {
    1242   my($This) = @_;
    1243
    1244   if ($This->{FingerprintsBitVectorStringMode} && ($This->{FingerprintsString} =~ /^FingerprintsBitVector/i)) {
    1245     $This->_GenerateWriteRequiredHeaderDataKeysForBitVectorString();
    1246   }
    1247   elsif ($This->{FingerprintsVectorStringMode} && ($This->{FingerprintsString} =~ /^FingerprintsVector/i)) {
    1248     $This->_GenerateWriteRequiredHeaderDataKeysForVectorString();
    1249   }
    1250   else {
    1251     croak "Error: ${ClassName}->_GenerateWriteRequiredHeaderDataKeys: Required header data keys can't be generated: FingerprintsStringMode value, $This->{FingerprintsStringMode}, doesn't correspond to type of first FingerprintsString: $This->{FingerprintsString}...";
    1252   }
    1253
    1254   return $This;
    1255 }
    1256
    1257 # Generate required data header keys and values for writing fingerprints bit vector string...
    1258 #
    1259 sub _GenerateWriteRequiredHeaderDataKeysForBitVectorString {
    1260   my($This) = @_;
    1261   my($Key, $VectorType, $Description, $Size, $BitStringFormat, $BitsOrder);
    1262
    1263   @{$This->{RequiredHeaderDataKeys}} = ();
    1264   push @{$This->{RequiredHeaderDataKeys}}, $This->_GetRequiredHeaderDataKeys('FingerprintsBitVector');
    1265
    1266   ($VectorType, $Description, $Size, $BitStringFormat, $BitsOrder) = Fingerprints::FingerprintsStringUtil::GetFingerprintsStringValues($This->{FingerprintsString});
    1267
    1268   %{$This->{RequiredHeaderDataKeysAndValues}} = ();
    1269
    1270   for $Key (@{$This->{RequiredHeaderDataKeys}}) {
    1271     KEYTYPE: {
    1272       if ($Key =~ /^FingerprintsStringType$/i) {
    1273         $This->{RequiredHeaderDataKeysAndValues}{$Key} = $VectorType;
    1274         last KEYTYPE;
    1275       }
    1276       if ($Key =~ /^Description$/i) {
    1277         $This->{RequiredHeaderDataKeysAndValues}{$Key} = $Description;
    1278         last KEYTYPE;
    1279       }
    1280       if ($Key =~ /^Size$/i) {
    1281         $This->{RequiredHeaderDataKeysAndValues}{$Key} = $Size;
    1282         last KEYTYPE;
    1283       }
    1284       if ($Key =~ /^BitStringFormat$/i) {
    1285         $This->{RequiredHeaderDataKeysAndValues}{$Key} = $BitStringFormat;
    1286         last KEYTYPE;
    1287       }
    1288       if ($Key =~ /^BitsOrder$/i) {
    1289         $This->{RequiredHeaderDataKeysAndValues}{$Key} = $BitsOrder;
    1290         last KEYTYPE;
    1291       }
    1292       croak "Error: ${ClassName}->_GenerateWriteRequiredHeaderDataKeysForBitVectorString: Required header data key, $Key, value can't be generated: It's not a known key ...";
    1293     }
    1294   }
    1295
    1296   return $This;
    1297 }
    1298
    1299 # Generate required data header keys and values for writing fingerprints vector string...
    1300 #
    1301 sub _GenerateWriteRequiredHeaderDataKeysForVectorString {
    1302   my($This) = @_;
    1303   my($Key, $Value,  $VectorType, $Description, $NumOfValues, $VectorValuesType, $VectorStringFormat);
    1304
    1305   @{$This->{RequiredHeaderDataKeys}} = ();
    1306   push @{$This->{RequiredHeaderDataKeys}}, $This->_GetRequiredHeaderDataKeys('FingerprintsVector');
    1307
    1308   ($VectorType, $Description, $NumOfValues, $VectorValuesType, $VectorStringFormat) = Fingerprints::FingerprintsStringUtil::GetFingerprintsStringValues($This->{FingerprintsString});
    1309
    1310   %{$This->{RequiredHeaderDataKeysAndValues}} = ();
    1311
    1312   for $Key (@{$This->{RequiredHeaderDataKeys}}) {
    1313     KEYTYPE: {
    1314       if ($Key =~ /^FingerprintsStringType$/i) {
    1315         $This->{RequiredHeaderDataKeysAndValues}{$Key} = $VectorType;
    1316         last KEYTYPE;
    1317       }
    1318       if ($Key =~ /^Description$/i) {
    1319         $This->{RequiredHeaderDataKeysAndValues}{$Key} = $Description;
    1320         last KEYTYPE;
    1321       }
    1322       if ($Key =~ /^VectorValuesType$/i) {
    1323         $This->{RequiredHeaderDataKeysAndValues}{$Key} = $VectorValuesType;
    1324         last KEYTYPE;
    1325       }
    1326       if ($Key =~ /^VectorStringFormat$/i) {
    1327         $This->{RequiredHeaderDataKeysAndValues}{$Key} = $VectorStringFormat;
    1328         last KEYTYPE;
    1329       }
    1330       croak "Error: ${ClassName}->_GenerateWriteRequiredHeaderDataKeysForVectorString: Required header data key, $Key, value can't be generated: It's not a known key ...";
    1331     }
    1332   }
    1333
    1334   return $This;
    1335 }
    1336
    1337
    1338 # Get ready for writing fingerprints FP file...
    1339 #
    1340 sub _PrepareForWritingFingerprintsFPFileData {
    1341   my($This) = @_;
    1342   my($FPFile, $FileDir, $FileName, $FileExt, $OutDelim);
    1343
    1344   $FPFile = $This->{Name};
    1345   if (!$This->{Overwrite}) {
    1346     if (-e $FPFile) {
    1347       croak "Error: ${ClassName}->_PrepareForWritingFingerprintsFPFileData: File, $FPFile, already exist. Use overwrite option...";
    1348     }
    1349   }
    1350
    1351   # Setup FingerprintsStringMode status...
    1352   #
    1353   $This->{FingerprintsBitVectorStringMode} = 0;
    1354   $This->{FingerprintsVectorStringMode} = 0;
    1355   $This->{ValidFingerprintsStringMode} = 0;
    1356
    1357   if ($This->{FingerprintsStringMode} =~ /^FingerprintsBitVectorString$/i) {
    1358     $This->{FingerprintsBitVectorStringMode} = 1;
    1359   }
    1360   elsif ($This->{FingerprintsStringMode} =~ /^FingerprintsVectorString$/i) {
    1361     $This->{FingerprintsVectorStringMode} = 1;
    1362   }
    1363
    1364   $This->{ValidFingerprintsStringMode} = ($This->{FingerprintsBitVectorStringMode} || $This->{FingerprintsVectorStringMode}) ? 1 : 0;
    1365
    1366   if ($This->{FingerprintsBitVectorStringMode}) {
    1367     $This->_SetDefaultBitStringFormat();
    1368     $This->_SetDefaultBitsOrder();
    1369   }
    1370   elsif ($This->{FingerprintsVectorStringMode}) {
    1371     $This->_SetDefaultVectorStringFormat();
    1372   }
    1373
    1374   return $This;
    1375 }
    1376
    1377 # Set default value for bit string format...
    1378 #
    1379 sub _SetDefaultBitStringFormat {
    1380   my($This) = @_;
    1381
    1382   if (!$This->{BitStringFormat}) {
    1383     $This->{BitStringFormat} = Fingerprints::FingerprintsStringUtil::GetDefaultBitStringFormat();
    1384   }
    1385
    1386   return $This;
    1387 }
    1388
    1389 # Set default value for bit string format...
    1390 #
    1391 sub _SetDefaultBitsOrder {
    1392   my($This) = @_;
    1393
    1394   if (!$This->{BitsOrder}) {
    1395     $This->{BitsOrder} = Fingerprints::FingerprintsStringUtil::GetDefaultBitsOrder();
    1396   }
    1397
    1398   return $This;
    1399 }
    1400
    1401 # Set default value for vector string format...
    1402 #
    1403 sub _SetDefaultVectorStringFormat {
    1404   my($This) = @_;
    1405
    1406   if (!$This->{VectorStringFormat} && $This->{FingerprintsObject}) {
    1407     $This->{VectorStringFormat} = Fingerprints::FingerprintsStringUtil::GetDefaultVectorStringFormat($This->{FingerprintsObject});
    1408   }
    1409
    1410   return $This;
    1411 }
    1412
    1413 # Generate fingerprints object using current fingerprints string...
    1414 #
    1415 sub _GenerateFingerprintsObject {
    1416   my($This) = @_;
    1417
    1418   $This->{FingerprintsObject} = undef;
    1419
    1420   if (!$This->{FingerprintsString}) {
    1421     return $This;
    1422   }
    1423
    1424   if ($This->{FingerprintsBitVectorStringMode}) {
    1425     $This->{FingerprintsObject} = Fingerprints::FingerprintsStringUtil::ParseFingerprintsBitVectorString($This->{FingerprintsString});
    1426   }
    1427   elsif ($This->{FingerprintsVectorStringMode}) {
    1428     $This->{FingerprintsObject} = Fingerprints::FingerprintsStringUtil::ParseFingerprintsVectorString($This->{FingerprintsString});
    1429   }
    1430   else {
    1431     return undef;
    1432   }
    1433
    1434   return $This;
    1435 }
    1436
    1437 # Generate fingerprints string using current fingerprints object...
    1438 #
    1439 sub _GenerateFingerprintsString {
    1440   my($This) = @_;
    1441
    1442   $This->{FingerprintsString} = '';
    1443
    1444   if (!$This->{FingerprintsObject}) {
    1445     return $This;
    1446   }
    1447
    1448   if ($This->{FingerprintsBitVectorStringMode}) {
    1449     $This->{FingerprintsString} = Fingerprints::FingerprintsStringUtil::GenerateFingerprintsString($This->{FingerprintsObject}, $This->{BitStringFormat}, $This->{BitsOrder});
    1450   }
    1451   elsif ($This->{FingerprintsVectorStringMode}) {
    1452     $This->{FingerprintsString} = Fingerprints::FingerprintsStringUtil::GenerateFingerprintsString($This->{FingerprintsObject}, $This->{VectorStringFormat});
    1453   }
    1454
    1455   return $This;
    1456 }
    1457
    1458 # Generate fingerprints string using partial fingerprints string and header keys data...
    1459 #
    1460 # Notes:
    1461 #   o FP file fingerprints data line only contain partial fingerprints data which
    1462 #     can't be used directly to create fingerprints bit-vector or vector objects
    1463 #     using functions available in FingerprintsStringUtil.pm module
    1464 #
    1465 sub _GenerateFingerprintsStringFromPartialFingerprintsString {
    1466   my($This) = @_;
    1467   my($FPStringDelim);
    1468
    1469   $This->{FingerprintsString} = '';
    1470
    1471   if (!$This->{PartialFingerprintsString}) {
    1472     return $This;
    1473   }
    1474
    1475   $FPStringDelim = Fingerprints::FingerprintsStringUtil::GetFingeprintsStringDelimiter();
    1476
    1477   if ($This->{FingerprintsBitVectorStringMode}) {
    1478     $This->{FingerprintsString} = $This->{FingerprintsBitVectorStringPrefix} . $FPStringDelim . $This->{PartialFingerprintsString};
    1479   }
    1480   elsif ($This->{FingerprintsVectorStringMode}) {
    1481     my($NumOfValues, $VectorStringData);
    1482
    1483     ($NumOfValues, $VectorStringData) =  $This->{PartialFingerprintsString} =~ /^(.*?)$FPStringDelim(.*?)$/;
    1484     if (!(defined($NumOfValues) && defined($VectorStringData) && $VectorStringData)) {
    1485       return $This;
    1486     }
    1487
    1488     $This->{FingerprintsString} = $This->{FingerprintsVectorStringPrefix1} . $FPStringDelim . $NumOfValues . $FPStringDelim . $This->{FingerprintsVectorStringPrefix2} . $FPStringDelim . $VectorStringData;
    1489   }
    1490
    1491   return $This;
    1492 }
    1493
    1494 # Generate partial fingerprints string using fingerprints string and header keys data...
    1495 #
    1496 # Notes:
    1497 #   o FP file fingerprints data line only contain partial fingerprints data which
    1498 #     can't be used directly to create fingerprints bit-vector or vector objects
    1499 #     using functions available in FingerprintsStringUtil.pm module
    1500 #
    1501 sub _GeneratePartialFingerprintsStringFromFingerprintsString {
    1502   my($This) = @_;
    1503
    1504   $This->{PartialFingerprintsString} = '';
    1505
    1506   if (!$This->{FingerprintsString}) {
    1507     return $This;
    1508   }
    1509
    1510   if ($This->{FingerprintsBitVectorStringMode}) {
    1511     my($VectorType, $Description, $Size, $BitStringFormat, $BitsOrder, $BitVectorString);
    1512
    1513     ($VectorType, $Description, $Size, $BitStringFormat, $BitsOrder, $BitVectorString) = Fingerprints::FingerprintsStringUtil::GetFingerprintsStringValues($This->{FingerprintsString});
    1514     $This->{PartialFingerprintsString} = $BitVectorString;
    1515   }
    1516   elsif ($This->{FingerprintsVectorStringMode}) {
    1517     my($FPStringDelim, $VectorType, $Description, $NumOfValues, $VectorValuesType, $VectorStringFormat, $VectorString1, $VectorString2, $VectorString);
    1518
    1519     $FPStringDelim = Fingerprints::FingerprintsStringUtil::GetFingeprintsStringDelimiter();
    1520
    1521     ($VectorType, $Description, $NumOfValues, $VectorValuesType, $VectorStringFormat, $VectorString1, $VectorString2) = Fingerprints::FingerprintsStringUtil::GetFingerprintsStringValues($This->{FingerprintsString});
    1522     $VectorString = TextUtil::IsEmpty($VectorString2) ? $VectorString1 : "${VectorString1}${FPStringDelim}${VectorString2}";
    1523
    1524     $This->{PartialFingerprintsString} = $NumOfValues . $FPStringDelim . $VectorString;
    1525   }
    1526
    1527   return $This;
    1528 }
    1529
    1530 # Is it a fingerprints file?
    1531 sub IsFingerprintsFPFile ($;$) {
    1532   my($FirstParameter, $SecondParameter) = @_;
    1533   my($This, $FileName, $Status);
    1534
    1535   if ((@_ == 2) && (_IsFingerprintsFPFileIO($FirstParameter))) {
    1536     ($This, $FileName) = ($FirstParameter, $SecondParameter);
    1537   }
    1538   else {
    1539     $FileName = $FirstParameter;
    1540   }
    1541
    1542   # Check file extension...
    1543   $Status = FileUtil::CheckFileType($FileName, "fpf fp");
    1544
    1545   return $Status;
    1546 }
    1547
    1548 # Is it a FingerprintsFPFileIO object?
    1549 sub _IsFingerprintsFPFileIO {
    1550   my($Object) = @_;
    1551
    1552   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
    1553 }
    1554