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