Coverage Report - us.daveread.utility.formatcheck.format.FormatType
 
Classes in this File Line Coverage Branch Coverage Complexity
FormatType
57%
62/109
61%
17/28
2.75
 
 1  
 package us.daveread.utility.formatcheck.format;
 2  
 
 3  
 import java.util.*;
 4  
 import java.text.*;
 5  
 
 6  
 /**
 7  
  * <p>Title: FormatType
 8  
  * <p>Description: Container for a format defining acceptable structure of a
 9  
  *     file.
 10  
  * <p>Copyright: Copyright (c) 2005
 11  
  * <p>This program is free software; you can redistribute it and/or modify
 12  
  * it under the terms of the GNU General Public License as published by
 13  
  * the Free Software Foundation; either version 2 of the License, or
 14  
  * (at your option) any later version.
 15  
  * <p>This program is distributed in the hope that it will be useful,
 16  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 18  
  * GNU General Public License for more details.
 19  
  * <p>You should have received a copy of the GNU General Public License
 20  
  * along with this program; if not, write to the Free Software
 21  
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 22  
  * </p>
 23  
  *
 24  
  * @author David Read
 25  
  * @version $Id: FormatType.java,v 1.1.1.1 2006/05/22 02:14:09 daveread Exp $
 26  
  */
 27  
 public class FormatType
 28  
     implements Comparable {
 29  
 
 30  
   /** The display name of the format */
 31  
   private String name;
 32  
 
 33  
   /** The version identifier of the format */
 34  
   private String version;
 35  
 
 36  
   /** The record types making up the format */
 37  
   private RecordType recordTypes[];
 38  
 
 39  
   /** The previous Record Id that was processed */
 40  
   private String previousRecordId;
 41  
 
 42  
   /** Stored variables */
 43  
   private Map variables;
 44  
 
 45  
   /**
 46  
    * Creates an anonymous format.  The setName and setVersion should be used
 47  
    * at some point before completing the setup of the format.
 48  
    */
 49  
   public FormatType() {
 50  0
     this(null, null);
 51  0
   }
 52  
 
 53  
   /**
 54  
    * Creates a format with a display name and version.
 55  
    *
 56  
    * @param aName String The name for this format
 57  
    * @param aVersion String The version identifier for this format
 58  
    */
 59  7
   public FormatType(String aName, String aVersion) {
 60  7
     setName(aName);
 61  7
     setVersion(aVersion);
 62  7
     recordTypes = new RecordType[0];
 63  7
   }
 64  
 
 65  
   /**
 66  
    * Sets the format display name.
 67  
    *
 68  
    * @param aName String The display name for the format
 69  
    */
 70  
   protected void setName(String aName) {
 71  7
     name = aName;
 72  7
   }
 73  
 
 74  
   /**
 75  
    * Gets the format display name.
 76  
    *
 77  
    * @return String The format display name
 78  
    */
 79  
   public String getName() {
 80  1
     return name;
 81  
   }
 82  
 
 83  
   /**
 84  
    * Sets the version identifier.
 85  
    *
 86  
    * @param aVersion String The version identifier
 87  
    */
 88  
   protected void setVersion(String aVersion) {
 89  7
     version = aVersion;
 90  7
   }
 91  
 
 92  
   /**
 93  
    * Gets the version identifier.
 94  
    *
 95  
    * @return String The version identifier
 96  
    */
 97  
   public String getVersion() {
 98  1
     return version;
 99  
   }
 100  
 
 101  
   /**
 102  
    * Add a record type definition to this format.
 103  
    *
 104  
    * @param aRecordType RecordType The record type definition
 105  
    */
 106  
   public void addRecordType(RecordType aRecordType) {
 107  14
     List tempRecordTypes = new ArrayList(Arrays.asList(recordTypes));
 108  14
     tempRecordTypes.add(aRecordType);
 109  14
     recordTypes = (RecordType[]) tempRecordTypes.toArray(
 110  
         new RecordType[tempRecordTypes.size()]);
 111  14
   }
 112  
 
 113  
   /**
 114  
    * Retrieve the number of record types associated with this format.
 115  
    *
 116  
    * @return int The number of record types in this format
 117  
    */
 118  
   public int getNumRecordTypes() {
 119  1
     return recordTypes.length;
 120  
   }
 121  
 
 122  
   /**
 123  
    * Get record type at the requested index position from the set of
 124  
    * associated record types.
 125  
    *
 126  
    * @param aIndex int The index of the record type
 127  
    * @return RecordType The record type
 128  
    */
 129  
   public RecordType getRecordType(int aIndex) {
 130  1
     return recordTypes[aIndex];
 131  
   }
 132  
 
 133  
   /**
 134  
    * Resets the validation state in preparation for validating a new file.
 135  
    * Applications should assure this method is called before the first
 136  
    * call to the validate method to process an input file.
 137  
    */
 138  
   public void newValidation() {
 139  7
     previousRecordId = null;
 140  7
     variables = new HashMap();
 141  7
   }
 142  
 
 143  
   /**
 144  
    * Returns a formatted string breaking the input record up into
 145  
    * rows based on fields.  If the record type cannot be determined,
 146  
    * the output will split the input record into 30 character rows.
 147  
    *
 148  
    * @param aRecord String The record to be parsed and formatted
 149  
    * @return String[] The returned output-ready string
 150  
    */
 151  
   public String getReportableParsedRecord(String aRecord) {
 152  
     StringBuffer report;
 153  
     ValidationResult result;
 154  
     int matchedRecType;
 155  
     RecordType recordType;
 156  
     String parsedRecord[];
 157  
 
 158  2
     result = null;
 159  
 
 160  
     // Find the record type for this record
 161  2
     for (matchedRecType = 0; matchedRecType < recordTypes.length;
 162  0
          ++matchedRecType) {
 163  2
       result = recordTypes[matchedRecType].validate(aRecord, variables);
 164  2
       if (result.isValid() || result.isMatchLock()) {
 165  0
         break;
 166  
       }
 167  
     }
 168  
 
 169  2
     report = new StringBuffer();
 170  
 
 171  2
     if (matchedRecType < recordTypes.length) {
 172  2
       report.append("Column Numbers:                12345678901234567890123456789012345678901234567890\n");
 173  
 
 174  2
       recordType = recordTypes[matchedRecType];
 175  2
       parsedRecord = recordType.parse(aRecord);
 176  
 
 177  6
       for (int field = 0; field < recordType.getNumFieldTypes(); ++field) {
 178  4
         report.append( (recordType.getFieldType(field).getName() +
 179  
                         "                              ").substring(0, 30));
 180  4
         report.append(":");
 181  4
         if (parsedRecord.length > field) {
 182  4
           report.append(parsedRecord[field]);
 183  
         }
 184  4
         report.append("\n");
 185  
       }
 186  
     }
 187  
     else {
 188  
       // Unknown record type, format data with 30-character lines
 189  
       int start, end;
 190  0
       NumberFormat format = new DecimalFormat("0000");
 191  0
       start = 0;
 192  0
       report.append(
 193  
           "Column Numbers:   12345678901234567890123456789012345678901234567890\n");
 194  0
       while (start < aRecord.length()) {
 195  0
         end = start + 50;
 196  0
         if (end > aRecord.length()) {
 197  0
           end = aRecord.length();
 198  
         }
 199  0
         report.append("Columns ");
 200  0
         report.append(format.format(start + 1));
 201  0
         report.append("-");
 202  0
         report.append(format.format(end));
 203  0
         report.append(":");
 204  0
         report.append(aRecord.substring(start, end));
 205  0
         report.append("\n");
 206  
 
 207  0
         start = end;
 208  
       }
 209  
     }
 210  
 
 211  2
     return report.toString();
 212  
   }
 213  
 
 214  
   /**
 215  
    * Validate a record against this format.
 216  
    *
 217  
    * @param aRecord String The record to validate
 218  
    * @return ValidationResult The result of the validation
 219  
    */
 220  
   public ValidationResult validate(String aRecord, int aRecordNumber) {
 221  
     ValidationResult result;
 222  
     int matchedRecType, recType;
 223  
     String errorMessage;
 224  
 
 225  5
     result = null;
 226  
 
 227  
     // Find the record type for this record
 228  5
     for (matchedRecType = 0; matchedRecType < recordTypes.length;
 229  3
          ++matchedRecType) {
 230  8
       result = recordTypes[matchedRecType].validate(aRecord, variables);
 231  8
       if (result.isValid() || result.isMatchLock()) {
 232  3
         break;
 233  
       }
 234  
     }
 235  
 
 236  5
     result.setMatchedRecordTypeId(matchedRecType);
 237  
 
 238  
     // Check first occurs row restrictions
 239  15
     for (recType = 0; recType < recordTypes.length; ++recType) {
 240  
       // Test for must be first
 241  10
       if (recordTypes[recType].isMustBeFirst() && aRecordNumber == 1) {
 242  0
         result.addOpportunity();
 243  0
         if (recType != matchedRecType) {
 244  0
           if (matchedRecType < recordTypes.length) {
 245  0
             result.addDefectMessage("First record must be a " +
 246  
                                     recordTypes[recType] +
 247  
                                     " [Found: " +
 248  
                                     recordTypes[matchedRecType] + "]");
 249  
           }
 250  
           else {
 251  0
             result.addDefectMessage("First record must be a " +
 252  
                                     recordTypes[recType] +
 253  
                                     " [Found: No Matching Record]");
 254  
           }
 255  
         }
 256  
       }
 257  10
       else if (recordTypes[recType].getRequiredOccurRow() > 1 &&
 258  
                aRecordNumber == recordTypes[recType].getRequiredOccurRow()) {
 259  
         // Test for must be specific line number (other than first)
 260  0
         result.addOpportunity();
 261  0
         if (recType != matchedRecType) {
 262  0
           if (matchedRecType < recordTypes.length) {
 263  0
             result.addDefectMessage("Record " + aRecordNumber + " must be a " +
 264  
                                     recordTypes[recType].getDescription() +
 265  
                                     " [Found: " +
 266  
                                     recordTypes[matchedRecType].getDescription() +
 267  
                                     "]");
 268  
           }
 269  
           else {
 270  0
             result.addDefectMessage("Record " + aRecordNumber + " must be a " +
 271  
                                     recordTypes[recType].getDescription() +
 272  
                                     " [Found: No Matching Record]");
 273  
           }
 274  
         }
 275  
       }
 276  
     }
 277  
 
 278  
     // Check followsIds restrictions
 279  5
     errorMessage = previousRecordTypeCheck(matchedRecType);
 280  5
     if (errorMessage != null) {
 281  0
       result.addOpportunity();
 282  0
       result.addDefectMessage(errorMessage);
 283  
     }
 284  
 
 285  
     // Store previous record type id, if one was matched
 286  5
     if (matchedRecType < recordTypes.length) {
 287  5
       previousRecordId = recordTypes[matchedRecType].getId();
 288  
     }
 289  
     else {
 290  0
       previousRecordId = null;
 291  
     }
 292  
 
 293  5
     return result;
 294  
   }
 295  
 
 296  
   /**
 297  
    * Verify that the current record type is allowed to directly follow the
 298  
    * previous record type.
 299  
    *
 300  
    * @param aMatchedRecType int The current record type id
 301  
    *
 302  
    * @return String An error message if the record type cannot follow the
 303  
    *     previous record type, otherwise the string will be null.
 304  
    */
 305  
   private String previousRecordTypeCheck(int aMatchedRecType) {
 306  
     int previousIdIndex;
 307  
     String result, okayIds;
 308  
 
 309  5
     result = null;
 310  5
     okayIds = "";
 311  
 
 312  5
     if (aMatchedRecType < recordTypes.length && previousRecordId != null) {
 313  3
       if (recordTypes[aMatchedRecType].getNumFollowsIds() > 0) {
 314  0
         for (previousIdIndex = 0;
 315  0
              previousIdIndex < recordTypes[aMatchedRecType].getNumFollowsIds();
 316  0
              ++previousIdIndex) {
 317  0
           if (previousRecordId.equals(recordTypes[aMatchedRecType].
 318  
                                       getFollowsIds(previousIdIndex))) {
 319  0
             break;
 320  
           }
 321  0
           if (okayIds.length() > 0) {
 322  0
             okayIds += ",";
 323  
           }
 324  0
           okayIds += recordTypes[aMatchedRecType].getFollowsIds(previousIdIndex);
 325  
         }
 326  0
         if (previousIdIndex >= recordTypes[aMatchedRecType].getNumFollowsIds()) {
 327  0
           result = "Record id " + recordTypes[aMatchedRecType].getId() +
 328  
               " must follow a record with id in list (" + okayIds +
 329  
               "), previous record had id " + previousRecordId;
 330  
         }
 331  
       }
 332  
     }
 333  
 
 334  5
     return result;
 335  
   }
 336  
 
 337  
   /**
 338  
    * Test that the last record type was correct.  An error will only be reported
 339  
    * if there is a record type defined to occur last and this record type
 340  
    * is not that one.  If there is no record type defined to occur last,
 341  
    * or if the record type that did occur last was supposed to occur last,
 342  
    * the method will return a result with no errors.
 343  
    *
 344  
    * @param aLastMatchedRecordType int The last record type processed
 345  
    * @return ValidationResult The result of the validation
 346  
    */
 347  
   public ValidationResult validateLastCorrect(int aLastMatchedRecordType) {
 348  
     ValidationResult result;
 349  
     int recType;
 350  
 
 351  1
     result = new ValidationResult();
 352  
 
 353  3
     for (recType = 0; recType < recordTypes.length; ++recType) {
 354  
       // Test for must be first
 355  2
       if (recordTypes[recType].isMustBeLast()) {
 356  1
         result.addOpportunity();
 357  1
         if (recType != aLastMatchedRecordType) {
 358  0
           if (aLastMatchedRecordType >= 0 &&
 359  
               aLastMatchedRecordType < recordTypes.length) {
 360  0
             result.addDefectMessage("Last record must be a " +
 361  
                                     recordTypes[recType] +
 362  
                                     " [Found: " +
 363  
                                     recordTypes[aLastMatchedRecordType] + "]");
 364  
           }
 365  
           else {
 366  0
             result.addDefectMessage("Last record must be a " +
 367  
                                     recordTypes[recType] +
 368  
                                     " [Found: No Matching Record]");
 369  
           }
 370  
         }
 371  
       }
 372  
     }
 373  
 
 374  1
     return result;
 375  
   }
 376  
 
 377  
   /**
 378  
    * Convenience to return the format display name.
 379  
    *
 380  
    * @return String The display name of the format
 381  
    */
 382  
   public String toString() {
 383  0
     return getName();
 384  
   }
 385  
 
 386  
   // Begin Comparable interface
 387  
 
 388  
   public int compareTo(Object compare) {
 389  0
     return getName().compareTo( ( (FormatType) compare).getName());
 390  
   }
 391  
 
 392  
   // End Comparable interface
 393  
 
 394  
 }