1   package org.apache.lucene.index;
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 org.apache.lucene.store.IndexInput;
21  
22  import java.io.IOException;
23  
24  final class SegmentTermPositions
25  extends SegmentTermDocs implements TermPositions {
26    private IndexInput proxStream;
27    private int proxCount;
28    private int position;
29    
30    // the current payload length
31    private int payloadLength;
32    // indicates whether the payload of the currend position has
33    // been read from the proxStream yet
34    private boolean needToLoadPayload;
35    
36    // these variables are being used to remember information
37    // for a lazy skip
38    private long lazySkipPointer = -1;
39    private int lazySkipProxCount = 0;
40    
41    SegmentTermPositions(SegmentReader p) {
42      super(p);
43      this.proxStream = null;  // the proxStream will be cloned lazily when nextPosition() is called for the first time
44    }
45  
46    final void seek(TermInfo ti, Term term) throws IOException {
47      super.seek(ti, term);
48      if (ti != null)
49        lazySkipPointer = ti.proxPointer;
50      
51      lazySkipProxCount = 0;
52      proxCount = 0;
53      payloadLength = 0;
54      needToLoadPayload = false;
55    }
56  
57    public final void close() throws IOException {
58      super.close();
59      if (proxStream != null) proxStream.close();
60    }
61  
62    public final int nextPosition() throws IOException {
63      if (currentFieldOmitTf)
64        // This field does not store term freq, positions, payloads
65        return 0;
66      // perform lazy skips if neccessary
67      lazySkip();
68      proxCount--;
69      return position += readDeltaPosition();
70    }
71  
72    private final int readDeltaPosition() throws IOException {
73      int delta = proxStream.readVInt();
74      if (currentFieldStoresPayloads) {
75        // if the current field stores payloads then
76        // the position delta is shifted one bit to the left.
77        // if the LSB is set, then we have to read the current
78        // payload length
79        if ((delta & 1) != 0) {
80          payloadLength = proxStream.readVInt();
81        } 
82        delta >>>= 1;
83        needToLoadPayload = true;
84      }
85      return delta;
86    }
87    
88    protected final void skippingDoc() throws IOException {
89      // we remember to skip a document lazily
90      lazySkipProxCount += freq;
91    }
92  
93    public final boolean next() throws IOException {
94      // we remember to skip the remaining positions of the current
95      // document lazily
96      lazySkipProxCount += proxCount;
97      
98      if (super.next()) {               // run super
99        proxCount = freq;               // note frequency
100       position = 0;               // reset position
101       return true;
102     }
103     return false;
104   }
105 
106   public final int read(final int[] docs, final int[] freqs) {
107     throw new UnsupportedOperationException("TermPositions does not support processing multiple documents in one call. Use TermDocs instead.");
108   }
109 
110 
111   /** Called by super.skipTo(). */
112   protected void skipProx(long proxPointer, int payloadLength) throws IOException {
113     // we save the pointer, we might have to skip there lazily
114     lazySkipPointer = proxPointer;
115     lazySkipProxCount = 0;
116     proxCount = 0;
117     this.payloadLength = payloadLength;
118     needToLoadPayload = false;
119   }
120 
121   private void skipPositions(int n) throws IOException {
122     assert !currentFieldOmitTf;
123     for (int f = n; f > 0; f--) {        // skip unread positions
124       readDeltaPosition();
125       skipPayload();
126     }      
127   }
128   
129   private void skipPayload() throws IOException {
130     if (needToLoadPayload && payloadLength > 0) {
131       proxStream.seek(proxStream.getFilePointer() + payloadLength);
132     }
133     needToLoadPayload = false;
134   }
135 
136   // It is not always neccessary to move the prox pointer
137   // to a new document after the freq pointer has been moved.
138   // Consider for example a phrase query with two terms:
139   // the freq pointer for term 1 has to move to document x
140   // to answer the question if the term occurs in that document. But
141   // only if term 2 also matches document x, the positions have to be
142   // read to figure out if term 1 and term 2 appear next
143   // to each other in document x and thus satisfy the query.
144   // So we move the prox pointer lazily to the document
145   // as soon as positions are requested.
146   private void lazySkip() throws IOException {
147     if (proxStream == null) {
148       // clone lazily
149       proxStream = (IndexInput)parent.proxStream.clone();
150     }
151     
152     // we might have to skip the current payload
153     // if it was not read yet
154     skipPayload();
155       
156     if (lazySkipPointer != -1) {
157       proxStream.seek(lazySkipPointer);
158       lazySkipPointer = -1;
159     }
160      
161     if (lazySkipProxCount != 0) {
162       skipPositions(lazySkipProxCount);
163       lazySkipProxCount = 0;
164     }
165   }
166   
167   public int getPayloadLength() {
168     return payloadLength;
169   }
170 
171   public byte[] getPayload(byte[] data, int offset) throws IOException {
172     if (!needToLoadPayload) {
173       throw new IOException("Payload cannot be loaded more than once for the same term position.");
174     }
175 
176     // read payloads lazily
177     byte[] retArray;
178     int retOffset;
179     if (data == null || data.length - offset < payloadLength) {
180       // the array is too small to store the payload data,
181       // so we allocate a new one
182       retArray = new byte[payloadLength];
183       retOffset = 0;
184     } else {
185       retArray = data;
186       retOffset = offset;
187     }
188     proxStream.readBytes(retArray, retOffset, payloadLength);
189     needToLoadPayload = false;
190     return retArray;
191   }
192 
193   public boolean isPayloadAvailable() {
194     return needToLoadPayload && payloadLength > 0;
195   }
196 
197 }
198