-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRule.java
2524 lines (2038 loc) · 76.8 KB
/
Rule.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
**
** Copyright (c) 2014 Bill Lanahan
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in all
** copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
** SOFTWARE.
**
*/
package validate;
import validate.Validate.format;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Properties;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.regex.PatternSyntaxException;
/*
* Rule will be an instance for each RULE with an ID in the config file. There must be at least 1 for a working program.
*
* @author Bill Lanahan
*/
class Rule {
private final ArrayList<String> fileList = new ArrayList<String>();
private ArrayList<String> nvRegexUsed = new ArrayList<String>();
private Options ruleOption = null;
private int errCount;
private int lineNumber = 0;
private File inputFile = null;
private BufferedReader br = null;
private String dir = null;
private String f = null;
private String ruleID = null;
private String line = null;
private String tmpline = null;
private String configFileName = null;
private String lineCheckRegex = null;
private String lineSkipRegex = null;
private boolean continuationState = false;
boolean lineSkipRegexExists = false;
boolean lineCheckRegexExists = false;
// for genericLineEdits
StringBuilder linePrefixSuffixRegex = new StringBuilder ( 50 );
String linePrefixRegex = null;
String lineSuffixRegex = null;
String lineReplaceRegex = null;
boolean linePrefixRegexExists = false;
boolean lineSuffixRegexExists = false;
Pattern linePrefixSuffixPattern = null;
boolean lineReplaceFlag = false;
String[] replaceArray = null;
String replaceRegex = null;
Matcher m = null;
ArrayList<Pattern> lineFormatRegexList = new ArrayList<Pattern>(); // used with LINE format
ArrayList<Integer> fromLineList = new ArrayList<Integer>(); // used with LINE format
ArrayList<Integer> toLineList = new ArrayList<Integer>(); // used with LINE format
private Pattern lineSkipPattern = null;
private Pattern lineCheckPattern = null;
private Matcher lineSkipMatcher = null;
Object[] patternStringListArray; // used in delimited format
Pattern fullDelimitedLinePattern;
Matcher lineCheckMatcher = null;
boolean hadFirstError = false;
format ruleFormat = null;
HashMap<String, NVP> nvpHM = null ; // gets defined in RegexBlock
NVP nvp = null;
// next 3 NOT private used by RegexBlock
StringBuilder outDups = new StringBuilder(100);
StringBuilder outInfo = null;
StringBuilder outUnchecked = null;
Object[] patternListArray = null;
Rule () {
// default not used
}
Rule ( String ruleID, Options ruleOption, String f ) {
this.ruleID = ruleID;
this.ruleOption = ruleOption;
this.configFileName = f; // need in summary output "f" is the data/user file
}
// setter/getter
// the format will determine how the files to get validated are parsed
public void setFormat( format value ) {
this.ruleFormat = value;
}
public format getFormat( ) {
return ruleFormat;
}
public String getRuleID( ) {
return ruleID;
}
public void fileAdd( String ruleValue ) {
fileList.add( new String(ruleValue ) );
}
// this actually does the validation
void checkIt ( ) {
switch ( ruleFormat ) {
case NAME_VALUE:
doNameValue();
break;
case JAVA:
doJava();
break;
case DELIMITED:
doDelimited();
break;
case CUSTOM:
doCustom();
break;
case LINE:
doLine();
break;
case VALUE_LINE: // similar to lines but doesn't read from a file
doValueLine();
break;
case VALUE_DELIMITED: // similar to DELIMITED but doesn't read from a file
doValueDelimited();
break;
default:
System.err.println( "\nfatal: invalid <format=value> found in rule class.");
System.exit ( 15 );
}
}
/*
* a delimited file gets a regex for each column in the data
* after any COMMENT lines are skipped over
*/
private void doDelimited() {
lineSkipRegexExists = false;
lineCheckRegexExists = false;
boolean errorFieldUnderline = ruleOption.getErrorFieldUnderline();
char[] errorUnderline = null;
char[] temp = null;
String lineCheckRegex = null;
lineCheckPattern = null;
m = null;
if ( (lineSkipRegex = ruleOption.getLineSkipRegex()) != null ) {
lineSkipRegexExists = true;
}
if ( (lineCheckRegex = ruleOption.getLineCheckRegex()) != null ) {
lineCheckRegexExists = true;
lineCheckPattern = Pattern.compile( lineCheckRegex ); // already tested in options
}
// do the rest for EVERY file given
for (final String fileName : fileList) {
errCount = 0;
if ( fileName == null ) {
if ( ruleOption.getWarnRuleWithoutFile() ) {
System.err.println( "\nwarning: RULE with id <" + ruleID + "> has no <file=> specified; RULE is skipped.");
}
continue; // nothing else we can do with no file
}
if ( (dir = ruleOption.getDirPathString()) != null ) {
f = dir + fileName;
} else {
f = fileName;
}
lineNumber = 0;
try {
int expectedFieldCount = patternListArray.length; // number or Regexes in block
// set size appropriately, but if extra fields are found java will grow size
int ArrayListInitialSize = expectedFieldCount + 1 + ruleOption.getExtraFieldCount();
int actualFieldCount;
boolean hadError = false;
final StringBuilder delimiterRegexInParens = new StringBuilder();
delimiterRegexInParens.append( "(" ).append( ruleOption.getDelimiterRegex() ).append( ")" );
final Pattern delimiterPattern = Pattern.compile( delimiterRegexInParens.toString() );
ArrayList<Integer> delimiterStart = new ArrayList<Integer>( ArrayListInitialSize );
ArrayList<Integer> delimiterEnd = new ArrayList<Integer>( ArrayListInitialSize );
int delimiterIndex = 0;
int lineLength = 0;
StringBuilder fields;
String[] lineArray = null;
lineSkipPattern = Pattern.compile( lineSkipRegex );
Matcher delimiterMatcher = null;
inputFile = new File(f);
br = new BufferedReader(new FileReader(inputFile));
outInfo = new StringBuilder( 500 );
errCount = 0;
lineNumber = 0;
String EOLdelimiterAtEOL = ruleOption.getDelimiterRegex() + '$';
Pattern EOLPat = Pattern.compile( EOLdelimiterAtEOL ); // will use on every non-null (non-comment too) line of input
// handle editing
// gets all options for prefix, suffix, replace
genericLineEdittingPrep();
while ( true ) { // read all the lines in the file to validate
fields = new StringBuilder( 500 );
hadError = false;
actualFieldCount = 0;
lineLength = 0;
line = br.readLine();
if ( line == null ) {
// file is done lets close and then check status
checkEOFstatus( outInfo.toString(), lineNumber, errCount, "", f); // sends the DATA file
outInfo = new StringBuilder( 500 );
break;
}
// see if we should skip lines
if ( lineSkipRegexExists ) {
lineSkipMatcher = lineSkipPattern.matcher( line );
if ( lineSkipMatcher.find() ) {
continue;
}
}
/*
* use of genericLineEdditing to adjust line before validation
*/
if ( lineReplaceFlag ) {
//line = line.replaceAll( replaceArray[0], replaceArray[1]);
Pattern p = Pattern.compile( replaceArray[0] );
Matcher mm = p.matcher( line );
if ( mm.find() ) {
line = mm.replaceAll( replaceRegex );
}
}
/*
* we can trim off the start and/or end of each line BEFORE we look at the remainder "nv" pair
*/
if ( linePrefixRegexExists || lineSuffixRegexExists ) {
m = linePrefixSuffixPattern.matcher( line );
line = m.replaceAll( "" );
}
/*
* OK but we could still have more than we are interested in so last global check
* determines if we really parse the line
*/
// use it to decide skip it or check it
if ( lineCheckRegexExists ) {
lineCheckMatcher = lineCheckPattern.matcher( line );
if (! lineCheckMatcher.find() ) {
continue;
}
}
// end of genericLineEditting
// use it to decide skip it or check it
if ( lineCheckRegexExists ) {
lineCheckMatcher = lineCheckPattern.matcher( line );
if ( ! lineCheckMatcher.find() ) {
continue;
}
}
lineNumber++;
// for debugging let user see what is being checked
if ( (ruleOption.getLineShow() == true)) {
outInfo.append("\nvalidating: <").append(lineNumber).append(">: ").append(line);
}
lineLength = line.length();
delimiterMatcher = delimiterPattern.matcher( line );
/*
* expectedFieldCount=the number or regexes we have, thus the number of fields=actualFieldCount we expect
* BUT
* we have multiple end of line delimiter situations e.g.
* expectedFieldCount=2 so we expect 2 fields, we could have
* ONE:TWO
* ONE:TWO:
* less than 1 : would be an error TOO FEW FIELDS
* more than 2 : would be an error TOO MANY
* if : = 1 EOLdelimiter=false OK
* if : = 2 EOLdelimiter=true OK
* if : = 1 or 2 AND EOLdelimiter=optional OK
*/
// lets see what the input line ends with
Matcher EOLmatch = EOLPat.matcher( line );
if ( EOLmatch.find() ) {
// if here line ends in a delimiter
if ( "false".equals(ruleOption.getEOLdelimiter() ) ) {
// line is no good since no EOLdelimiter wanted and its there
outInfo.append("\n").append(lineNumber).append("\n").append(line).append("\n <error, final delimiter not expected>\n");
errCount++;
hadError = true;
Validate.rc( 2 ); // record error
}
// if here: we had an EOLdelimiter lets trim it off - but the line might not be correct
line = line.replaceAll( EOLdelimiterAtEOL, "");
} else {
// EOLdelimiter is NOT there: if optional its ok, but if required its error
if ( "true".equals( ruleOption.getEOLdelimiter() ) ) {
outInfo.append("\n").append(lineNumber).append(":\n").append(line).append("\n <error, final delimiter missing>\n");
errCount++;
hadError = true;
Validate.rc( 2 ); // record error
}
}
EOLmatch = null;
// since EOL is taken care of now we need to be concerned with actual fields of data
// ***** so the rest below is ONLY needed if we have NOT had an error
boolean fieldErr = false;
if ( hadError == false ) {
delimiterIndex = 0;
if ( errorFieldUnderline ) {
temp = new char[ lineLength ];
Arrays.fill( temp, '0' );
}
delimiterIndex = 0;
while ( delimiterMatcher.find() ) { // get the start and end of each delimiter
delimiterStart.add( delimiterMatcher.start() );
delimiterEnd.add( delimiterMatcher.end() );
if ( errorFieldUnderline ) {
Arrays.fill( temp, delimiterStart.get(delimiterIndex), delimiterEnd.get(delimiterIndex), '1');
}
delimiterIndex++;
}
lineArray = line.split( ruleOption.getDelimiterRegex(), delimiterIndex + 1);
// from the delimiterArray we can build a str + start/end arrays needed only for underline
int[] stringStart = null;
int[] stringEnd = null;
boolean done = false;
actualFieldCount = lineArray.length;
/*
* because we allow a trailing field in certain cases of EOL we need to
* check more that actual and expected count
*/
if ( actualFieldCount < expectedFieldCount ) {
// failure
// add a test in case it was just whitespace
// only in the "all case"
if ( ruleOption.getErrorReportDetails() == 'a' ) {
if ( line.matches("\\s*") ) {
outInfo.append("\n\n").append(lineNumber).append(
": << error, input is just whitespace, therefore less fields than expected for the give regexes. Consider adding to lineSkipRegex. >>\n");
} else {
outInfo.append("\n\n").append(lineNumber).append(":\n").append(line).append("\n <error, less fields than expected for the given regexes>\n");
}
}
errCount++;
hadError = true;
Validate.rc( 2 ); // record error
done = true;
} else if ( actualFieldCount == expectedFieldCount ) {
; // perfect just check fields
} else {
// so actual is greater
// special case: the user could want extra end fields but not test them
if ( ruleOption.getExtraFieldCount() >= 1 ) {
if ( actualFieldCount > expectedFieldCount + ruleOption.getExtraFieldCount() ) {
// too many call it an error
// only in the "all case"
if ( ruleOption.getErrorReportDetails() == 'a' ) {
outInfo.append("\n").append(lineNumber).append(":\n").append(line).append("\n <error, <").append(
actualFieldCount - expectedFieldCount).append("> extra field(s) exceed the count of <").append(
ruleOption.getExtraFieldCount() ).append("> that was specified>");
}
errCount++;
hadError = true;
Validate.rc( 2 );
} else {
if (ruleOption.getWarnExtraFields() == true ) {
outInfo.append("\n").append(lineNumber).append(":\n").append(line).append("\n <warning, <").append(
actualFieldCount - expectedFieldCount).append("> extra field(s), only the first <").append(
expectedFieldCount).append("> are validated>");
}
Validate.rc( 1 );
}
} else if ( ruleOption.getExtraFieldCount() < 0 ) {
; // just ignore them without warning
} else {
// by default extra == 0 so the user has an error to deal with
// only in the "all case"
if ( ruleOption.getErrorReportDetails() == 'a' ) {
outInfo.append("\n").append(lineNumber).append(":\n").append(
line).append(" <error, too many fields for the given regexes> ");
}
errCount++;
hadError = true;
Validate.rc( 2 ); // record error
done = true;
}
} // end of if for had delimiter
// before we check if each field matched we want to know the position of strings IF
// the user wants underlining else don't bother
// we had the right delimiter state above so need to do fields
if ( ! done ) {
if ( errorFieldUnderline ) {
errorUnderline = new char[ lineLength ];
Arrays.fill( errorUnderline, ' ' ); // start clean and update to ^ as needed
stringStart = new int[ lineArray.length ];
stringEnd = new int[ lineArray.length ];
int index = 0;
boolean marking = false;
int p = 0;
for ( p = 0; p < lineLength; p++ ) {
// walk the temp[] and set string start/end values
if ( marking == false ) {
if ( temp != null && temp[ p ] == '0' ) {
marking = true;
stringStart[ index ] = p;
}
} else {
// marking = true
if ( temp[ p ] == '1' ) {
stringEnd[ index ] = p;
marking = false;
index++;
}
}
}
if ( marking == true ) {
// need to close the stringEnd
stringEnd[ index ] = p;
}
} // end if errorFieldUnderline
// go field by field
// we will still use the groups captured to determine string lengths
int j = 0;
for ( ; j < expectedFieldCount; j++ ) {
boolean justNull = false;
m = ((Pattern)patternListArray[ j ]).matcher( lineArray[ j ] ); // reset the matcher for the next pattern
if ( ! m.find() ) { // NO-Match=error
fieldErr = true;
Validate.rc( 2 ); // record error
if ( fields.length() > 0 ) {
fields.append( "," );
} else {
fields.append( ":" );
}
// special case mark a null field that failed
if ( lineArray[j].length() == 0 ) {
justNull = true;
fields.append( "^" );
}
fields.append( j + 1 ); // increment field so we count from 1 not Zero
if ( errorFieldUnderline && justNull == false ) {
if ( lineLength > 0 && fieldErr ) {
Arrays.fill( errorUnderline, stringStart[ j ] , stringEnd[ j ], '^' );
} else {
outInfo.append("\n\n").append(lineNumber).append(":\n").append(" < line is only a newline character >\n");
}
}
}
} // end of checking field by field
// now only for case of allowed extra fields && warning on
if (errorFieldUnderline && ruleOption.getExtraFieldCount() >= 1 && ruleOption.getWarnExtraFields() ) {
for ( ; j < actualFieldCount; j++ ) {
Arrays.fill( errorUnderline, stringStart[ j ] , stringEnd[ j ], '?' );
}
}
if ( hadError == false && fieldErr == true ) {
errCount++;
Validate.rc( 2 ); // record error
}
}
}
if ( hadError == false && fieldErr == false ) {
// for debugging
if ( ruleOption.getShowValidData() == true ) {
outInfo.append("\n\n").append(lineNumber).append(": valid\n").append(line).append("\n");
}
continue;
}
// how to report findings on error
if ( (hadFirstError == false) && (ruleOption.getErrorReportDetails() != 'e') ) {
hadFirstError = true;
}
switch ( ruleOption.getErrorReportDetails() ) {
case 'n':
outInfo.append(lineNumber).append(",");
break;
case 'l':
outInfo.append(line);
break;
case 'a':
if ( fieldErr ) {
outInfo.append("\n").append(lineNumber);
}
if ( ruleOption.getErrorFieldUnderline() ) {
if ( fieldErr == true ) {
// user wants bad fields underlined with '^'
// we have a line (with correct field count) so can report it
outInfo.append(fields).append("\n").append(line).append("\n");
// its possible there was an error but nothing to underline because of a null field
if ( hadFirstError ) {
outInfo.append( new String( errorUnderline ) );
}
}
}
break;
default:
// got nothing, so show nothing
break;
}
if ( ruleOption.getErrorReportDetails() == 'l' ) {
outInfo.append("\n");
}
} // end of while
} catch ( final IOException ioe ) {
System.err.println( "\nfatal: can't read input file <" + fileName + ">, RULE with id <" + ruleID + ">. " + ioe.getMessage() );
System.exit( 50 );
} catch ( final PatternSyntaxException pse) {
System.err.println( "\nfatal: option <delimiterString> RULE with id <" + ruleID + "> does not compile. " );
System.exit( 50 );
} finally {
br = null;
inputFile = null;
}
// clean up
nvpHM = null;
} // end of for fileList
}
/*
* doCustom - similar to NV formatting, but with less restrictions on the name; more flexibilities on checking
*/
private void doCustom() {
m = null; // for matcher
lineSkipRegex = null;
replaceArray = new String[2];
replaceRegex = null;
lineSkipRegexExists = false;
lineCheckRegexExists = false;
lineCheckPattern = null;
lineCheckRegex = null;
int itemsChecked = 0;
// handle the line skip regex pattern, to be used later
if ( (lineSkipRegex = ruleOption.getLineSkipRegex()) != null ) {
lineSkipRegexExists = true;
try {
lineSkipPattern = Pattern.compile( lineSkipRegex );
} catch (final PatternSyntaxException e) {
System.err.println( "\nerror: RULE with id <" + ruleID + "> had option <regexCommentRegex>, which did not compile. " + e.getMessage());
System.exit( 15 );
}
}
// handle editing
// gets all options for prefix, suffix, replace
genericLineEdittingPrep();
if ( ! Validate.testMode ) {
// in testMode we may not have the real file, so we can't pass to java
File inputFile = null;
if ( (nvpHM == null) || nvpHM.isEmpty() ) {
System.err.println( "\nfatal: no REGEX data was found in RULE with id <" + ruleID + ">. " );
System.exit( 15 );
}
StringBuilder outDupsNameVar = null;
// iterate over the file list of name=value files in this Rule
for ( final String file : fileList ) {
// need clean for each file tested
outInfo = new StringBuilder( 100 );
outUnchecked = new StringBuilder( 100 );
String line = null;
String[] nvArray = new String[2];
itemsChecked = 0;
errCount = 0;
if ( file == null ) {
if ( ruleOption.getWarnRuleWithoutFile() ) {
System.err.println( "\nwarning: RULE with id <" + ruleID + "> has no <file=> specified; RULE is skipped.");
}
continue; // nothing else we can do with no file
}
if ( nvRegexUsed != null ) { // so we can start fresh for next file
nvRegexUsed = new ArrayList<String>();
}
outDupsNameVar = new StringBuilder(100); // declare here so its fresh for each file
lineNumber = 0;
try {
/*
* we need to read each non-comment line in the Custom file and test
*/
if ( (dir = ruleOption.getDirPathString()) != null ) {
f = dir + file;
} else {
f = file;
}
inputFile = new File( f );
br = new BufferedReader( new FileReader( inputFile ) );
short lineNumber = 0;
itemsChecked = 0;
while ( true ) {
line = br.readLine();
if ( line == null ) {
break; // we're done
}
lineNumber++;
// will handle backslash continued lines if needed
/*
* STEP 1. must do multi-line first so we do't loose info
*/
final String strReturned = continuationProcessor( line ) ;
if ( strReturned.length() == 0 ) {
continue;
} else {
line = strReturned;
}
/*
* STEP 2. first hurdle is see if its a comment/ignore line to be skipped
*/
// if comment skip it
if ( lineSkipRegexExists ) {
lineSkipMatcher = lineSkipPattern.matcher( line );
if ( lineSkipMatcher.find() ) {
continue;
}
}
/*
* use of genericLineEdditing to adjust line before validation
*/
// Step 2A.
// custom only has this
if ( lineReplaceFlag ) {
//line = line.replaceAll( replaceArray[0], replaceArray[1]);
Pattern p = Pattern.compile( replaceArray[0] );
Matcher mm = p.matcher( line );
if ( mm.find() ) {
line = mm.replaceAll( replaceRegex );
}
}
/*
* Step 3A & B we can trim off the start and/or end of each line BEFORE we look at the remainder "nv" pair
*/
if ( linePrefixRegexExists || lineSuffixRegexExists ) {
m = linePrefixSuffixPattern.matcher( line );
line = m.replaceAll( "" );
}
/*
* Step 4. OK but we could still have more than we are interested in so last global check
* determines if we really parse the line
*/
// use it to decide skip it or check it
if ( lineCheckRegexExists ) {
lineCheckMatcher = lineCheckPattern.matcher( line );
if (! lineCheckMatcher.find() ) {
continue;
}
}
// end of genericLineEditting
// for debugging let user see what is being checked
if ( ruleOption.getLineShow() == true ) {
outInfo.append("\nvalidating: <" + lineNumber + ">: " + line);
}
/*
* ok we really are interested in this line so parse it
*/
itemsChecked++;
// tokenize the line
nvArray = line.split(ruleOption.getDelimiterRegex(), 2); // user must be careful if they have spaces
nvArray[0] = nvArray[0].trim();
if ( nvArray.length > 2 ) {
System.err.println("\nerror: invalid format for NAME<delimiter>VALUE on line <" + lineNumber + "> line <" + line + ">.");
Validate.rc( 2 ); // recorded error level
return;
} else if ( (nvArray.length == 2) && (nvArray[0] == null) && (nvArray[1] == null) ) {
System.err.println("\nerror: invalid format for NAME<delimiter>VALUE on line <" + lineNumber + ">, both NAME and VALUE are null.");
Validate.rc( 2 ); // recorded error level
return;
} else if ( (nvArray.length == 2) && (nvArray[0] == null) ) {
System.err.println("\nerror: invalid format for NAME<delimiter>VALUE on line <" + lineNumber + ">, NAME is null.");
Validate.rc( 2 );// recorded error level
return;
} else if ( (nvArray.length == 2) && (nvArray[1] == null) ) {
System.err.println("\nerror: invalid format for NAME<delimiter>VALUE on line <" + lineNumber + ">, VALUE is null.");
Validate.rc( 2 ); // recorded error level
return;
}
/*
* so the nvArray holds the name <regex> value that we will VALIDATE for the user
* nvRegexUsed - names from the users input need to track for missing names
* the nvpHM holds the names and regexes that we will TEST TO
*
* there are multiple outcomes
* silence - if what we expect to VALIDATE matches the TEST
* error - if what we expect fails the match
* warning - (depends on options) if we did NOT FIND something that we had a TEST for
* waring - (depends on options) if we found a second (or more) occurrence of the name to TEST (a duplicate)
* warning - (depends on options) if the user file had a name that we did NOT test
*/
// see if its a name in the users file
// yes the regexUsed means it WAS used to check a name already - so the name is a dup
// not the regex (regex duplicates were checked in Regex object
if ( (nvRegexUsed != null) && nvRegexUsed.contains( nvArray[0] ) ) {
// we have a duplicate
if ( ruleOption.getWarnDuplicates() ) {
if ( outDupsNameVar.length() > 0 ) {
outDupsNameVar.append(", ").append( nvArray[0] );
} else {
outDupsNameVar.append( nvArray[0] );
}
}
} else {
// we need to save the name of NV pair we had in the file being examined for future
if ( nvArray != null ) {
nvRegexUsed.add( nvArray[0] ); // saved for missing test at end
}
}
/*
* if it was in a user file but NOT in the config file, its extra
*/
if ( nvpHM.containsKey( nvArray[0] ) == false ) {
itemsChecked--; // since it was already counted
// we have an extra property
if ( outUnchecked.length() > 0 ) {
outUnchecked.append(", ").append( nvArray[0] );
} else {
outUnchecked.append( nvArray[0] );
}
continue;
}
// retrieve the object so we can access the regex
nvp = nvpHM.get( nvArray[0] );
// do WORK - based on the correct name object
if ( ! nvp.validateValueWithRegex( nvArray[1] ) ) {
errCount++;
switch ( ruleOption.getErrorReportDetails() ) {
case 'n':
outInfo.append(lineNumber).append(",");
break;
case 'l':
outInfo.append("\n").append(nvArray[0]).append("=").append(nvArray[1]);
break;
case 'a':
outInfo.append("\n").append(lineNumber).append(": fail\n").append("NAME <").append(nvArray[0]).append("> VALUE <").
append(nvArray[1]).append("> does not match regex: ").append(nvp.getValue()).append("\n");
break;
default:
// got nothing, so show nothing
break;
}
if ( ruleOption.getErrorReportDetails() == 'n' ) {
outInfo.append("\n");
}
} else {
if ( ruleOption.getShowValidData() == true ) {
outInfo.append("\n").append(nvp.getLineNumber()).append(": valid\nNAME <").append(nvArray[0]).append(">\n");
}
}
} // end of while line reading
} catch ( final IOException io ) {
System.err.println( "\nfatal: error reading file <" + f + "> in RULE with id <" + ruleID + ">. " + io.getMessage() );
System.exit( 19 );
} catch ( final IllegalArgumentException iae ) {
System.err.println( "\nfatal: error reading file <" + f + "> in RULE with id <" + ruleID + ">. " + iae.getMessage() );
System.exit( 19 );
}
checkEOFstatus( outInfo.toString(), itemsChecked, errCount, outUnchecked.toString(), outDups.toString(), outDupsNameVar.toString(), f );
}
}
// clean up
nvpHM = null;
}
/*
* process the file(s) to be validated using the formatting and processing for a LINE
*/
private void doLine() {
String lineSkipRegex = null;
char[] lineUnderline = null;
lineCheckRegexExists = false;
lineSkipRegexExists = false;
lineCheckRegex = null;
Pattern lineCheckPattern = null;
m = null; // for matcher
if ( lineFormatRegexList.isEmpty() ) {
System.err.println( "\nfatal: there were no saved, regexes for rule with ID <" + ruleID + ">");
System.exit( 96 );
}
if ( (lineSkipRegex = ruleOption.getLineSkipRegex()) != null ) {
lineSkipRegexExists = true;
try {
lineSkipPattern = Pattern.compile( lineSkipRegex );
} catch (final PatternSyntaxException e) {
System.err.println( "\nerror: RULE with id <" + ruleID + "> had option <regexCommentRegex>, which did not compile. " + e.getMessage());
System.exit( 15 );
}
}
if ( (lineCheckRegex = ruleOption.getLineCheckRegex()) != null ) {
lineCheckRegexExists = true;
lineCheckPattern = Pattern.compile( lineCheckRegex ); // already tested in options
}
// handle editing
// gets all options for prefix, suffix, replace
genericLineEdittingPrep();
// can have many files
for (final String fileName : fileList) {
errCount = 0;
if ( fileName == null ) {
if ( ruleOption.getWarnRuleWithoutFile() ) {
System.err.println( "\nwarning: RULE with id <" + ruleID + "> has no <file=> specified; RULE skipped.");
}
continue; // nothing we can do
}
if ( (dir = ruleOption.getDirPathString()) != null ) {
f = dir + fileName;
} else {
f = fileName;
}
try {
br = new BufferedReader( new FileReader( new File( f ) ));
outInfo = new StringBuilder( 100 );
errCount = 0;
lineNumber = 0;
while ( true ) {
line = br.readLine();
if ( line == null ) {
// file is done lets close and then check status
checkEOFstatus( outInfo.toString(), lineNumber, errCount, null, f ); // sends DATA file
break;