add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / contrib / benchmark / src / java / org / apache / lucene / benchmark / byTask / feeds / TrecContentSource.java
1 package org.apache.lucene.benchmark.byTask.feeds;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.io.BufferedReader;
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InputStreamReader;
25 import java.io.Reader;
26 import java.text.DateFormat;
27 import java.text.ParsePosition;
28 import java.text.SimpleDateFormat;
29 import java.util.ArrayList;
30 import java.util.Date;
31 import java.util.Locale;
32
33 import org.apache.lucene.benchmark.byTask.feeds.TrecDocParser.ParsePathType;
34 import org.apache.lucene.benchmark.byTask.utils.Config;
35 import org.apache.lucene.benchmark.byTask.utils.StreamUtils;
36 import org.apache.lucene.benchmark.byTask.utils.StringBuilderReader;
37 import org.apache.lucene.util.ThreadInterruptedException;
38
39 /**
40  * Implements a {@link ContentSource} over the TREC collection.
41  * <p>
42  * Supports the following configuration parameters (on top of
43  * {@link ContentSource}):
44  * <ul>
45  * <li><b>work.dir</b> - specifies the working directory. Required if "docs.dir"
46  * denotes a relative path (<b>default=work</b>).
47  * <li><b>docs.dir</b> - specifies the directory where the TREC files reside.
48  * Can be set to a relative path if "work.dir" is also specified
49  * (<b>default=trec</b>).
50  * <li><b>trec.doc.parser</b> - specifies the {@link TrecDocParser} class to use for
51  * parsing the TREC documents content (<b>default=TrecGov2Parser</b>).
52  * <li><b>html.parser</b> - specifies the {@link HTMLParser} class to use for
53  * parsing the HTML parts of the TREC documents content (<b>default=DemoHTMLParser</b>).
54  * <li><b>content.source.encoding</b> - if not specified, ISO-8859-1 is used.
55  * <li><b>content.source.excludeIteration</b> - if true, do not append iteration number to docname
56  * </ul>
57  */
58 public class TrecContentSource extends ContentSource {
59
60   private static final class DateFormatInfo {
61     DateFormat[] dfs;
62     ParsePosition pos;
63   }
64
65   public static final String DOCNO = "<DOCNO>";
66   public static final String TERMINATING_DOCNO = "</DOCNO>";
67   public static final String DOC = "<DOC>";
68   public static final String TERMINATING_DOC = "</DOC>";
69
70   /** separator between lines in the byffer */ 
71   public static final String NEW_LINE = System.getProperty("line.separator");
72
73   private static final String DATE_FORMATS [] = {
74        "EEE, dd MMM yyyy kk:mm:ss z",   // Tue, 09 Dec 2003 22:39:08 GMT
75        "EEE MMM dd kk:mm:ss yyyy z",    // Tue Dec 09 16:45:08 2003 EST
76        "EEE, dd-MMM-':'y kk:mm:ss z",   // Tue, 09 Dec 2003 22:39:08 GMT
77        "EEE, dd-MMM-yyy kk:mm:ss z",    // Tue, 09 Dec 2003 22:39:08 GMT
78        "EEE MMM dd kk:mm:ss yyyy",      // Tue Dec 09 16:45:08 2003
79        "dd MMM yyyy",                   // 1 March 1994
80        "MMM dd, yyyy",                  // February 3, 1994
81        "yyMMdd",                        // 910513
82        "hhmm z.z.z. MMM dd, yyyy",       // 0901 u.t.c. April 28, 1994
83   };
84
85   private ThreadLocal<DateFormatInfo> dateFormats = new ThreadLocal<DateFormatInfo>();
86   private ThreadLocal<StringBuilderReader> trecDocReader = new ThreadLocal<StringBuilderReader>();
87   private ThreadLocal<StringBuilder> trecDocBuffer = new ThreadLocal<StringBuilder>();
88   private File dataDir = null;
89   private ArrayList<File> inputFiles = new ArrayList<File>();
90   private int nextFile = 0;
91   private int rawDocSize = 0;
92
93   // Use to synchronize threads on reading from the TREC documents.
94   private Object lock = new Object();
95
96   // Required for test
97   BufferedReader reader;
98   int iteration = 0;
99   HTMLParser htmlParser;
100   
101   private boolean excludeDocnameIteration;
102   private TrecDocParser trecDocParser = new TrecGov2Parser(); // default
103   ParsePathType currPathType; // not private for tests
104   
105   private DateFormatInfo getDateFormatInfo() {
106     DateFormatInfo dfi = dateFormats.get();
107     if (dfi == null) {
108       dfi = new DateFormatInfo();
109       dfi.dfs = new SimpleDateFormat[DATE_FORMATS.length];
110       for (int i = 0; i < dfi.dfs.length; i++) {
111         dfi.dfs[i] = new SimpleDateFormat(DATE_FORMATS[i], Locale.US);
112         dfi.dfs[i].setLenient(true);
113       }
114       dfi.pos = new ParsePosition(0);
115       dateFormats.set(dfi);
116     }
117     return dfi;
118   }
119
120   private StringBuilder getDocBuffer() {
121     StringBuilder sb = trecDocBuffer.get();
122     if (sb == null) {
123       sb = new StringBuilder();
124       trecDocBuffer.set(sb);
125     }
126     return sb;
127   }
128   
129   Reader getTrecDocReader(StringBuilder docBuffer) {
130     StringBuilderReader r = trecDocReader.get();
131     if (r == null) {
132       r = new StringBuilderReader(docBuffer);
133       trecDocReader.set(r);
134     } else {
135       r.set(docBuffer);
136     }
137     return r;
138   }
139
140   HTMLParser getHtmlParser() {
141     return htmlParser;
142   }
143   
144   /**
145    * Read until a line starting with the specified <code>lineStart</code>.
146    * @param buf buffer for collecting the data if so specified/ 
147    * @param lineStart line start to look for, must not be null.
148    * @param collectMatchLine whether to collect the matching line into <code>buffer</code>.
149    * @param collectAll whether to collect all lines into <code>buffer</code>.
150    * @throws IOException
151    * @throws NoMoreDataException
152    */
153    private void read(StringBuilder buf, String lineStart, 
154        boolean collectMatchLine, boolean collectAll) throws IOException, NoMoreDataException {
155     String sep = "";
156     while (true) {
157       String line = reader.readLine();
158
159       if (line == null) {
160         openNextFile();
161         continue;
162       }
163
164       rawDocSize += line.length();
165
166       if (lineStart!=null && line.startsWith(lineStart)) {
167         if (collectMatchLine) {
168           buf.append(sep).append(line);
169           sep = NEW_LINE;
170         }
171         return;
172       }
173
174       if (collectAll) {
175         buf.append(sep).append(line);
176         sep = NEW_LINE;
177       }
178     }
179   }
180   
181   void openNextFile() throws NoMoreDataException, IOException {
182     close();
183     currPathType = null;
184     while (true) {
185       if (nextFile >= inputFiles.size()) { 
186         // exhausted files, start a new round, unless forever set to false.
187         if (!forever) {
188           throw new NoMoreDataException();
189         }
190         nextFile = 0;
191         iteration++;
192       }
193       File f = inputFiles.get(nextFile++);
194       if (verbose) {
195         System.out.println("opening: " + f + " length: " + f.length());
196       }
197       try {
198         InputStream inputStream = StreamUtils.inputStream(f); // support either gzip, bzip2, or regular text file, by extension  
199         reader = new BufferedReader(new InputStreamReader(inputStream, encoding), StreamUtils.BUFFER_SIZE);
200         currPathType = TrecDocParser.pathType(f);
201         return;
202       } catch (Exception e) {
203         if (verbose) {
204           System.out.println("Skipping 'bad' file " + f.getAbsolutePath()+" due to "+e.getMessage());
205           continue;
206         }
207         throw new NoMoreDataException();
208       }
209     }
210   }
211
212   public Date parseDate(String dateStr) {
213     dateStr = dateStr.trim();
214     DateFormatInfo dfi = getDateFormatInfo();
215     for (int i = 0; i < dfi.dfs.length; i++) {
216       DateFormat df = dfi.dfs[i];
217       dfi.pos.setIndex(0);
218       dfi.pos.setErrorIndex(-1);
219       Date d = df.parse(dateStr, dfi.pos);
220       if (d != null) {
221         // Parse succeeded.
222         return d;
223       }
224     }
225     // do not fail test just because a date could not be parsed
226     if (verbose) {
227       System.out.println("failed to parse date (assigning 'now') for: " + dateStr);
228     }
229     return null; 
230   }
231   
232   @Override
233   public void close() throws IOException {
234     if (reader == null) {
235       return;
236     }
237
238     try {
239       reader.close();
240     } catch (IOException e) {
241       if (verbose) {
242         System.out.println("failed to close reader !");
243         e.printStackTrace(System.out);
244       }
245     }
246     reader = null;
247   }
248
249   @Override
250   public DocData getNextDocData(DocData docData) throws NoMoreDataException, IOException {
251     String name = null;
252     StringBuilder docBuf = getDocBuffer();
253     ParsePathType parsedPathType;
254     
255     // protect reading from the TREC files by multiple threads. The rest of the
256     // method, i.e., parsing the content and returning the DocData can run unprotected.
257     synchronized (lock) {
258       if (reader == null) {
259         openNextFile();
260       }
261       
262       // 1. skip until doc start - required for all TREC formats
263       docBuf.setLength(0);
264       read(docBuf, DOC, false, false);
265       
266       // save parsedFile for passing trecDataParser after the sync block, in 
267       // case another thread will open another file in between.
268       parsedPathType = currPathType;
269       
270       // 2. name - required for all TREC formats
271       docBuf.setLength(0);
272       read(docBuf, DOCNO, true, false);
273       name = docBuf.substring(DOCNO.length(), docBuf.indexOf(TERMINATING_DOCNO,
274           DOCNO.length())).trim();
275       
276       if (!excludeDocnameIteration) {
277         name = name + "_" + iteration;
278       }
279
280       // 3. read all until end of doc
281       docBuf.setLength(0);
282       read(docBuf, TERMINATING_DOC, false, true);
283     }
284       
285     // count char length of text to be parsed (may be larger than the resulted plain doc body text).
286     addBytes(docBuf.length()); 
287
288     // This code segment relies on HtmlParser being thread safe. When we get 
289     // here, everything else is already private to that thread, so we're safe.
290     try {
291       docData = trecDocParser.parse(docData, name, this, docBuf, parsedPathType);
292       addDoc();
293     } catch (InterruptedException ie) {
294       throw new ThreadInterruptedException(ie);
295     }
296
297     return docData;
298   }
299
300   @Override
301   public void resetInputs() throws IOException {
302     synchronized (lock) {
303       super.resetInputs();
304       close();
305       nextFile = 0;
306       iteration = 0;
307     }
308   }
309
310   @Override
311   public void setConfig(Config config) {
312     super.setConfig(config);
313     // dirs
314     File workDir = new File(config.get("work.dir", "work"));
315     String d = config.get("docs.dir", "trec");
316     dataDir = new File(d);
317     if (!dataDir.isAbsolute()) {
318       dataDir = new File(workDir, d);
319     }
320     // files
321     collectFiles(dataDir, inputFiles);
322     if (inputFiles.size() == 0) {
323       throw new IllegalArgumentException("No files in dataDir: " + dataDir);
324     }
325     // trec doc parser
326     try {
327       String trecDocParserClassName = config.get("trec.doc.parser", "org.apache.lucene.benchmark.byTask.feeds.TrecGov2Parser");
328       trecDocParser = Class.forName(trecDocParserClassName).asSubclass(TrecDocParser.class).newInstance();
329     } catch (Exception e) {
330       // Should not get here. Throw runtime exception.
331       throw new RuntimeException(e);
332     }
333     // html parser
334     try {
335       String htmlParserClassName = config.get("html.parser",
336           "org.apache.lucene.benchmark.byTask.feeds.DemoHTMLParser");
337       htmlParser = Class.forName(htmlParserClassName).asSubclass(HTMLParser.class).newInstance();
338     } catch (Exception e) {
339       // Should not get here. Throw runtime exception.
340       throw new RuntimeException(e);
341     }
342     // encoding
343     if (encoding == null) {
344       encoding = "ISO-8859-1";
345     }
346     // iteration exclusion in doc name 
347     excludeDocnameIteration = config.get("content.source.excludeIteration", false);
348   }
349
350 }