1 /*
   2  * Copyright (c) 2008, 2014, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package ensemble.search;
  33 
  34 import java.io.BufferedReader;
  35 import java.io.IOException;
  36 import java.io.InputStream;
  37 import java.io.InputStreamReader;
  38 import java.util.ArrayList;
  39 import java.util.HashMap;
  40 import java.util.List;
  41 import java.util.Map;
  42 import org.apache.lucene.store.Directory;
  43 import org.apache.lucene.store.IndexInput;
  44 import org.apache.lucene.store.IndexOutput;
  45 
  46 /**
  47  * A very simple implementation of lucene Directory, it reads a index from the classpath in a directory called index
  48  * under the package that contains this file. It depends on a "listAll.txt" file written into that directory containing
  49  * the names of all the other files and their sizes. In the format "name:length" one file per line. When a file needs
  50  * to be read the whole file is loaded into memory.
  51  */
  52 public class ClasspathDirectory extends Directory {
  53     private String[] allFiles;
  54     private final Map<String,Long> fileLengthMap = new HashMap<>();
  55 
  56     public ClasspathDirectory() {
  57         // load list of all files
  58         try {
  59             BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("index/listAll.txt")));
  60             String line;
  61             List<String> fileNames = new ArrayList<>();
  62             while ((line = reader.readLine()) != null) {
  63                 String[] parts = line.split(":");
  64                 fileNames.add(parts[0]);
  65                 fileLengthMap.put(parts[0], Long.parseLong(parts[1]));
  66             }
  67             reader.close();
  68             allFiles = fileNames.toArray(new String[fileNames.size()]);
  69         } catch (IOException e) {
  70             e.printStackTrace();
  71         }
  72     }
  73 
  74     @Override public String[] listAll() throws IOException {
  75         return allFiles;
  76     }
  77 
  78     @Override public IndexInput openInput(String s) throws IOException {
  79         return new ClassPathIndexInput(
  80             getClass().getResourceAsStream("index/"+s),
  81             fileLengthMap.get(s).intValue()
  82         );
  83     }
  84 
  85     private static class ClassPathIndexInput extends IndexInput {
  86         private byte[] data;
  87         private int pointer = 0;
  88         private int length;
  89 
  90         private ClassPathIndexInput(InputStream in, int length) throws IOException {
  91             this.length = length;
  92             // read whole file into memory, so we can provide random access
  93             data = new byte[length];
  94             // read in upto 20k chunks
  95             // this is needed as the amount of bytes read in any call in not
  96             // garenteed to be number asked for
  97             final byte[] buf = new byte[1024*20];
  98             int offset = 0, remaining = length, read;
  99             do {
 100                 read = in.read(buf,0,Math.min(remaining, buf.length));
 101                 // copy read bytes to data
 102                 if (read > 0) {
 103                     System.arraycopy(buf, 0, data, offset, read);
 104                     offset += read;
 105                     remaining -= read;
 106                 }
 107             } while (read != -1 && remaining > 0);
 108             in.close();
 109         }
 110 
 111         @Override public byte readByte() throws IOException {
 112             return data[pointer ++];
 113         }
 114 
 115         @Override public void readBytes(byte[] bytes, int offset, int len) throws IOException {
 116             System.arraycopy(data, pointer, bytes, offset, len);
 117             pointer += len;
 118         }
 119 
 120         @Override public void close() throws IOException {}
 121 
 122         @Override public long getFilePointer() { return pointer; }
 123 
 124         @Override public void seek(long l) throws IOException { pointer = (int)l; }
 125 
 126         @Override public long length() { return length; }
 127     }
 128 
 129     @Override public void close() throws IOException {}
 130     @Override public boolean fileExists(String s) throws IOException { throw new UnsupportedOperationException("Not implemented"); }
 131     @Override public long fileModified(String s) throws IOException { throw new UnsupportedOperationException("Not implemented"); }
 132     @Override @Deprecated public void touchFile(String s) throws IOException { throw new UnsupportedOperationException("Not implemented"); }
 133     @Override public void deleteFile(String s) throws IOException { throw new UnsupportedOperationException("Not implemented"); }
 134     @Override public long fileLength(String s) throws IOException { throw new UnsupportedOperationException("Not implemented"); }
 135     @Override public IndexOutput createOutput(String s) throws IOException { throw new UnsupportedOperationException("Not implemented"); }
 136 }