home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1995 November / PCWK1195.iso / inne / win95 / sieciowe / hotja32.lzh / hotjava / classsrc / browser / tools / javasearch / doc.java < prev    next >
Text File  |  1995-08-11  |  13KB  |  419 lines

  1. /*
  2.  * @(#)Doc.java    1.11 95/03/14 David A. Brown
  3.  *
  4.  * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * Permission to use, copy, modify, and distribute this software
  7.  * and its documentation for NON-COMMERCIAL purposes and without
  8.  * fee is hereby granted provided that this copyright notice
  9.  * appears in all copies. Please refer to the file "copyright.html"
  10.  * for further important copyright and licensing information.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  13.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  14.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  15.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  16.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  17.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  18.  */
  19.  
  20. package browser.tools.JavaSearch;
  21. import java.io.*;
  22.  
  23. /** One JavaSearch Document */
  24. public class Doc {
  25.  
  26.     //
  27.     // Persistent info about this Document (this is all
  28.     //   saved in the docs file)
  29.     //
  30.  
  31.     /** Document ID:  NOTE maximum of 65535 docs in one index!! */
  32.     public char docID;
  33.  
  34.     /**
  35.      *  "Filename" of this document.  This is NOT a full
  36.      *  pathname -- it should be combined with either our Database's
  37.      *  docPathPrefix or docURLPrefix, in order to get a
  38.      *  fully-qualified filename or URL respectively.
  39.      */
  40.     public String filename;
  41.  
  42.     /**
  43.      *  Document's "headline" or "title".  Should be human-readable
  44.      *  and useful in the context of a list of search hits.
  45.      */
  46.     public String headline;
  47.  
  48.     // FUTURE stuff:
  49.     //int startChar, endChar; // Character range of this doc in 'filename'
  50.     //int numLines; // # lines in the doc
  51.     //int length;      // # bytes in the doc
  52.     //String date;  // Human-readable date string, based on last-mod-date
  53.     
  54.     //
  55.     // Other (transient) info about this Document
  56.     //
  57.  
  58.     /** "Document type":  slightly change the behavior of
  59.      *  getWord() for different input file types.
  60.      */
  61.     public static final int INVALID_TYPE = 0;
  62.     public static final int TEXT_TYPE = 1;
  63.     public static final int HTML_TYPE = 2;
  64.     //public static final int NEWS_TYPE = 3;
  65.     //
  66.     public int type = INVALID_TYPE;
  67.  
  68.  
  69.     /** Counter used to generate consecutive doc IDs.  Starts at 1
  70.      *  since doc ID 0 is never used!  (it means the end of an
  71.      *  index entry).
  72.      *    Be sure to call resetDocIDCounter() if a single Java program
  73.      *  is ever used to create more than one JavaSearch index.
  74.      */
  75.     private static int doc_ID_counter = 1;
  76.  
  77.     /** Maximum length (in chars) of a Doc headline */
  78.     private static final int HEADLINE_MAX_LENGTH = 80;
  79.  
  80.  
  81.     /** 
  82.      *  Doc constructor to use when we already know all the Doc's info,
  83.      *  eg. when we're reading it from the doc file.
  84.      *  Used by the Doc.readFromStream() method. 
  85.      */
  86.     private Doc(char id, String fn, String hl) {
  87.     docID = id;
  88.     filename = fn;
  89.     headline = hl;
  90.     }
  91.  
  92.     /**
  93.      * Generate a new Doc object based on a filename.
  94.      * Doc type is derived from the filename.
  95.      * Headline is computed based on the doc type, and may involve
  96.      *   opening and reading the file.
  97.      *
  98.      * Automatically generates a new sequential docID.
  99.      *
  100.      * This Doc constructor is used when CREATING an index.
  101.      */
  102.     public Doc(String aFilename) {
  103.     filename = aFilename;
  104.     type = docTypeForFilename(filename);
  105.     headline = generateHeadline(filename, type);
  106.  
  107.     // Generate a unique doc ID.
  108.     docID = (char)(doc_ID_counter++);
  109.     if (docID > 65535 ) {    // REMIND: Is there a more correct way
  110.                    // to express the biggest number you can fit in a char?
  111.         throw new Exception("Doc ID overflow:  too many documents for one index!");
  112.     }
  113.  
  114.     }
  115.  
  116.     /**
  117.      * Create a new Doc object by looking it up, using the
  118.      * specified Doc ID, in the specified .docs and .docindex files.
  119.      *
  120.      * The .docindex files gives us a pointer into the .docs
  121.      * file, from where we read the actual document info.
  122.      *
  123.      * This Doc constructor is used when SEARCHING.
  124.      */
  125.     public Doc(char id, RandomAccessFile docsFile,
  126.            RandomAccessFile docindexFile) {
  127.     
  128.     // Read an docs file position from the docindex file.
  129.     // docindex entries are ints, which are 4 bytes long.
  130.     docindexFile.seek(4*(int)id);
  131.     int docsPos = docindexFile.readInt();
  132.  
  133.     // Seek the docsfile to the right place, and read the
  134.     //   Doc info.
  135.     docsFile.seek(docsPos);
  136.     readFromFile(docsFile);
  137.  
  138.     // Now sanity-check the Doc we read from docsFile:
  139.     //   make sure its ID is what we started off looking for!
  140.  
  141.     if (id != docID) {
  142.         System.out.println("  Doc constructor:  tried for ID "+
  143.                    id+", but readFromFile() found "+docID+"!!!");
  144.         throw new Exception("Doc ID mismatch in .docs/.docindex files!");
  145.     }
  146.     }
  147.  
  148.     /** Reset the counter used to generate consecutive doc IDs.
  149.      *  This must be called if a single Java program is ever
  150.      *  used to create more than one JavaSearch index.
  151.      */
  152.     public static void resetDocIDCounter() {
  153.     doc_ID_counter = 1;
  154.     }
  155.  
  156.     /** Generate a simple printed representation of this Doc */
  157.     public String toString() {
  158.         return "[DocID " + docID + "]\t" + filename + 
  159.         "\t'" + headline + "'";
  160.     }
  161.  
  162.     /** Trim a prefix off this Doc's filename.
  163.      *  Used by the indexer.  If our filename begins with
  164.      *  "prefix", then we replace our filename with everything
  165.      *  that *follows* prefix.
  166.      */
  167.     public void trimFilename(String prefix) {
  168.     //System.out.println("\ntrimFilename:  prefix '"+prefix+
  169.     //     "', filename '"+filename+"'.");
  170.     if (prefix == null) return;
  171.     if (filename.startsWith(prefix)) {
  172.         filename = filename.substring(prefix.length());
  173.     }
  174.     //System.out.println("  trimmed!  filename is now '"+filename+"'.");
  175.     }
  176.         
  177.     /** Write this Doc to the specified Output stream */
  178.     void writeToStream(DataOutputStream out) {
  179.     // Our doc ID:  one char
  180.     // Note the ID is redundant when writing the docs file
  181.     //    (since Docs are in sequential ID order!) but we still
  182.     //    save it and do a sanity check when retrieving.
  183.     out.writeChar(docID);
  184.  
  185.     // Filename and headline, terminated by '\n's:
  186.     out.writeBytes(filename);
  187.         out.writeByte('\n');
  188.     out.writeBytes(headline);
  189.         out.writeByte('\n');
  190.     }
  191.  
  192.     /**
  193.      *  Read a single Document entry from the specified stream.
  194.      *  Returns a Doc object, or null if stream hits EOF.
  195.      *  The stream must be pointing at the start of a
  196.      *  valid Doc entry!!
  197.      */
  198.     static Doc readFromStream(DataInputStream in) {
  199.     char id = in.readChar();
  200.     System.out.println("  Doc.readFromStream:  got a char: "+id);
  201.     // REMIND:  what does readChar() return if we're at EOF??
  202.     //  Is it OK to just use the readLine() calls (below) to detect EOF?
  203.  
  204.     String filename = in.readLine();
  205.     String headline = in.readLine();
  206.     if (filename==null || headline==null) {
  207.         return null;
  208.     }
  209.  
  210.     Doc doc = new Doc(id,filename,headline);
  211.     return doc;
  212.     }
  213.  
  214.     /**
  215.      * Read a single Document entry from the specified file.  NOTE that
  216.      * indexFile must be pointing at the start of a valid Doc entry!!
  217.      *
  218.      * This duplicates the functionality of
  219.      * readFromStream(), but unfortunately
  220.      * RandomAccessFiles are not InputStreams...
  221.      */
  222.     public void readFromFile(RandomAccessFile docsFile) {
  223.  
  224.     docID = docsFile.readChar();
  225.     filename = docsFile.readLine();
  226.     headline = docsFile.readLine();
  227.     if (filename==null || headline==null) {
  228.         throw new Exception("Doc.readFromFile hit EOF: must have been a bad pointer into docsFile!");
  229.     }
  230.     //System.out.println("  Read Doc from docsFile: "+this);
  231.     }
  232.  
  233.  
  234.     /**
  235.      *  Return a Document Type given the specified filename.
  236.      *  Specifically, look for any special file extensions we recognize.
  237.      */
  238.     private static int docTypeForFilename(String filename) {
  239.     // Currently the only special filename we recognize
  240.     //   is "*.html":
  241.     // REMIND: the 'endsWith(".html")' part might be Unix-specific!
  242.     if (filename.endsWith(".html")) {
  243.         //System.out.println("Got an HTML file: "+filename);
  244.         return HTML_TYPE;
  245.     }
  246.  
  247.     // REMIND:  Here's where we would detect a NEWS type file.
  248.     //    But there's no way to do that from a filename, unless
  249.     //    we assume a "numeric" filename (like '469') is a news
  250.     //    article!
  251.     // Do we have to also open the file here to figure out what
  252.     //    it is?  Maybe the functionality of docTypeForFilename()
  253.     //    should be merged with generateHeadline(), which already
  254.     //    has to read the file anyway?
  255.  
  256.     else {
  257.         // default to TEXT...
  258.         return TEXT_TYPE;
  259.     }
  260.     }
  261.     
  262.     /**
  263.      *  Compute a headline for this Doc, given the specified "filename" and
  264.      *  Doc type.  This may involve opening and reading some of the file
  265.      *  for certain doc types!
  266.      */
  267.     private static String generateHeadline(String filename, int docType) {
  268.  
  269.     String headline = null;
  270.  
  271.     // Handle all known types
  272.     switch (docType) {
  273.  
  274.     case TEXT_TYPE:
  275.         // Headline is simply the filename
  276.         headline = filename;
  277.         break;
  278.  
  279.     case HTML_TYPE:
  280.         headline = getHTMLHeadline(filename);
  281.         break;
  282.  
  283.     //case NEWS_TYPE:
  284.     //    headline = getNewsHeadline(filename);
  285.     //    break;
  286.  
  287.     default:
  288.         throw new Exception("Doc.generateHeadline:  invalid docType ("+
  289.                 docType+")!");
  290.     }
  291.  
  292.     // Some final processing:
  293.  
  294.     if ((headline == null) || (headline.length() == 0)) {
  295.         headline = "[No Headline]";
  296.     }
  297.  
  298.     // REMIND:  We should strip out any newlines,
  299.     //   and maybe any other nasty characters here
  300.  
  301.     // Enforce HEADLINE_MAX_LENGTH:
  302.     if (headline.length() > HEADLINE_MAX_LENGTH) {
  303.         headline = headline.substring(0,HEADLINE_MAX_LENGTH);
  304.     }
  305.  
  306.     //System.out.println("generateHeadline("+filename+"): returning '"+
  307.     //           headline+"'.");
  308.     return headline;
  309.     }
  310.  
  311.     /**
  312.      * Compute the headline of an HTML file, by reading the file
  313.      * and finding everything between the <title> and </title>.
  314.      * Returns a String, or null if no HTML title was found.
  315.      */
  316.     private static String getHTMLHeadline(String filename) {
  317.     String line,tmpline;
  318.     String result= null;
  319.     int pos;
  320.  
  321.     // Delimiters of an HTML title (lowercase versions)
  322.     String titleStart = "<title>";
  323.     String titleEnd = "</title>";
  324.  
  325.     //System.out.println("getHTMLHeadline: opening '" + filename + "'...");
  326.     FileInputStream filein = new FileInputStream(filename);
  327.     DataInputStream in = new DataInputStream(filein);
  328.  
  329.     while (true) {
  330.         line = in.readLine();
  331.         if (line == null) {
  332.         filein.close();
  333.         return null;
  334.         }
  335.         tmpline = IndexingInputStream.downcase(line);
  336.         if ((pos = tmpline.indexOf(titleStart)) != -1) {
  337.         // Got the title!  Save the rest of this line
  338.         result = line.substring(pos+titleStart.length());
  339.         break;
  340.         }
  341.     }
  342.         
  343.     // If we already have titleEnd, we're done.
  344.     tmpline = IndexingInputStream.downcase(result);
  345.     if ((pos = tmpline.indexOf(titleEnd)) != -1) {
  346.         filein.close();
  347.         return result.substring(0,pos);
  348.     }
  349.  
  350.     // Ok, keep reading more lines, looking for titleEnd
  351.     while ((line = in.readLine()) != null) {
  352.             tmpline = IndexingInputStream.downcase(line);
  353.         if ((pos = tmpline.indexOf(titleEnd)) != -1) {
  354.         // This line contained titleEnd.
  355.  
  356.         // Add a space to result unless we're at
  357.         //   the very beginning or end of the title
  358.         if ((result.length() != 0) && (pos != 0)) {
  359.             result += " ";
  360.         }
  361.  
  362.         // And append anything *before* titleEnd
  363.         result += line.substring(0,pos);
  364.         break;
  365.         }
  366.         else {
  367.         // Add a space to result unless we're at
  368.                 //   the very beginning of the title,
  369.         //   or if this line is empty
  370.                 if ((result.length() != 0) && (line.length() != 0)) {
  371.                     result += " ";
  372.                 }
  373.         
  374.         // Append this whole line to result
  375.         result += line;
  376.         }
  377.     }
  378.  
  379.     filein.close();
  380.     return result;
  381.     }
  382.  
  383.  
  384. //    /**
  385. //     * Compute the headline of a News article, by reading the file
  386. //     * and looking for the From: and Subject: lines.
  387. //     * Returns a String, or null if no title could be constructed.
  388. //     */
  389. //    private static String getNewsHeadline(String filename) {
  390. //    String line,tmpline;
  391. //    String result = null;;
  392. //    int pos;
  393. //
  394. //    // Header fields we care about
  395. //    String fromHeader = "from:";
  396. //    String subjectHeader = "subject:";
  397. //
  398. //    //System.out.println("getNewsHeadline: opening '" + filename + "'...");
  399. //    FileInputStream filein = new FileInputStream(filename);
  400. //    DataInputStream in = new DataInputStream(filein);
  401. //
  402. //    // Read through the lines of the file
  403. //    while ((line = in.readLine()) != null) {
  404. //
  405. //        // Search for fromHeader or subjectHeader here
  406. //
  407. //    }
  408. //
  409. //    // Construct a nice-looking headline based on the
  410. //    //   From and Subject fields.  Maybe something like
  411. //    //   "Subject of this Article [fromaddress@machine.sun.com]"
  412. //
  413. //    filein.close();
  414. //    return result;
  415. //    }
  416.  
  417.  
  418. }
  419.