DocumentContentImpl.java
001 /*
002  *  DocumentContentImpl.java
003  *
004  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Hamish Cunningham, 11/Feb/2000
013  *
014  *  $Id: DocumentContentImpl.java 17604 2014-03-09 10:08:13Z markagreenwood $
015  */
016 
017 package gate.corpora;
018 
019 import gate.DocumentContent;
020 import gate.util.BomStrippingInputStreamReader;
021 import gate.util.InvalidOffsetException;
022 
023 import java.io.BufferedReader;
024 import java.io.IOException;
025 import java.io.InputStream;
026 import java.net.URL;
027 import java.net.URLConnection;
028 import java.util.zip.GZIPInputStream;
029 
030 import org.apache.commons.io.IOUtils;
031 
032 /** Represents the commonalities between all sorts of document contents.
033   */
034 public class DocumentContentImpl implements DocumentContent
035 {
036   /** Buffer size for reading
037    *  16k is 4 times the block size on most filesystems
038    *  so it should be efficient for most cases
039    *  */
040   private static final int INTERNAL_BUFFER_SIZE  = 16*1024;
041 
042   /** Default construction */
043   public DocumentContentImpl() {
044     content = new String();
045   // default construction
046 
047   /** Contruction from URL and offsets. */
048   public DocumentContentImpl(URL u, String encoding, Long start, Long end)
049   throws IOException {
050 
051     int readLength = 0;
052     char[] readBuffer = new char[INTERNAL_BUFFER_SIZE];
053 
054     BufferedReader uReader = null;
055     InputStream uStream = null;
056     StringBuffer buf = new StringBuffer();
057 
058     long s = 0, e = Long.MAX_VALUE;
059     if(start != null && end != null) {
060       s = start.longValue();
061       e = end.longValue();
062     }
063 
064     try {
065       URLConnection conn = u.openConnection();
066       uStream = conn.getInputStream();
067             
068       if ("gzip".equals(conn.getContentEncoding())) {
069         uStream = new GZIPInputStream(uStream);
070       }
071       
072       if(encoding != null && !encoding.equalsIgnoreCase("")) {
073         uReader = new BomStrippingInputStreamReader(uStream, encoding, INTERNAL_BUFFER_SIZE);
074       else {
075         uReader = new BomStrippingInputStreamReader(uStream, INTERNAL_BUFFER_SIZE);
076       };
077   
078       // 1. skip S characters
079       uReader.skip(s);
080   
081       // 2. how many character shall I read?
082       long toRead = e - s;
083   
084       // 3. read gtom source into buffer
085       while (
086         toRead > &&
087         (readLength = uReader.read(readBuffer, 0, INTERNAL_BUFFER_SIZE)) != -1
088       ) {
089         if (toRead <  readLength) {
090           //well, if toRead(long) is less than readLenght(int)
091           //then there can be no overflow, so the cast is safe
092           readLength = (int)toRead;
093         }
094   
095         buf.append(readBuffer, 0, readLength);
096         toRead -= readLength;
097       }
098     }
099     finally {
100       // 4.close reader
101       IOUtils.closeQuietly(uReader);
102       IOUtils.closeQuietly(uStream);
103     }
104 
105     content = new String(buf);
106     originalContent = content;
107   // Contruction from URL and offsets
108 
109   /** Propagate changes to the document content. */
110   void edit(Long start, Long end, DocumentContent replacement)
111   {
112     int s = start.intValue(), e = end.intValue();
113     String repl = replacement == null "" :
114       ((DocumentContentImplreplacement).content;
115     StringBuffer newContent = new StringBuffer(content);
116     newContent.replace(s, e, repl);
117     content = newContent.toString();
118   // edit(start,end,replacement)
119 
120   @Override
121   public DocumentContent getContent(Long start, Long end)
122     throws InvalidOffsetException
123   {
124     if(! isValidOffsetRange(start, end))
125       throw new InvalidOffsetException("Invalid offset range "+start+" to "+end+
126               " for document content of length "+this.size());
127 
128     return new DocumentContentImpl(
129       content.substring(start.intValue(), end.intValue())
130     );
131   // getContent(start, end)
132 
133   /** Returns the String representing the content in case of a textual document.
134     * NOTE: this is a temporary solution until we have a more generic one.
135     */
136   @Override
137   public String toString(){
138     return content;
139   }
140 
141   /** The size of this content (e.g. character length for textual
142     * content).
143     */
144   @Override
145   public Long size() {
146     return new Long(content.length());
147   // size()
148 
149   /** Check that an offset is valid */
150   boolean isValidOffset(Long offset) {
151     if(offset == null)
152       return false;
153 
154     long o = offset.longValue();
155     long len = content.length();
156     if(o > len || o < 0)
157       return false;
158 
159     return true;
160   // isValidOffset
161 
162   /** Check that both start and end are valid offsets and that
163     * they constitute a valid offset range
164     */
165   boolean isValidOffsetRange(Long start, Long end) {
166     return
167       isValidOffset(start&& isValidOffset(end&&
168       start.longValue() <= end.longValue();
169   // isValidOffsetRange(start,end)
170 
171   /** Two documents are the same if their contents is the same
172    */
173   @Override
174   public boolean equals(Object other) {
175     if (!(other instanceof DocumentContentImpl)) return false;
176 
177     DocumentContentImpl docImpl = (DocumentContentImplother;
178     return content.equals(docImpl.toString());
179   // equals
180 
181   /** Calculate the hash value for the object. */
182   @Override
183   public int hashCode(){ return toString().hashCode()}
184 
185   /** Just for now - later we have to cater for different types of
186     * content.
187     */
188   String content;
189 
190   /**
191    * For preserving the original content of the document.
192    * The edit command didn't affect on the original content.
193    * If you construct the content by URL the originalContent will keep
194    * whole information retrieved by URL even you set some start and end.
195    */
196   String originalContent;
197 
198   /**
199    * Return the original content of the document received during the loading
200    * phase or on construction from string.
201    */
202   public String getOriginalContent() { return originalContent; }
203 
204   /** For ranges */
205   public DocumentContentImpl(String s)
206     content = s; originalContent = content; }
207 
208   /** Freeze the serialization UID. */
209   static final long serialVersionUID = -1426940535575467461L;
210 // class DocumentContentImpl