| EmailDocumentHandler |

    1   /*
    2    *  EmailDocumentHandler.java
    3    *
    4    *  Copyright (c) 1998-2001, The University of Sheffield.
    5    *
    6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
    7    *  software, licenced under the GNU Library General Public License,
    8    *  Version 2, June 1991 (in the distribution as file licence.html,
    9    *  and also available at http://gate.ac.uk/gate/licence.html).
    10   *
    11   *  Cristian URSU,  3/Aug/2000
    12   *
    13   *  $Id: EmailDocumentHandler.java,v 1.24 2001/11/08 17:23:31 cursu Exp $
    14   */
    15
    16  package gate.email;
    17
    18  import java.util.*;
    19  import java.io.*;
    20
    21  import gate.corpora.*;
    22  import gate.util.*;
    23  import gate.*;
    24  import gate.event.*;
    25
    26  import junit.framework.*;
    27
    28  /**
    29    * This class implements the behaviour of the Email reader
    30    * It takes the Gate Document representing a list with e-mails and
    31    * creates Gate annotations on it.
    32    */
    33  public class EmailDocumentHandler{
    34
    35    /** Debug flag */
    36    private static final boolean DEBUG = false;
    37
    38    private String content = null;
    39    private long documentSize = 0;
    40
    41    /**
    42      * Constructor used in tests mostly
    43      */
    44    public EmailDocumentHandler() {
    45      setUp();
    46    }//EmailDocumentHandler
    47
    48    /**
    49      * Constructor initialises some private fields
    50      */
    51    public EmailDocumentHandler( gate.Document aGateDocument,
    52                                 Map  aMarkupElementsMap,
    53                                 Map  anElement2StringMap
    54                                ) {
    55
    56      gateDocument = aGateDocument;
    57
    58      // gets AnnotationSet based on the new gate document
    59      if (basicAS == null)
    60        basicAS = gateDocument.getAnnotations(
    61                                  GateConstants.ORIGINAL_MARKUPS_ANNOT_SET_NAME);
    62
    63      markupElementsMap = aMarkupElementsMap;
    64      element2StringMap = anElement2StringMap;
    65      setUp();
    66    }// EmailDocumentHandler
    67
    68    /**
    69      * Reads the Gate Document line by line and does the folowing things:
    70      * <ul>
    71      * <li> Each line is analized in order to detect where an e-mail starts.
    72      * <li> If the line belongs to an e-mail header then creates the
    73      *      annotation if the markupElementsMap allows that.
    74      * <li> Lines belonging to the e-mail body are placed under a Gate
    75      *      annotation called messageBody.
    76      * </ul>
    77      */
    78    public void annotateMessages() throws IOException,
    79                                          gate.util.InvalidOffsetException {
    80      // obtain a BufferedReader form the Gate document...
    81      BufferedReader gateDocumentReader = null;
    82      // Get the string representing the content of the document
    83      // It is used inside CreateAnnotation method
    84      content = gateDocument.getContent().toString();
    85      // Get the sieze of the Gate Document. For the same purpose.
    86      documentSize = gateDocument.getContent().size().longValue();
    87
    88  //    gateDocumentReader = new BufferedReader(new InputStreamReader(
    89  //              gateDocument.getSourceUrl().openConnection().getInputStream()));
    90      gateDocumentReader = new BufferedReader(new InputStreamReader(
    91                                  new ByteArrayInputStream(content.getBytes())));
    92
    93      // for each line read from the gateDocumentReader do
    94      // if the line begins an e-mail message then fire a status listener, mark
    95      // that we are processing an e-mail, update the cursor and go to the next
    96      // line.
    97
    98      // if we are inside an e-mail, test if the line belongs to the message
    99      // header
    100     // if so, create a header field annotation.
    101
    102     // if we are inside a a body and this is the first line from the body,
    103     // create the message body annotation.
    104     // Otherwise just update the cursor and go to the next line
    105
    106     // if the line doesn't belong to an e-mail message then just update the
    107     // cursor.
    108     // next line
    109
    110     String line = null;
    111     String aFieldName = null;
    112
    113     long cursor = 0;
    114     long endEmail = 0;
    115     long startEmail = 0;
    116     long endHeader = 0;
    117     long startHeader = 0;
    118     long endBody = 0;
    119     long startBody = 0;
    120     long endField = 0;
    121     long startField = 0;
    122
    123     boolean insideAnEmail   = false;
    124     boolean insideHeader    = false;
    125     boolean insideBody      = false;
    126     boolean emailReadBefore = false;
    127     boolean fieldReadBefore = false;
    128
    129     long nlSize = detectNLSize();
    130
    131     //Out.println("NL SIZE = " + nlSize);
    132
    133     // read each line from the reader
    134     while ((line = gateDocumentReader.readLine()) != null){
    135       // Here we test if the line delimitates two e-mail messages.
    136       // Each e-mail message begins with a line like this:
    137       // From P.Fairhurst Thu Apr 18 12:22:23 1996
    138       // Method lineBeginsMessage() detects such lines.
    139       if (lineBeginsMessage(line)){
    140             // Inform the status listener to fire only
    141             // if no. of elements processed.
    142             // So far is a multiple of ELEMENTS_RATE
    143           if ((++ emails % EMAILS_RATE) == 0)
    144             fireStatusChangedEvent("Reading emails : " + emails);
    145           // if there are e-mails read before, then the previous e-mail
    146           // ends here.
    147           if (true == emailReadBefore){
    148             // Cursor points at the beggining of the line
    149             // E-mail and Body ends before the \n char
    150             // Email ends as cursor value indicates
    151             endEmail = cursor - nlSize ;
    152             // also the e-mail body ends when an e-mail ends
    153             endBody = cursor - nlSize;
    154             //Annotate an E-mail body (startBody, endEmail)
    155             createAnnotation("Body",startBody,endBody,null);
    156             //Annotate an E-mail message(startEmail, endEmail) Email starts
    157             createAnnotation("Message",startEmail,endEmail,null);
    158           }
    159           // if no e-mail was read before, now there is at list one message
    160           // read
    161           emailReadBefore = true;
    162           // E-mail starts imediately from the beginning of this line which
    163           // sepatates 2 messages.
    164           startEmail = cursor;
    165           // E-mail header starts also from here
    166           startHeader = cursor;
    167           // The cursor is updated with the length of the line + the
    168           // new line char
    169           cursor += line.length() + nlSize;
    170           // We are inside an e-mail
    171           insideAnEmail = true;
    172           // Next is the E-mail header
    173           insideHeader = true;
    174           // No field inside header has been read before
    175           fieldReadBefore = false;
    176           // Read the next line
    177           continue;
    178       }//if (lineBeginsMessage(line))
    179       if (false == insideAnEmail){
    180         // the cursor is update with the length of the line +
    181         // the new line char
    182         cursor += line.length() + nlSize;
    183         // read the next line
    184         continue;
    185       }//if
    186       // here we are inside an e-mail message (inside Header or Body)
    187       if (true == insideHeader){
    188         // E-mail spec sais that E-mail header is separated by E-mail body
    189         // by a \n char
    190         if (line.equals("")){
    191           // this \n sepatates the header of an e-mail form its body
    192           // If we are here it means that the header has ended.
    193           insideHeader  = false;
    194           // e-mail header ends here
    195           endHeader = cursor - nlSize;
    196           // update the cursor with the length of \n
    197           cursor += line.length() + nlSize;
    198           // E-mail body starts from here
    199           startBody = cursor;
    200           // if fields were read before, it means that the e-mail has a header
    201           if (true == fieldReadBefore){
    202             endField = endHeader;
    203             //Create a field annotation (fieldName, startField, endField)
    204             createAnnotation(aFieldName, startField, endField, null);
    205             //Create an e-mail header annotation
    206             createAnnotation("Header",startHeader,endHeader,null);
    207           }//if
    208           // read the next line
    209           continue;
    210         }//if (line.equals(""))
    211         // if line begins with a field then prepare to create an
    212         // annotation with the name of the field
    213         if (lineBeginsWithField(line)){
    214           // if a field was read before, it means that the previous field ends
    215           // here
    216           if (true == fieldReadBefore){
    217             // the previous field end here
    218             endField = cursor - nlSize;
    219             //Create a field annotation (fieldName, startField, endField)
    220             createAnnotation(aFieldName, startField, endField, null);
    221           }//if
    222           fieldReadBefore = true;
    223           aFieldName = getFieldName();
    224           startField = cursor + aFieldName.length() + ":".length();
    225         }//if
    226         // in both cases the cursor is updated and read the next line
    227         // the cursor is update with the length of the line +
    228         // the new line char
    229         cursor += line.length() + nlSize;
    230         // read the next line
    231         continue;
    232       }//if (true == insideHeader)
    233       // here we are inside the E-mail body
    234       // the body will end when the e-mail will end.
    235       // here we just update the cursor
    236       cursor += line.length() + nlSize;
    237     }//while
    238     // it might be possible that the file to contain only one e-mail and
    239     // if the file contains only one e-mail message then the variable
    240     // emailReadBefore must be set on true value
    241     if (true == emailReadBefore){
    242       endBody  = cursor - nlSize;
    243       endEmail = cursor - nlSize;
    244       //Annotate an E-mail body (startBody, endEmail)
    245       createAnnotation("Body",startBody,endBody,null);
    246       //Annotate an E-mail message(startEmail, endEmail) Email starts
    247       createAnnotation("Message",startEmail,endEmail,null);
    248     }
    249     // if emailReadBefore is not set on true, that means that we didn't
    250     // encounter any line like this:
    251     // From P.Fairhurst Thu Apr 18 12:22:23 1996
    252   }//annotateMessages
    253
    254   /**
    255     * This method detects if the text file which contains e-mail messages
    256     * is under MSDOS or UNIX format.
    257     * Under MSDOS the size of NL is 2 (\n \r) and under UNIX (\n) the size is 1
    258     * @return the size of the NL (1,2 or 0 = if no \n is found)
    259     */
    260   private int detectNLSize() {
    261
    262     // get a char array
    263     char[] document = null;
    264
    265     // transform the gate Document into a char array
    266     document = gateDocument.getContent().toString().toCharArray();
    267
    268     // search for the \n char
    269     // when it is found test if is followed by the \r char
    270     for (int i=0; i<document.length; i++){
    271       if (document[i] == '\n'){
    272
    273         // we just found a \n char.
    274         // here we test if is followed by a \r char or preceded by a \r char
    275         if (
    276             (((i+1) < document.length) && (document[i+1] == '\r'))
    277             ||
    278             (((i-1) >= 0)              && (document[i-1] == '\r'))
    279            ) return 2;
    280         else return 1;
    281       }
    282     }
    283     //if no \n char is found then the document is contained into a single text
    284     // line.
    285     return 0;
    286
    287   } // detectNLSize
    288
    289   /**
    290     * This method creates a gate annotation given its name, start, end and
    291     * feature map.
    292     */
    293   private void createAnnotation(String anAnnotationName, long anAnnotationStart,
    294                                  long anAnnotationEnd, FeatureMap aFeatureMap)
    295                                        throws gate.util.InvalidOffsetException{
    296
    297 /*
    298     while (Character.isWhitespace(content.charAt((int) anAnnotationStart)))
    299       anAnnotationStart ++;
    300
    301 //    System.out.println(content.charAt((int) anAnnotationEnd));
    302     while (Character.isWhitespace(content.charAt((int) anAnnotationEnd)))
    303       anAnnotationEnd --;
    304
    305     anAnnotationEnd ++;
    306 */
    307    if (canCreateAnnotation(anAnnotationStart,anAnnotationEnd,documentSize)){
    308       if (aFeatureMap == null)
    309           aFeatureMap = Factory.newFeatureMap();
    310       basicAS.add( new Long(anAnnotationStart),
    311                    new Long(anAnnotationEnd),
    312                    anAnnotationName.toLowerCase(),
    313                    aFeatureMap);
    314    }// End if
    315   }//createAnnotation
    316   /**
    317     * This method verifies if an Annotation can be created.
    318     */
    319   private boolean canCreateAnnotation(long start,
    320                                       long end,
    321                                       long gateDocumentSize){
    322
    323     if (start < 0 || end < 0 ) return false;
    324     if (start > end ) return false;
    325     if ((start > gateDocumentSize) || (end > gateDocumentSize)) return false;
    326     return true;
    327   }// canCreateAnnotation
    328
    329   /**
    330     * Tests if the line begins an e-mail message
    331     * @param aTextLine a line from the file containing the e-mail messages
    332     * @return true if the line begins an e-mail message
    333     * @return false if is doesn't
    334     */
    335   private boolean lineBeginsMessage(String aTextLine){
    336     int score = 0;
    337
    338     // if first token is "From" and the rest contains Day, Zone, etc
    339     // then this line begins a message
    340     // create a new String Tokenizer with " " as separator
    341     StringTokenizer tokenizer = new StringTokenizer(aTextLine," ");
    342
    343     // get the first token
    344     String firstToken = null;
    345     if (tokenizer.hasMoreTokens())
    346         firstToken = tokenizer.nextToken();
    347     else return false;
    348
    349     // trim it
    350     firstToken.trim();
    351
    352     // check against "From" word
    353     // if the first token is not From then the entire line can not begin
    354     // a message.
    355     if (!firstToken.equals("From"))
    356         return false;
    357
    358     // else continue the analize
    359     while (tokenizer.hasMoreTokens()){
    360
    361       // get the next token
    362       String token = tokenizer.nextToken();
    363       token.trim();
    364
    365       // see if it has a meaning(analize if is a Day, Month,Zone, Time, Year )
    366       if (hasAMeaning(token))
    367           score += 1;
    368     }
    369
    370     // a score greather or equql with 5 means that this line begins a message
    371     if (score >= 5) return true;
    372     else return false;
    373
    374   } // lineBeginsMessage
    375
    376   /**
    377     * Tests if the line begins with a field from the e-mail header
    378     * If the answer is true then it also sets the member fieldName with the
    379     * value of this e-mail header field.
    380     * @param aTextLine a line from the file containing the e-mail text
    381     * @return true if the line begins with a field from the e-mail header
    382     * @return false if is doesn't
    383     */
    384   private boolean lineBeginsWithField(String aTextLine){
    385     if (containsSemicolon(aTextLine)){
    386       StringTokenizer tokenizer = new StringTokenizer(aTextLine,":");
    387
    388       // get the first token
    389       String firstToken = null;
    390
    391       if (tokenizer.hasMoreTokens())
    392         firstToken = tokenizer.nextToken();
    393       else return false;
    394
    395       if (firstToken != null){
    396         // trim it
    397         firstToken.trim();
    398         if (containsWhiteSpaces(firstToken)) return false;
    399
    400         // set the member field
    401         fieldName = firstToken;
    402       }
    403       return true;
    404     } else return false;
    405
    406   } // lineBeginsWithField
    407
    408   /**
    409     * This method checks if a String contains white spaces.
    410     */
    411   private boolean containsWhiteSpaces(String aString) {
    412     for (int i = 0; i<aString.length(); i++)
    413       if (Character.isWhitespace(aString.charAt(i))) return true;
    414     return false;
    415   } // containsWhiteSpaces
    416
    417   /**
    418     * This method checks if a String contains a semicolon char
    419     */
    420   private boolean containsSemicolon(String aString) {
    421     for (int i = 0; i<aString.length(); i++)
    422       if (aString.charAt(i) == ':') return true;
    423     return false;
    424   } // containsSemicolon
    425
    426   /**
    427     * This method tests a token if is Day, Month, Zone, Time, Year
    428     */
    429   private boolean hasAMeaning(String aToken) {
    430     // if token is a Day return true
    431     if (day.contains(aToken)) return true;
    432
    433     // if token is a Month return true
    434     if (month.contains(aToken)) return true;
    435
    436     // if token is a Zone then return true
    437     if (zone.contains(aToken)) return true;
    438
    439     // test if is a day number or a year
    440     Integer dayNumberOrYear = null;
    441     try{
    442       dayNumberOrYear = new Integer(aToken);
    443     } catch (NumberFormatException e){
    444       dayNumberOrYear = null;
    445     }
    446
    447     // if the creation succeded, then test if is day or year
    448     if (dayNumberOrYear != null) {
    449       int number = dayNumberOrYear.intValue();
    450
    451       // if is a number between 1 and 31 then is a day
    452       if ((number > 0) && (number < 32)) return true;
    453
    454       // if is a number between 1900 si 3000 then is a year ;))
    455       if ((number > 1900) && (number < 3000)) return true;
    456
    457       // it might be the last two digits of 19xx
    458       if ((number >= 0) && (number <= 99)) return true;
    459     }
    460     // test if is time: hh:mm:ss
    461     if (isTime(aToken)) return true;
    462
    463    return false;
    464   } // hasAMeaning
    465
    466   /**
    467     * Tests a token if is in time format HH:MM:SS
    468     */
    469   private boolean isTime(String aToken) {
    470     StringTokenizer st = new StringTokenizer(aToken,":");
    471
    472     // test each token if is hour, minute or second
    473     String hourString = null;
    474     if (st.hasMoreTokens())
    475         hourString = st.nextToken();
    476
    477     // if there are no more tokens, it means that is not a time
    478     if (hourString == null) return false;
    479
    480     // test if is a number between 0 and 23
    481     Integer hourInteger = null;
    482     try{
    483       hourInteger = new Integer(hourString);
    484     } catch (NumberFormatException e){
    485       hourInteger = null;
    486     }
    487     if (hourInteger == null) return false;
    488
    489     // if is not null then it means is a number
    490     // test if is in 0 - 23 range
    491     // if is not in this range then is not an hour
    492     int hour = hourInteger.intValue();
    493     if ( (hour < 0) || (hour > 23) ) return false;
    494
    495     // we have the hour
    496     // now repeat the test for minute and seconds
    497
    498     // minutes
    499     String minutesString = null;
    500     if (st.hasMoreTokens())
    501         minutesString = st.nextToken();
    502
    503     // if there are no more tokens (minutesString == null) then return false
    504     if (minutesString == null) return false;
    505
    506     // test if is a number between 0 and 59
    507     Integer minutesInteger = null;
    508     try {
    509       minutesInteger = new Integer (minutesString);
    510     } catch (NumberFormatException e){
    511       minutesInteger = null;
    512     }
    513
    514     if (minutesInteger == null) return false;
    515
    516     // if is not null then it means is a number
    517     // test if is in 0 - 59 range
    518     // if is not in this range then is not a minute
    519     int minutes = minutesInteger.intValue();
    520     if ( (minutes < 0) || (minutes > 59) ) return false;
    521
    522     // seconds
    523     String secondsString = null;
    524     if (st.hasMoreTokens())
    525         secondsString = st.nextToken();
    526
    527     // if there are no more tokens (secondsString == null) then return false
    528     if (secondsString == null) return false;
    529
    530     // test if is a number between 0 and 59
    531     Integer secondsInteger = null;
    532     try {
    533       secondsInteger = new Integer (secondsString);
    534     } catch (NumberFormatException e){
    535       secondsInteger = null;
    536     }
    537     if (secondsInteger == null) return false;
    538
    539     // if is not null then it means is a number
    540     // test if is in 0 - 59 range
    541     // if is not in this range then is not a minute
    542     int seconds = secondsInteger.intValue();
    543     if ( (seconds < 0) || (seconds > 59) ) return false;
    544
    545     // if there are more tokens in st it means that we don't have this format:
    546     // HH:MM:SS
    547     if (st.hasMoreTokens()) return false;
    548
    549     // if we are here it means we have a time
    550     return true;
    551   }// isTime
    552
    553   /**
    554     * Initialises the collections with data used by method lineBeginsMessage()
    555     */
    556   private void setUp(){
    557     day = new HashSet();
    558     day.add("Mon");
    559     day.add("Tue");
    560     day.add("Wed");
    561     day.add("Thu");
    562     day.add("Fri");
    563     day.add("Sat");
    564     day.add("Sun");
    565
    566     month = new HashSet();
    567     month.add("Jan");
    568     month.add("Feb");
    569     month.add("Mar");
    570     month.add("Apr");
    571     month.add("May");
    572     month.add("Jun");
    573     month.add("Jul");
    574     month.add("Aug");
    575     month.add("Sep");
    576     month.add("Oct");
    577     month.add("Nov");
    578     month.add("Dec");
    579
    580     zone = new HashSet();
    581     zone.add("UT");
    582     zone.add("GMT");
    583     zone.add("EST");
    584     zone.add("EDT");
    585     zone.add("CST");
    586     zone.add("CDT");
    587     zone.add("MST");
    588     zone.add("MDT");
    589     zone.add("PST");
    590     zone.add("PDT");
    591   }//setUp
    592
    593   /**
    594     * This method returns the value of the member fieldName.
    595     * fieldName is set by the method lineBeginsWithField(String line).
    596     * Each time the the line begins with a field name, that fiels will be stored
    597     * in this member.
    598     */
    599   private String getFieldName() {
    600     if (fieldName == null) return new String("");
    601     else return fieldName;
    602   } // getFieldName
    603
    604   // StatusReporter Implementation
    605
    606   /**
    607     * This methos is called when a listener is registered with this class
    608     */
    609   public void addStatusListener(StatusListener listener){
    610     myStatusListeners.add(listener);
    611   }
    612   /**
    613     * This methos is called when a listener is removed
    614     */
    615   public void removeStatusListener(StatusListener listener){
    616     myStatusListeners.remove(listener);
    617   }
    618
    619   /**
    620     * This methos is called whenever we need to inform the listener
    621     * about an event.
    622     */
    623   protected void fireStatusChangedEvent(String text){
    624     Iterator listenersIter = myStatusListeners.iterator();
    625     while(listenersIter.hasNext())
    626       ((StatusListener)listenersIter.next()).statusChanged(text);
    627   }
    628
    629   private static final int EMAILS_RATE = 16;
    630
    631   // the content of the e-mail document, without any tag
    632   // for internal use
    633   private String tmpDocContent = null;
    634
    635   // a gate document
    636   private gate.Document gateDocument = null;
    637
    638   // an annotation set used for creating annotation reffering the doc
    639   private gate.AnnotationSet basicAS = null;
    640
    641   // this map marks the elements that we don't want to create annotations
    642   private Map  markupElementsMap = null;
    643
    644   // this map marks the elements after we want to insert some strings
    645   private Map element2StringMap = null;
    646
    647   // listeners for status report
    648   protected List myStatusListeners = new LinkedList();
    649
    650   // this reports the the number of emails that have beed processed so far
    651   private int emails = 0;
    652
    653   // this is set by the method lineBeginsWithField(String line)
    654   // each time the the line begins with a field name, that fiels will be stored
    655   // in this member.
    656   private String fieldName = null;
    657
    658   private Collection day = null;
    659   private Collection month = null;
    660   private Collection zone = null;
    661
    662
    663  // TEST SECTION
    664
    665   /**
    666     * Test containsSemicolon
    667     */
    668   private void testContainsSemicolon() {
    669     String str1 = "X-Sender: oana@derwent";
    670     String str2 = "X-Sender oana@derwent";
    671     String str3 = ":X-Sender oana@derwent";
    672     String str4 = "X-Sender oana@derwent:";
    673
    674     Assert.assertTrue((containsSemicolon(str1) == true));
    675     Assert.assertTrue((containsSemicolon(str2)== false));
    676     Assert.assertTrue((containsSemicolon(str3) == true));
    677     Assert.assertTrue((containsSemicolon(str4) == true));
    678   }// testContainsSemicolon
    679
    680   /**
    681     * Test containsWhiteSpaces
    682     */
    683   private void testContainsWhiteSpaces(){
    684     String str1 = "Content-Type: TEXT/PLAIN; charset=US-ASCII";
    685     String str2 = "Content-Type:TEXT/PLAIN;charset=US-ASCII";
    686     String str3 = " Content-Type:TEXT/PLAIN;charset=US-ASCII";
    687     String str4 = "Content-Type:TEXT/PLAIN;charset=US-ASCII ";
    688
    689     Assert.assertTrue((containsWhiteSpaces(str1) == true));
    690     Assert.assertTrue((containsWhiteSpaces(str2) == false));
    691     Assert.assertTrue((containsWhiteSpaces(str3) == true));
    692     Assert.assertTrue((containsWhiteSpaces(str4) == true));
    693   }// testContainsWhiteSpaces
    694
    695   /**
    696     * Test hasAMeaning
    697     */
    698   private void testHasAMeaning() {
    699     String str1 = "12:05:22";
    700     String str2 = "Sep";
    701     String str3 = "Fri";
    702     String str4 = "2000";
    703     String str5 = "GMT";
    704     String str6 = "Date: Wed, 13 Sep 2000 13:05:22 +0100 (BST)";
    705     String str7 = "12:75:22";
    706     String str8 = "September";
    707     String str9 = "Friday";
    708
    709     Assert.assertTrue((hasAMeaning(str1) == true));
    710     Assert.assertTrue((hasAMeaning(str2) == true));
    711     Assert.assertTrue((hasAMeaning(str3) == true));
    712     Assert.assertTrue((hasAMeaning(str4) == true));
    713     Assert.assertTrue((hasAMeaning(str5) == true));
    714     Assert.assertTrue((hasAMeaning(str6) == false));
    715     Assert.assertTrue((hasAMeaning(str7) == false));
    716     Assert.assertTrue((hasAMeaning(str8) == false));
    717     Assert.assertTrue((hasAMeaning(str9) == false));
    718   } // testHasAMeaning
    719
    720   /**
    721     * Test isTime
    722     */
    723   private void testIsTime() {
    724     String str1 = "13:05:22";
    725     String str2 = "13/05/22";
    726     String str3 = "24:05:22";
    727
    728     Assert.assertTrue((isTime(str1) == true));
    729     Assert.assertTrue((isTime(str2) == false));
    730     Assert.assertTrue((isTime(str3) == false));
    731   }// testIsTime
    732
    733   /**
    734     * Test lineBeginsMessage
    735     */
    736   private void testLineBeginsMessage(){
    737     String str1 = "From oana@dcs.shef.ac.uk Wed Sep 13 13:05:23 2000";
    738     String str2 = "Date: Wed, 13 Sep 2000 13:05:22 +0100 (BST)";
    739     String str3 = "From oana@dcs.shef.ac.uk Sep 13 13:05:23 2000";
    740
    741     Assert.assertTrue((lineBeginsMessage(str1) == true));
    742     Assert.assertTrue((lineBeginsMessage(str2) == false));
    743     Assert.assertTrue((lineBeginsMessage(str3) == false));
    744
    745   }// testLineBeginsMessage
    746
    747   /**
    748     * Test lineBeginsWithField
    749     */
    750   private void testLineBeginsWithField() {
    751     String str1 = "Message-ID: <Pine.SOL.3.91.1000913130311.19537A-10@derwent>";
    752     String str2 = "%:ContentType TEXT/PLAIN; charset=US-ASCII";
    753
    754     Assert.assertTrue((lineBeginsWithField(str1) == true));
    755     Assert.assertTrue((lineBeginsWithField(str2) == true));
    756   }// testLineBeginsWithField
    757
    758    /**
    759      * Test final
    760      */
    761    public void testSelf(){
    762      testContainsSemicolon();
    763      testContainsWhiteSpaces();
    764      testHasAMeaning();
    765      testIsTime();
    766      testLineBeginsMessage();
    767      testLineBeginsWithField();
    768    } // testSelf
    769
    770 } //EmailDocumentHandler
    771

| EmailDocumentHandler |