       1 package FileIO::FingerprintsTextFileIO;
       2 #
       3 # File: FingerprintsTextFileIO.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 Fingerprints::FingerprintsStringUtil ();
      33 use FileIO::FileIO;
      34
      35 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
      36
      37 @ISA = qw(FileIO::FileIO Exporter);
      38 @EXPORT = qw();
      39 @EXPORT_OK = qw(IsFingerprintsTextFile);
      40
      41 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
      42
      43 # Setup class variables...
      44 my($ClassName);
      45 _InitializeClass();
      46
      47 # Class constructor...
      48 sub new {
      49   my($Class, %NamesAndValues) = @_;
      50
      51   # Initialize object...
      52   my $This = $Class->SUPER::new();
      53   bless $This, ref($Class) || $Class;
      54   $This->_InitializeFingerprintsTextFileIO();
      55
      56   $This->_InitializeFingerprintsTextFileIOProperties(%NamesAndValues);
      57
      58   return $This;
      59 }
      60
      61 # Initialize object data...
      62 #
      63 sub _InitializeFingerprintsTextFileIO {
      64   my($This) = @_;
      65
      66   # Fingerprints string data format during read/write...
      67   #
      68   # For file read:
      69   #
      70   # AutoDetect  - automatically detect format of fingerprints string
      71   # FingerprintsBitVectorString - Bit vector fingerprints string format
      72   # FingerprintsVectorString - Vector fingerprints string format
      73   #
      74   # Default value: AutoDetect
      75   #
      76   # For file write:
      77   #
      78   # FingerprintsBitVectorString - Bit vector fingerprints string format
      79   # FingerprintsVectorString - Vector fingerprints string format
      80   #
      81   # Default value: undef
      82   #
      83   $This->{FingerprintsStringMode} = undef;
      84
      85   # For file read:
      86   #
      87   #   o Fingerprints bit-vector and vector object for current fingerprints string
      88   #
      89   # For file write:
      90   #
      91   #   o Fingerprints bit-vector and vector object for current fingerprints string
      92   #   o Any supported fingerprints object: PathLengthFingerprints, ExtendedConnectivity, and so on.
      93   #
      94   $This->{FingerprintsObject} = undef;
      95
      96   # Fingepritns string for current line during read/write...
      97   $This->{FingerprintsString} = undef;
      98
      99   # First data line read/write...
     100   $This->{FirstDataLineIO} = 1;
     101
     102   # Current fingerprints string data line number during read/write...
     103   $This->{LineNum} = 0;
     104
     105   # Text line data during read/write...
     106   $This->{DataLine} = undef;
     107   @{$This->{DataLineWords}} = ();
     108
     109   # Text file column data during read/write...
     110   @{$This->{DataColLabels}} = ();
     111
     112   # Text file delimiter during read/write...
     113   $This->{Delim} = '';
     114
     115   # Initialize parameters for read...
     116   $This->_InitializeFingerprintsTextFileIORead();
     117
     118   # Initialize parameters for write...
     119   $This->_InitializeFingerprintsTextFileIOWrite();
     120
     121   return $This;
     122 }
     123
     124 # Initialize class ...
     125 sub _InitializeClass {
     126   #Class name...
     127   $ClassName = __PACKAGE__;
     128
     129 }
     130
     131 # Initialize object data for reading fingerprints text file...
     132 #
     133 sub _InitializeFingerprintsTextFileIORead {
     134   my($This) = @_;
     135
     136   # Column ID specification for identification of comound ID or fingerints string
     137   # data column...
     138   #
     139   # ColNum - A valid column number
     140   # ColLabel - A valid column name
     141   #
     142   $This->{ColMode} = 'ColNum';
     143
     144   # Fingerprints column to use for retrieving fingerprints string data...
     145   #
     146   # Value of AutoDetect implies use first column containing the word  Fingerprints in its
     147   # column label to retrieve fingerprints string data. Othwewise, a valid column number
     148   # or column name must be specified based on the value of ColMode.
     149   #
     150   $This->{FingerprintsCol} = 'AutoDetect';
     151
     152   # Compound ID column to use for retrieving compound IDs for fingerprints...
     153   #
     154   # Value of AutoDetect implies use first column containing the word CompoundID in its column
     155   # label to retrieve compound IDs or assign seqyentially generated compound IDs. Othwewise,
     156   # a valid column number or column name must be specified based on the value of ColMode.
     157   #
     158   $This->{CompoundIDCol} = 'AutoDetect';
     159
     160   # A prefix string used for generating compound IDs like LabelPrefixString<Number> during
     161   # sequential generation of compound IDs. Default value, Cmpd, generates compound IDs
     162   # which look like like Cmpd<Number>.
     163   #
     164   $This->{CompoundIDPrefix} = 'Cmpd';
     165
     166   # Input delimiter for fingerprints CSV text file. Possible values: comma, semicolon or tab. This
     167   # option is ignored for TSV text file and tab is used as the delimiter.
     168   #
     169   $This->{InDelim} = 'comma';
     170
     171   # By default, the fingerprints data corresponding to FingerprintsCol is assumed to
     172   # be valid and no validation is performed before generating fingerprints objects...
     173   #
     174   $This->{ValidateData} = 1;
     175
     176   # Level of detail to print during validation of data for invalid or missing data...
     177   $This->{DetailLevel} = 1;
     178
     179   # Number of missing and invalid fingerprints string data lines...
     180   $This->{NumOfLinesWithMissingData} = 0;
     181   $This->{NumOfLinesWithInvalidData} = 0;
     182
     183   # Compound ID for current fingerprints string...
     184   $This->{CompoundID} = undef;
     185
     186   # Status of data in fingerprints text file...
     187   $This->{ValidFileData} = 0;
     188
     189   $This->{ValidCompoundIDCol} = 0;
     190   $This->{ValidFingerprintsCol} = 0;
     191
     192   $This->{ValidFingerprintsStringMode} = 0;
     193
     194   return $This;
     195 }
     196
     197 # Initialize object data for writing fingerprints text file...
     198 #
     199 sub _InitializeFingerprintsTextFileIOWrite {
     200   my($This) = @_;
     201
     202   # Fingerprints bit vector string format...
     203   #
     204   # Possible values: BinaryString or HexadecimalString [Default]
     205   #
     206   # Default BitStringFormat is set during first write using Fingerprints::FingerprintsStringUtil::GetDefaultBitStringFormat.
     207   #
     208   $This->{BitStringFormat} = undef;
     209
     210   # Bits order in fingerprints bit vector string...
     211   #
     212   # Ascending - First bit in each byte as the lowest bit [Default]
     213   # Descending - First bit in each byte as the highest bit
     214   #
     215   # Default BitsOrder is set during first write using Fingerprints::FingerprintsStringUtil::GetDefaultBitsOrder.
     216   #
     217   $This->{BitsOrder} = undef;
     218
     219   # Fingerprints vector string format...
     220   #
     221   # Possible values: IDsAndValuesString, IDsAndValuesPairsString, ValuesAndIDsString, ValuesAndIDsPairsString, ValuesString
     222   #
     223   # Default VectorStringFormat is set during first write using Fingerprints::FingerprintsStringUtil::GetDefaultVectorStringFormat.
     224   # For fingerprints vector object containing vector NumericalValues, it corresponds to IDsAndValuesString; othwerwise,
     225   # it's set to ValuesString.
     226   #
     227   $This->{VectorStringFormat} = undef;
     228
     229   # Delimiter for output fingerprints CSV/TSV file. Possible values: comma, tab, semicolon. This
     230   # option is ignored for TSV text file and tab is used as the delimiter.
     231   #
     232   $This->{OutDelim} = 'comma';
     233
     234   # Quotes around column values for output fingerprints CSV/TSV text file...
     235   $This->{OutQuote} = 1;
     236
     237   # Overwriting existing file...
     238   $This->{Overwrite} = 0;
     239
     240   return $This;
     241 }
     242
     243 # Initialize object values...
     244 sub _InitializeFingerprintsTextFileIOProperties {
     245   my($This, %NamesAndValues) = @_;
     246
     247   # All other property names and values along with all Set/Get<PropertyName> methods
     248   # are implemented on-demand using ObjectProperty class.
     249
     250   my($Name, $Value, $MethodName);
     251   while (($Name, $Value) = each  %NamesAndValues) {
     252     $MethodName = "Set${Name}";
     253     $This->$MethodName($Value);
     254   }
     255
     256   if (!exists $NamesAndValues{Name}) {
     257     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying file name...";
     258   }
     259
     260   # Make sure it's a fingerprints file...
     261   $Name = $NamesAndValues{Name};
     262   if (!$This->IsFingerprintsTextFile($Name)) {
     263     croak "Error: ${ClassName}->New: Object can't be instantiated: File, $Name, doesn't appear to be fingerprints format...";
     264   }
     265
     266   if ($This->GetMode() =~ /^Read$/i) {
     267     $This->_InitializeFingerprintsTextFileIOReadProperties(%NamesAndValues);
     268   }
     269   elsif ($This->GetMode() =~ /^(Write|Append)$/i) {
     270     $This->_InitializeFingerprintsTextFileIOWriteProperties(%NamesAndValues);
     271   }
     272
     273   return $This;
     274 }
     275
     276 # Initialize object properties for reading fingerprints text file...
     277 #
     278 sub _InitializeFingerprintsTextFileIOReadProperties {
     279   my($This, %NamesAndValues) = @_;
     280
     281   # Set default value for FingerprintsStringMode...
     282   if (!$This->{FingerprintsStringMode}) {
     283     $This->{FingerprintsStringMode} = 'AutoDetect';
     284   }
     285
     286   $This->_PrepareForReadingFingerprintsTextFileData();
     287
     288   return $This;
     289 }
     290
     291 # Initialize object properties for writing fingerprints text file...
     292 #
     293 sub _InitializeFingerprintsTextFileIOWriteProperties {
     294   my($This, %NamesAndValues) = @_;
     295
     296   # Check FingerprintsStringMode value...
     297   if (!exists $NamesAndValues{FingerprintsStringMode}) {
     298     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying FingerprintsStringMode...";
     299   }
     300
     301   if ($This->{FingerprintsStringMode} !~ /^(FingerprintsBitVectorString|FingerprintsVectorString)$/i) {
     302     croak "Error: ${ClassName}->: Object can't be instantiated: FingerprintsStringMode value, $This->{FingerprintsStringMode}, is not valid; Supported values for write/append: FingerprintsBitVectorString or FingerprintsVectorString...";
     303   }
     304
     305   if (!exists $NamesAndValues{DataColLabels}) {
     306     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying DataColLabels...";
     307   }
     308
     309   if ($This->{OutDelim} =~ /semicolon/i && !$This->{OutQuote}) {
     310     croak "Error: ${ClassName}->: Object can't be instantiated: The value specified, $This->{OutQuote}, using \"OutQuote\" is not allowed with semicolon value of \"OutDelim\": Fingerprints string use semicolon as delimiter for various data fields and must be quoted.\n";
     311   }
     312
     313   $This->_PrepareForWritingFingerprintsTextFileData();
     314
     315   return $This;
     316 }
     317
     318 # Set FingerprintsStringMode...
     319 #
     320 sub SetFingerprintsStringMode {
     321   my($This, $Value) = @_;
     322
     323   # AutoDetect - automatically detect format of fingerprints string
     324   # FingerprintsBitVectorString - Bit vector fingerprints string format
     325   # FingerprintsVectorString - Vector fingerprints string format
     326
     327   if ($Value !~ /^(AutoDetect|FingerprintsBitVectorString|FingerprintsVectorString)$/i) {
     328     croak "Error: ${ClassName}->SetFingerprintsStringMode: FingerprintsStringMode value, $Value, is not valid; Supported values: AutoDetect, FingerprintsBitVectorString or FingerprintsVectorString...";
     329   }
     330
     331   $This->{FingerprintsStringMode} = $Value;
     332
     333   return $This;
     334 }
     335
     336 # Set ColMode...
     337 #
     338 sub SetColMode {
     339   my($This, $Value) = @_;
     340
     341   if ($Value !~ /^(ColNum|ColLabel)$/i) {
     342     croak "Error: ${ClassName}->SetColMode: ColMode value, $Value, is not valid; Supported values: ColNum or ColLabel...";
     343   }
     344
     345   $This->{ColMode} = $Value;
     346
     347   return $This;
     348 }
     349
     350 # Set InDelim...
     351 #
     352 sub SetInDelim {
     353   my($This, $Value) = @_;
     354
     355   if ($Value !~ /^(comma|semicolon|tab)$/i) {
     356     croak "Error: ${ClassName}->SetInDelim: InDelim value, $Value, is not valid; Supported values: comma, semicolon, or tab...";
     357   }
     358
     359   $This->{InDelim} = $Value;
     360
     361   return $This;
     362 }
     363
     364 # Set DetailLevel...
     365 #
     366 sub SetDetailLevel {
     367   my($This, $Value) = @_;
     368
     369   if (!TextUtil::IsPositiveInteger($Value)) {
     370     croak "Error: ${ClassName}->SetDetailLevel: DetailLevel value, $Value, is not valid; Supported values: > 0...";
     371   }
     372
     373   $This->{DetailLevel} = $Value;
     374
     375   return $This;
     376 }
     377
     378 # Set BitStringFormat...
     379 #
     380 sub SetBitStringFormat {
     381   my($This, $Value) = @_;
     382
     383   if ($Value !~ /^(BinaryString|HexadecimalString)$/i) {
     384     croak "Error: ${ClassName}->SetBitStringFormat: BitStringFormat value, $Value, is not valid; Supported values: BinaryString or HexadecimalString...";
     385   }
     386
     387   $This->{BitStringFormat} = $Value;
     388
     389   return $This;
     390 }
     391
     392 # Set BitsOrder...
     393 #
     394 sub SetBitsOrder {
     395   my($This, $Value) = @_;
     396
     397   # Ascending - First bit in each byte as the lowest bit
     398   # Descending - First bit in each byte as the highest bit
     399   #
     400   if ($Value !~ /^(Ascending|Descending)$/i) {
     401     croak "Error: ${ClassName}->SetBitsOrder: FingerprintsStringMode value, $Value, is not valid; Supported values: Ascending or Descending...";
     402   }
     403
     404   $This->{BitsOrder} = $Value;
     405
     406   return $This;
     407 }
     408
     409 # Set VectorStringFormat...
     410 #
     411 sub SetVectorStringFormat {
     412   my($This, $Value) = @_;
     413
     414   # Possible values: IDsAndValuesString, IDsAndValuesPairsString, ValuesAndIDsString, ValuesAndIDsPairsString, ValuesString
     415
     416   if ($Value !~ /^(IDsAndValuesString|IDsAndValuesPairsString|ValuesAndIDsString|ValuesAndIDsPairsString|ValuesString)$/i) {
     417     croak "Error: ${ClassName}->SetVectorStringFormat: FingerprintsStringMode value, $Value, is not valid; Supported values: IDsAndValuesString, IDsAndValuesPairsString, ValuesAndIDsString, ValuesAndIDsPairsString, or ValuesString...";
     418   }
     419
     420   $This->{VectorStringFormat} = $Value;
     421
     422   return $This;
     423 }
     424
     425 # Set FingerprintsStringMode...
     426 #
     427 sub SetOutDelim {
     428   my($This, $Value) = @_;
     429
     430   if ($Value !~ /^(comma|tab|semicolon)$/i) {
     431     croak "Error: ${ClassName}->SetOutDelim: OutDelim value, $Value, is not valid; Supported values: comma, tab or semicolon...";
     432   }
     433
     434   $This->{OutDelim} = $Value;
     435
     436   return $This;
     437 }
     438
     439 # Set DataColLabels...
     440 #
     441 # Set output data column labels using:
     442 #    o List of column labels
     443 #    o Reference to an list of column labels
     444 #
     445 sub SetDataColLabels {
     446   my($This, @Values) = @_;
     447   my($FirstValue, $TypeOfFirstValue);
     448
     449   if (!@Values) {
     450     carp "Warning: ${ClassName}->_SetDataColLabels: No data column labels specified...";
     451     return $This;
     452   }
     453
     454   @{$This->{DataColLabels}} = ();
     455
     456   $FirstValue = $Values[0];
     457   $TypeOfFirstValue = ref $FirstValue;
     458
     459   if ($TypeOfFirstValue =~ /^ARRAY/) {
     460     # Initialize using array refernce...
     461     push @{$This->{DataColLabels}}, @{$FirstValue};
     462   }
     463   else {
     464     # It's a list of values...
     465     push @{$This->{DataColLabels}}, @Values;
     466   }
     467
     468   return $This;
     469 }
     470
     471 # Get column labels or number of column labels in first text line...
     472 #
     473 sub GetDataColLabels {
     474   my($This) = @_;
     475
     476   return wantarray ? @{$This->{DataColLabels}} : scalar @{$This->{DataColLabels}};
     477 }
     478
     479 # Get words or number of words in current data line...
     480 #
     481 sub GetDataLineWords {
     482   my($This) = @_;
     483
     484   return wantarray ? @{$This->{DataLineWords}} : scalar @{$This->{DataLineWords}};
     485 }
     486
     487 # Set DataLineWords...
     488 #
     489 # Set data line words using:
     490 #    o List of line words
     491 #    o Reference to an list of line words
     492 #
     493 sub SetDataLineWords {
     494   my($This, @Values) = @_;
     495   my($FirstValue, $TypeOfFirstValue);
     496
     497   if (!@Values) {
     498     carp "Warning: ${ClassName}->SetDataLineWords: No line words specified...";
     499     return $This;
     500   }
     501
     502   @{$This->{DataLineWords}} = ();
     503
     504   $FirstValue = $Values[0];
     505   $TypeOfFirstValue = ref $FirstValue;
     506
     507   if ($TypeOfFirstValue =~ /^ARRAY/) {
     508     # Initialize using array refernce...
     509     push @{$This->{DataLineWords}}, @{$FirstValue};
     510   }
     511   else {
     512     # It's a list of values...
     513     push @{$This->{DataLineWords}}, @Values;
     514   }
     515
     516   return $This;
     517 }
     518
     519 # Get fingerprints object for current data line using fingerprints, fingerprints bit-vector
     520 # fingerprints vector object. Fingerprints object correspond to any of supported fingerprints
     521 # objects such as PathLengthFingerprints, ExtendedConnectivity, and so on.
     522 #
     523 sub GetFingerprints {
     524   my($This) = @_;
     525
     526   return $This->{FingerprintsObject};
     527 }
     528
     529 # Set fingerprints object for current data line...
     530 #
     531 sub SetFingerprints {
     532   my($This, $FingerprintsObject) = @_;
     533
     534   $This->{FingerprintsObject} = $FingerprintsObject;
     535
     536   return $This;
     537 }
     538
     539 # Get fingerprints string  for current data line...
     540 #
     541 sub GetFingerprintsString {
     542   my($This) = @_;
     543
     544   return $This->{FingerprintsString} ? $This->{FingerprintsString} : 'None';
     545 }
     546
     547 # Set fingerprints string for current data line...
     548 #
     549 sub SetFingerprintsString {
     550   my($This, $FingerprintsString) = @_;
     551
     552   $This->{FingerprintsString} = $FingerprintsString;
     553
     554   return $This;
     555 }
     556
     557 # Does fingerprints text file contain valid data?
     558 #
     559 sub IsFingerprintsFileDataValid {
     560   my($This) = @_;
     561
     562   return $This->{ValidFileData} ? 1 : 0;
     563 }
     564
     565 # Does current data line contains valid fingerprints object data?
     566 #
     567 sub IsFingerprintsDataValid {
     568   my($This) = @_;
     569
     570   return defined $This->{FingerprintsObject} ? 1 : 0;
     571 }
     572
     573 # Read next available fingerprints line, process it and generate appropriate fingerprints
     574 # objects...
     575 #
     576 sub Read {
     577   my($This) = @_;
     578
     579   # Read data line...
     580   if (!$This->_ReadDataLine()) {
     581     return undef;
     582   }
     583
     584   # No need to process invalid text file with invalid data...
     585   if (!$This->{ValidFileData}) {
     586     if ($This->{ValidateData}) {
     587       $This->{NumOfLinesWithMissingData} += 1;
     588     }
     589     return $This;
     590   }
     591
     592   # Perform data validation...
     593   if ($This->{ValidateData}) {
     594     if (!$This->_ValidateReadDataLine()) {
     595       return $This;
     596     }
     597   }
     598
     599   # Setup fingerprints string after checking again to handle problematic data for
     600   # non-validated data lines...
     601   #
     602   if ($This->{FingerprintsColNum} <= $#{$This->{DataLineWords}}) {
     603     $This->{FingerprintsString} = $This->{DataLineWords}[$This->{FingerprintsColNum}];
     604   }
     605
     606   # Generate fingeprints object...
     607   $This->_GenerateFingerprintsObject();
     608
     609   # Setup fingerprints compound ID for fingerprints string...
     610   $This->_GenerateCompoundID();
     611
     612   return $This;
     613 }
     614
     615 # Read next available fingerprints line, process it and generate appropriate fingerprints
     616 # objects...
     617 #
     618 sub Next {
     619   my($This) = @_;
     620
     621   return $This->Read();
     622 }
     623
     624 # Read fingerprints data line line...
     625 #
     626 sub _ReadDataLine {
     627   my($This) = @_;
     628
     629   if ($This->{FirstDataLineIO}) {
     630     $This->_ProcessFirstDataLineRead();
     631   }
     632
     633   # Initialize data for current line...
     634   $This->_InitializeReadDataLine();
     635
     636   # Get next data line...
     637   $This->{DataLine} = TextUtil::GetTextLine($This->{FileHandle});
     638   if (!$This->{DataLine}) {
     639     return 0;
     640   }
     641
     642   # Get line words...
     643   $This->{LineNum} += 1;
     644   @{$This->{DataLineWords}} = TextUtil::SplitWords($This->{DataLine}, $This->{Delim});
     645
     646   return 1;
     647 }
     648
     649 # Initialize data line for reading...
     650 #
     651 sub _InitializeReadDataLine {
     652   my($This) = @_;
     653
     654   $This->{CompoundID} = undef;
     655
     656   $This->{DataLine} = undef;
     657   @{$This->{DataLineWords}} = ();
     658
     659   $This->{FingerprintsObject} = undef;
     660   $This->{FingerprintsString} = undef;
     661
     662   return $This;
     663 }
     664
     665 # Validate fingerprints string data line...
     666 #
     667 sub _ValidateReadDataLine {
     668   my($This) = @_;
     669
     670   # Check for missing data...
     671   if ($This->{FingerprintsColNum} > $#{$This->{DataLineWords}}) {
     672     # Missing data...
     673     $This->{NumOfLinesWithMissingData} += 1;
     674     if ($This->{DetailLevel} >= 3) {
     675       carp "Warning: ${ClassName}->_ValidateReadDataLine: Data line number $This->{LineNum} contains no fingerprints data: $This->{DataLine}...";
     676     }
     677     elsif ($This->{DetailLevel} >= 2) {
     678       carp "Warning: ${ClassName}->_ValidateReadDataLine: Data line number $This->{LineNum} contains no fingerprints data...";
     679     }
     680     return 0;
     681   }
     682
     683   # Check for invalid data...
     684   my($InvalidFingerprintsData, $FingerprintsColNum, $FingerprintsType, $FingerprintsDescription);
     685
     686   $InvalidFingerprintsData = 0;
     687   $FingerprintsColNum = $This->{FingerprintsColNum};
     688
     689   if (Fingerprints::FingerprintsStringUtil::AreFingerprintsStringValuesValid($This->{DataLineWords}[$FingerprintsColNum])) {
     690     ($FingerprintsType, $FingerprintsDescription) = Fingerprints::FingerprintsStringUtil::GetFingerprintsStringTypeAndDescription($This->{DataLineWords}[$FingerprintsColNum]);
     691     if ($This->{FirstFingerprintsStringType} !~ /^$FingerprintsType$/i || $This->{FirstFingerprintsStringDescription} !~ /^$FingerprintsDescription$/i) {
     692       $InvalidFingerprintsData = 1;
     693     }
     694   }
     695   else {
     696     $InvalidFingerprintsData = 1;
     697   }
     698
     699   if ($InvalidFingerprintsData) {
     700     $This->{NumOfLinesWithInvalidData} += 1;
     701     if ($This->{DetailLevel} >= 3) {
     702       carp "Warning: ${ClassName}->_ValidateReadDataLine: Data line number $This->{LineNum} contains invalid fingerprints data: $This->{DataLine}...";
     703     }
     704     elsif ($This->{DetailLevel} >= 2) {
     705       carp "Warning: ${ClassName}->_ValidateReadDataLine: Data line number $This->{LineNum} contains invalid fingerprints data...";
     706     }
     707     return 0;
     708   }
     709
     710   return 1;
     711 }
     712
     713 # Setup fingerprints compound ID for fingerprints string...
     714 sub _GenerateCompoundID {
     715   my($This) = @_;
     716   my($CompoundID);
     717
     718   $CompoundID = '';
     719
     720   if ($This->{UseSequentialCompoundIDs} || ($This->{CompoundIDColNum} > $#{$This->{DataLineWords}})) {
     721     my($CompoundNum);
     722
     723     $CompoundNum = $This->{LineNum} - 1;
     724     $CompoundID = "$This->{CompoundIDPrefix}${CompoundNum}";
     725   }
     726   else {
     727     $CompoundID = $This->{DataLineWords}[$This->{CompoundIDColNum}];
     728   }
     729
     730   $This->{CompoundID} = $CompoundID;
     731
     732   # Set fingerprints ID...
     733   if ($This->{FingerprintsObject}) {
     734     $This->{FingerprintsObject}->SetID($This->{CompoundID});
     735   }
     736
     737   return $This;
     738 }
     739
     740 # Process first read...
     741 #
     742 sub _ProcessFirstDataLineRead {
     743   my($This) = @_;
     744
     745   # Skip column label line...
     746   $This->{LineNum} += 1;
     747   TextUtil::GetTextLine($This->{FileHandle});
     748
     749   $This->{FirstDataLineIO} = 0;
     750
     751   return $This;
     752 }
     753
     754 # Get ready for reading fingerprints text file...
     755 #
     756 sub _PrepareForReadingFingerprintsTextFileData {
     757   my($This) = @_;
     758
     759   # Retrieve text file columns information....
     760   $This->_RetrieveTextFileColData();
     761
     762   # Validate columns information...
     763   $This->_ValidateReadCompoundIDCol();
     764   $This->_ValidateReadFingerprintsCol();
     765
     766   # Validate fingeprints string mode information...
     767   if ($This->{ValidFingerprintsCol}) {
     768     $This->_ValidateReadFingerprintsStringMode();
     769   }
     770
     771   # Set status of text file data...
     772   $This->{ValidFileData} = ($This->{ValidCompoundIDCol} && $This->{ValidFingerprintsCol} && $This->{ValidFingerprintsStringMode}) ? 1 : 0;
     773
     774   return $This;
     775 }
     776
     777 # Retrieve information about columns and fingerprints string...
     778 #
     779 sub _RetrieveTextFileColData {
     780   my($This) = @_;
     781   my($TextFile, $FileDir, $FileName, $FileExt, $InDelim, $Line, $ColLabel, $ColNum, @ColLabels);
     782
     783   @{$This->{DataColLabels}} = ();
     784   %{$This->{DataColLabelToNumMap}} = ();
     785
     786   $TextFile = $This->{Name};
     787
     788   if (!(-e $TextFile)) {
     789     croak "Error: ${ClassName}->New: Object can't be instantiated: File, $TextFile, doesn't exist...";
     790   }
     791
     792   $FileDir = ""; $FileName = ""; $FileExt = "";
     793   ($FileDir, $FileName, $FileExt) = FileUtil::ParseFileName($TextFile);
     794
     795   $InDelim = ($FileExt =~ /^tsv$/i) ? "\t" : ($This->{InDelim} =~ /semicolon/i ? "\;" : "\,");
     796   $This->{Delim} = $InDelim;
     797
     798   if (!open TEXTFILE, "$TextFile") {
     799     croak "Error: ${ClassName}->New: Object can't be instantiated: Couldn't open input text file $TextFile: $! ...";
     800   }
     801
     802   # Get column label line...
     803   $Line = TextUtil::GetTextLine(\*TEXTFILE);
     804
     805   close TEXTFILE;
     806
     807   @ColLabels = TextUtil::SplitWords($Line, $InDelim);
     808
     809   # Set text file columns info....
     810   push @{$This->{DataColLabels}}, @ColLabels;
     811
     812   for $ColNum (0 .. $#ColLabels) {
     813     $ColLabel = $ColLabels[$ColNum];
     814     $This->{DataColLabelToNumMap}{$ColLabel} = $ColNum;
     815   }
     816
     817   return $This;
     818 }
     819
     820 # Validate compound ID column information...
     821 #
     822 sub _ValidateReadCompoundIDCol {
     823   my($This) = @_;
     824   my($CompoundIDCol, $CompoundIDColNum, $UseSequentialCompoundIDs, $ColFound, $ColLabel, $ColNum);
     825
     826   $This->{ValidCompoundIDCol} = 0;
     827   $This->{CompoundIDColNum} = undef;
     828   $This->{UseSequentialCompoundIDs} = 0;
     829
     830   $CompoundIDCol = $This->{CompoundIDCol};
     831
     832   $UseSequentialCompoundIDs = 0;
     833   $CompoundIDColNum = '';
     834
     835   if ($CompoundIDCol =~ /^AutoDetect$/i) {
     836     # First column containing the word CompoundID in its label or sequential generation...
     837
     838     $ColFound = 0;
     839     COLLABEL: for $ColLabel (@{$This->{DataColLabels}}) {
     840       if ($ColLabel =~ /CompoundID/i) {
     841         $ColFound = 1;
     842         $ColNum = $This->{DataColLabelToNumMap}{$ColLabel};
     843         last COLLABEL;
     844       }
     845     }
     846     if ($ColFound) {
     847       $CompoundIDColNum = $ColNum;
     848     }
     849     else {
     850       $UseSequentialCompoundIDs = 1;
     851     }
     852   }
     853   else {
     854     if ($This->{ColMode} =~ /^ColNum$/i) {
     855       # Is it a valid column number?
     856       if ($CompoundIDCol > scalar @{$This->{DataColLabels}}) {
     857         carp "Warning: ${ClassName}->_ValidateReadCompoundIDCol: Column number, $CompoundIDCol, specified using CompoundIDCol doesn't exist...";
     858         return 0;
     859       }
     860       $CompoundIDColNum = $CompoundIDCol - 1;
     861     }
     862     elsif ($This->{ColMode} =~ /^ColLabel$/i) {
     863       # Does this column exists?
     864       if (!exists $This->{DataColLabelToNumMap}{$CompoundIDCol}) {
     865         carp "Warning: ${ClassName}->_ValidateReadCompoundIDCol: Column name, $CompoundIDCol, specified using CompoundIDCol doesn't exist...";
     866         return 0;
     867       }
     868       $CompoundIDColNum = $This->{DataColLabelToNumMap}{$CompoundIDCol};
     869     }
     870   }
     871
     872   $This->{ValidCompoundIDCol} = 1;
     873   $This->{CompoundIDColNum} = $CompoundIDColNum;
     874   $This->{UseSequentialCompoundIDs} = $UseSequentialCompoundIDs;
     875
     876   return 1;
     877 }
     878
     879 # Validate fingerprints string column information...
     880 #
     881 sub _ValidateReadFingerprintsCol {
     882   my($This) = @_;
     883   my($FingerprintsColNum, $FingerprintsCol, $ColFound, $ColLabel, $ColNum);
     884
     885   $This->{ValidFingerprintsCol} = 0;
     886   $This->{FingerprintsColNum} = undef;
     887
     888   $FingerprintsColNum = undef;
     889   $FingerprintsCol = $This->{FingerprintsCol};
     890
     891   if ($FingerprintsCol =~ /^AutoDetect$/i) {
     892     # First column containing the word Fingerprints in its label...
     893
     894     $ColFound = 0;
     895     COLLABEL: for $ColLabel (@{$This->{DataColLabels}}) {
     896       if ($ColLabel =~ /Fingerprints/i) {
     897         $ColFound = 1;
     898         $ColNum = $This->{DataColLabelToNumMap}{$ColLabel};
     899         last COLLABEL;
     900       }
     901     }
     902     if (!$ColFound) {
     903       carp "Warning: ${ClassName}->_ValidateReadFingerprintsCol: Column label containing \"Fingerprints\" string in its name doesn't exist...";
     904       return 0;
     905     }
     906     $FingerprintsColNum = $ColNum;
     907   }
     908   else {
     909     if ($This->{ColMode} =~ /^ColNum$/i) {
     910       # Is it a valid column number?
     911       if ($FingerprintsCol > scalar @{$This->{DataColLabels}}) {
     912         carp "Warning: ${ClassName}->_ValidateReadFingerprintsCol: Column number, $FingerprintsCol, specified using FingerprintsCol doesn't exist...";
     913         return 0;
     914       }
     915       $FingerprintsColNum = $FingerprintsCol - 1;
     916     }
     917     elsif ($This->{ColMode} =~ /^ColLabel$/i) {
     918       # Does this column exists?
     919       if (!exists $This->{DataColLabelToNumMap}{$FingerprintsCol}) {
     920         carp "Warning: ${ClassName}->_ValidateReadFingerprintsCol: Column label, $FingerprintsCol, specified using FingerprintsCol doesn't exist...";
     921         return 0;
     922       }
     923       $FingerprintsColNum = $This->{DataColLabelToNumMap}{$FingerprintsCol};
     924     }
     925   }
     926
     927   $This->{ValidFingerprintsCol} = 1;
     928   $This->{FingerprintsColNum} = $FingerprintsColNum;
     929
     930   return 1;
     931 }
     932
     933 # Validate fingerprints string mode information...
     934 #
     935 sub _ValidateReadFingerprintsStringMode {
     936   my($This) = @_;
     937   my($FingerprintsBitVectorStringMode, $FingerprintsVectorStringMode, $FirstFingerprintsStringType, $FirstFingerprintsStringDescription, $TextFile, $Line, $FingerprintsColNum, $InDelim, $FingerprintsType, $FingerprintsDescription, @LineWords);
     938
     939   $This->{ValidFingerprintsStringMode} = 0;
     940
     941   $This->{FingerprintsBitVectorStringMode} = 0;
     942   $This->{FingerprintsVectorStringMode} = 0;
     943
     944   $This->{FirstFingerprintsStringType} = '';
     945   $This->{FirstFingerprintsStringDescription} = '';
     946
     947   $FingerprintsBitVectorStringMode = 0;
     948   $FingerprintsVectorStringMode = 0;
     949
     950   $FirstFingerprintsStringType = '';
     951   $FirstFingerprintsStringDescription = '';
     952
     953   $TextFile = $This->{Name};
     954
     955   if (!open TEXTFILE, "$TextFile") {
     956     croak "Error: ${ClassName}->New: Object can't be instantiated: Couldn't open input text file $TextFile: $! ...";
     957   }
     958
     959   # Skip column label line...
     960   $Line = TextUtil::GetTextLine(\*TEXTFILE);
     961
     962   # First first fingerprints data line...
     963   $Line = TextUtil::GetTextLine(\*TEXTFILE);
     964
     965   close TEXTFILE;
     966
     967   # Get first fingerprints type and description...
     968   $InDelim = $This->{Delim};
     969   @LineWords = TextUtil::SplitWords($Line, $InDelim);
     970
     971   $FingerprintsColNum = $This->{FingerprintsColNum};
     972
     973   ($FingerprintsType, $FingerprintsDescription) = Fingerprints::FingerprintsStringUtil::GetFingerprintsStringTypeAndDescription($LineWords[$FingerprintsColNum]);
     974
     975   if ($This->{FingerprintsStringMode} =~ /^FingerprintsBitVectorString$/i) {
     976     if ($FingerprintsType !~ /^FingerprintsBitVector$/i) {
     977       carp "Warning: ${ClassName}->_ValidateReadFingerprintsStringMode: First fingerprint string data type, $FingerprintsType, doesn't match value, FingerprintsBitVectorString, specified using \"FingerprintsStringMode\"...";
     978       return 0;
     979     }
     980     $FingerprintsBitVectorStringMode = 1;
     981     $FirstFingerprintsStringType = 'FingerprintsBitVector';
     982     $FirstFingerprintsStringDescription = $FingerprintsDescription;
     983   }
     984   elsif ($This->{FingerprintsStringMode} =~ /^FingerprintsVectorString$/i) {
     985     if ($FingerprintsType !~ /^FingerprintsVector$/i) {
     986       carp "Warning: ${ClassName}->_ValidateReadFingerprintsStringMode: First fingerprint string data type, $FingerprintsType, doesn't match value, FingerprintsVectorString, specified using \"FingerprintsStringMode\"...";
     987       return 0;
     988     }
     989     $FingerprintsVectorStringMode = 1;
     990     $FirstFingerprintsStringType = 'FingerprintsVector';
     991     $FirstFingerprintsStringDescription = $FingerprintsDescription;
     992   }
     993   else {
     994     # AutoDetect mode...
     995     if ($FingerprintsType =~ /^FingerprintsBitVector$/i) {
     996       $FingerprintsBitVectorStringMode = 1;
     997     }
     998     elsif ($FingerprintsType =~ /^FingerprintsVector$/i) {
     999       $FingerprintsVectorStringMode = 1;
    1000     }
    1001     else {
    1002       carp "Warning: ${ClassName}->_ValidateReadFingerprintsStringMode: First fingerprint string data type, $FingerprintsType, identified during, AutoDetect, value of \"FingerprintsStringMode\" is not valid; Supported fingerprints types: FingerprintBitVector or FingerprintsVector...";
    1003       return 0;
    1004     }
    1005     $FirstFingerprintsStringType = $FingerprintsType;
    1006     $FirstFingerprintsStringDescription = $FingerprintsDescription;
    1007   }
    1008
    1009   $This->{ValidFingerprintsStringMode} = 1;
    1010
    1011   $This->{FingerprintsBitVectorStringMode} = $FingerprintsBitVectorStringMode;
    1012   $This->{FingerprintsVectorStringMode} = $FingerprintsVectorStringMode;
    1013
    1014   $This->{FirstFingerprintsStringType} = $FirstFingerprintsStringType;
    1015   $This->{FirstFingerprintsStringDescription} = $FirstFingerprintsStringDescription;
    1016
    1017   return 1;
    1018 }
    1019
    1020 # Write fingerprints string generated from specified fingerprints, fingerprints-bit vector, or
    1021 # fingerprints vector object and other data to text file...
    1022 #
    1023 sub WriteFingerprints {
    1024   my($This, $FingerprintsObject, @DataColValues) = @_;
    1025
    1026   # Initialize data for current line...
    1027   $This->_InitializeWriteDataLine();
    1028
    1029   # Set fingerprints object...
    1030   $This->{FingerprintsObject} = $FingerprintsObject;
    1031
    1032   # Generate fingerprints string...
    1033   $This->_GenerateFingerprintsString();
    1034
    1035   # Set data line words...
    1036   $This->SetDataLineWords(@DataColValues);
    1037   push @{$This->{DataLineWords}}, $This->{FingerprintsString};
    1038
    1039   # Write data line..
    1040   $This->_WriteDataLine();
    1041
    1042   return $This;
    1043 }
    1044
    1045 # Write fingerprints string and other data to text file...
    1046 #
    1047 # Note:
    1048 #   o FingerprintsStringMode, BitStringFormat, BitsOrder, VectorStringFormat values
    1049 #     are ignored during writing of fingerprints and it's written to the file as it is.
    1050 #
    1051 #
    1052 sub WriteFingerprintsString {
    1053   my($This, $FingerprintsString, @DataColValues) = @_;
    1054
    1055   # Initialize data for current line...
    1056   $This->_InitializeWriteDataLine();
    1057
    1058   # Set fingerprints string...
    1059   $This->{FingerprintsString} = $FingerprintsString;
    1060
    1061   # Generate fingerprints object...
    1062   $This->_GenerateFingerprintsObject();
    1063
    1064   # Set data line words...
    1065   $This->SetDataLineWords(@DataColValues);
    1066   push @{$This->{DataLineWords}}, $FingerprintsString;
    1067
    1068   # Write data line..
    1069   $This->_WriteDataLine();
    1070
    1071   return $This;
    1072 }
    1073
    1074 # Initialize data line for reading...
    1075 #
    1076 sub _InitializeWriteDataLine {
    1077   my($This) = @_;
    1078
    1079   $This->{DataLine} = undef;
    1080   @{$This->{DataLineWords}} = ();
    1081
    1082   $This->{FingerprintsObject} = undef;
    1083   $This->{FingerprintsString} = undef;
    1084
    1085   return $This;
    1086 }
    1087
    1088 # Write fingerprints data line line...
    1089 #
    1090 sub _WriteDataLine {
    1091   my($This) = @_;
    1092   my($FileHandle, $Line);
    1093
    1094   if ($This->{FirstDataLineIO}) {
    1095     $This->_ProcessFirstDataLineWrite();
    1096   }
    1097
    1098   # Write out line words...
    1099   $Line = TextUtil::JoinWords(\@{$This->{DataLineWords}}, $This->{Delim}, $This->{OutQuote});
    1100
    1101   $This->{LineNum} += 1;
    1102   $FileHandle = $This->{FileHandle};
    1103   print $FileHandle "$Line\n";
    1104
    1105   $This->{DataLine} = $Line;
    1106
    1107   return $This;
    1108 }
    1109
    1110 # Process first write...
    1111 #
    1112 sub _ProcessFirstDataLineWrite {
    1113   my($This) = @_;
    1114   my($Line, $FileHandle);
    1115
    1116   $This->{FirstDataLineIO} = 0;
    1117
    1118   if ($This->GetMode() =~ /^Write$/i) {
    1119     # Write out column label line...
    1120     $Line = TextUtil::JoinWords(\@{$This->{DataColLabels}}, $This->{Delim}, $This->{OutQuote});
    1121
    1122     $This->{LineNum} += 1;
    1123     $FileHandle = $This->{FileHandle};
    1124     print $FileHandle "$Line\n";
    1125   }
    1126
    1127   return $This;
    1128 }
    1129
    1130 # Get ready for writing fingerprints text file...
    1131 #
    1132 sub _PrepareForWritingFingerprintsTextFileData {
    1133   my($This) = @_;
    1134   my($TextFile, $FileDir, $FileName, $FileExt, $OutDelim);
    1135
    1136   $TextFile = $This->{Name};
    1137   if (!$This->{Overwrite}) {
    1138     if (-e $TextFile) {
    1139       croak "Error: ${ClassName}->New: Object can't be instantiated: File, $TextFile, already exist. Use overwrite option...";
    1140     }
    1141   }
    1142
    1143   # Set up delimiter for writing file...
    1144
    1145   $FileDir = ""; $FileName = ""; $FileExt = "";
    1146   ($FileDir, $FileName, $FileExt) = FileUtil::ParseFileName($TextFile);
    1147
    1148   $OutDelim = ($FileExt =~ /^tsv$/i) ? "\t" : ($This->{OutDelim} =~ /semicolon/i ? "\;" : "\,");
    1149   $This->{Delim} = $OutDelim;
    1150
    1151   # Setup FingerprintsStringMode status...
    1152
    1153   $This->{FingerprintsBitVectorStringMode} = 0;
    1154   $This->{FingerprintsVectorStringMode} = 0;
    1155   $This->{ValidFingerprintsStringMode} = 0;
    1156
    1157   if ($This->{FingerprintsStringMode} =~ /^FingerprintsBitVectorString$/i) {
    1158     $This->{FingerprintsBitVectorStringMode} = 1;
    1159   }
    1160   elsif ($This->{FingerprintsStringMode} =~ /^FingerprintsVectorString$/i) {
    1161     $This->{FingerprintsVectorStringMode} = 1;
    1162   }
    1163
    1164   $This->{ValidFingerprintsStringMode} = ($This->{FingerprintsBitVectorStringMode} || $This->{FingerprintsVectorStringMode}) ? 1 : 0;
    1165
    1166   if ($This->{FingerprintsBitVectorStringMode}) {
    1167     $This->_SetDefaultBitStringFormat();
    1168     $This->_SetDefaultBitsOrder();
    1169   }
    1170   elsif ($This->{FingerprintsVectorStringMode}) {
    1171     $This->_SetDefaultVectorStringFormat();
    1172   }
    1173
    1174   return $This;
    1175 }
    1176
    1177 # Set default value for bit string format...
    1178 #
    1179 sub _SetDefaultBitStringFormat {
    1180   my($This) = @_;
    1181
    1182   if (!$This->{BitStringFormat}) {
    1183     $This->{BitStringFormat} = Fingerprints::FingerprintsStringUtil::GetDefaultBitStringFormat();
    1184   }
    1185
    1186   return $This;
    1187 }
    1188
    1189 # Set default value for bit string format...
    1190 #
    1191 sub _SetDefaultBitsOrder {
    1192   my($This) = @_;
    1193
    1194   if (!$This->{BitsOrder}) {
    1195     $This->{BitsOrder} = Fingerprints::FingerprintsStringUtil::GetDefaultBitsOrder();
    1196   }
    1197
    1198   return $This;
    1199 }
    1200
    1201 # Set default value for vector string format...
    1202 #
    1203 sub _SetDefaultVectorStringFormat {
    1204   my($This) = @_;
    1205
    1206   if (!$This->{VectorStringFormat} && $This->{FingerprintsObject}) {
    1207     $This->{VectorStringFormat} = Fingerprints::FingerprintsStringUtil::GetDefaultVectorStringFormat($This->{FingerprintsObject});
    1208   }
    1209
    1210   return $This;
    1211 }
    1212
    1213 # Generate fingerprints object using current fingerprints string...
    1214 #
    1215 sub _GenerateFingerprintsObject {
    1216   my($This) = @_;
    1217
    1218   $This->{FingerprintsObject} = undef;
    1219
    1220   if (!$This->{FingerprintsString}) {
    1221     return $This;
    1222   }
    1223
    1224   if ($This->{FingerprintsBitVectorStringMode}) {
    1225     $This->{FingerprintsObject} = Fingerprints::FingerprintsStringUtil::ParseFingerprintsBitVectorString($This->{FingerprintsString});
    1226   }
    1227   elsif ($This->{FingerprintsVectorStringMode}) {
    1228     $This->{FingerprintsObject} = Fingerprints::FingerprintsStringUtil::ParseFingerprintsVectorString($This->{FingerprintsString});
    1229   }
    1230   else {
    1231     return undef;
    1232   }
    1233
    1234   return $This;
    1235 }
    1236
    1237 # Generate fingerprints string using current fingerprints object...
    1238 #
    1239 sub _GenerateFingerprintsString {
    1240   my($This) = @_;
    1241
    1242   $This->{FingerprintsString} = '';
    1243
    1244   if (!$This->{FingerprintsObject}) {
    1245     return $This;
    1246   }
    1247
    1248   if ($This->{FingerprintsBitVectorStringMode}) {
    1249     $This->{FingerprintsString} = Fingerprints::FingerprintsStringUtil::GenerateFingerprintsString($This->{FingerprintsObject}, $This->{BitStringFormat}, $This->{BitsOrder});
    1250   }
    1251   elsif ($This->{FingerprintsVectorStringMode}) {
    1252     $This->{FingerprintsString} = Fingerprints::FingerprintsStringUtil::GenerateFingerprintsString($This->{FingerprintsObject}, $This->{VectorStringFormat});
    1253   }
    1254
    1255   return $This;
    1256 }
    1257
    1258 # Is it a fingerprints file?
    1259 sub IsFingerprintsTextFile ($;$) {
    1260   my($FirstParameter, $SecondParameter) = @_;
    1261   my($This, $FileName, $Status);
    1262
    1263   if ((@_ == 2) && (_IsFingerprintsTextFileIO($FirstParameter))) {
    1264     ($This, $FileName) = ($FirstParameter, $SecondParameter);
    1265   }
    1266   else {
    1267     $FileName = $FirstParameter;
    1268   }
    1269
    1270   # Check file extension...
    1271   $Status = FileUtil::CheckFileType($FileName, "csv tsv");
    1272
    1273   return $Status;
    1274 }
    1275
    1276 # Is it a FingerprintsTextFileIO object?
    1277 sub _IsFingerprintsTextFileIO {
    1278   my($Object) = @_;
    1279
    1280   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
    1281 }
    1282