View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  
20  package org.xnap.plugin.opennap.net;
21  
22  import java.io.File;
23  
24  import org.xnap.transfer.Segment;
25  
26  /***
27   * <p>The end of the segment can change through a split.
28   */
29  public class OpenNapSegment implements Cloneable, Segment {
30  
31  	//--- Constant(s) ---
32  
33  	/***
34  	 * The minimun size for a single segment.
35  	 */
36  	public static final long MIN_SEGEMENT_SIZE = 100 * 1024;
37  
38  	//--- Data Field(s) ---
39  
40  	private OpenNapSegmentData data;
41  	private long end;
42  	private long total;
43  	private long transferred;
44  	private OpenNapSegment previous;
45  	private OpenNapSegment next;
46  	private OpenNapDownload download;
47  	
48      //--- Constructor(s) ---
49  
50      public OpenNapSegment(long start, long end, long total) 
51      {
52  		data = new OpenNapSegmentData();
53  		
54  		data.start = start;
55  		this.end = end;
56  		this.total = total;
57  	}
58  
59  	/***
60  	 * Creates a new segment based on resume data. The segment spans from
61  	 * <code>data.start</code> to <code>total</code>.
62  	 */
63  	public OpenNapSegment(OpenNapSegmentData data, long total) 
64  	{
65  		this.data = data;
66  		
67  		this.transferred = (data.file != null) ? data.file.length() : 0;
68  		this.end = total;
69  		this.total = total;
70  	}
71  
72      //--- Methods ---
73  
74  	public Object clone()
75  	{
76  		OpenNapSegment n = new OpenNapSegment(data.start, end, total);
77  
78  		n.data.file = data.file;
79  		n.data.mergeFailCount = data.mergeFailCount;
80  		n.download = download;
81  		n.previous = previous;
82  		n.next = next;
83  		n.transferred = transferred;
84  
85  		return n;
86  	}
87  
88  	public int getAvailability()
89  	{
90  		return (download != null) ? 128 : 0;
91  	}
92  
93  	OpenNapSegmentData getData()
94  	{
95  		return data;
96  	}
97  
98  	public long getEnd() 
99  	{
100 		return this.end;
101 	}
102 
103 	/***
104 	 * Returns the download currently downloading the segment.
105 	 *
106 	 * @return null, if this segment is currently idle
107 	 */
108 	public OpenNapDownload getDownload()
109 	{
110 		return download;
111 	}
112 
113 	/***
114 	 * Returns the download offset that should be sent to the peer.
115 	 */
116 	public long getDownloadOffset()
117 	{
118 		return data.start + transferred - getOverlap();
119 	}
120 
121 	/***
122 	 * Returns the file offset to seek to when matching the overlap
123 	 */
124 	public long getFileOffset()
125 	{
126 		return transferred - getOverlap();
127 	}
128 
129  	public File getFile()
130  	{
131  		return data.file;
132  	}
133 	
134 	public OpenNapSegment getNext()
135 	{
136 		return next;
137 	}
138 
139 	/***
140 	 * Returns the number of bytes that need to be matched.
141 	 */
142 	public int getOverlap()
143 	{
144 		return (transferred < OpenNapSegmentManager.OVERLAP) 
145 			? (int)transferred
146 			: OpenNapSegmentManager.OVERLAP;
147 	}
148 
149 	public OpenNapSegment getPrevious()
150 	{
151 		return previous;
152 	}
153 
154 	public long getStart() 
155 	{
156 		return data.start;
157 	}
158 
159 	public long getTotal() 
160 	{
161 		return total;
162 	}
163 
164 	public long getTransferred() 
165 	{
166 		return transferred;
167 	}
168 
169 	public boolean isFinished()
170 	{
171 		return getRemaining() == 0;
172 	}
173 
174 	public void setDownload(OpenNapDownload download)
175 	{
176 		this.download = download;
177 	}
178 
179 	/***
180 	 * <p>Modifies: <code>file</code>
181 	 */
182 	public synchronized void setFile(File file)
183 	{
184 		data.file = file;
185 	}
186 
187 	/***
188 	 * Commits l bytes as transferred.
189 	 *
190 	 * <p>Modifies: <code>transferred</code>.
191 	 *
192 	 */
193 	synchronized void commit(int l)
194 	{
195 		transferred += l;
196 	}
197 
198 	/***
199 	 * Commits l bytes as transferred.
200 	 *
201 	 * <p>Modifies <code>transferred</code>, <code>offset</code>.
202 	 *
203 	 * @return the number of bytes that were accepted and should
204 	 *         be written to file
205 	 */
206 	synchronized int commitToFile(int l)
207 	{
208 		if (data.start + transferred + l > end) {
209 			l = (int)(end - data.start - transferred);
210 		}
211 
212 		transferred += l;
213 
214 		return l;
215 	}
216 
217 	synchronized long getRemaining()
218 	{
219 		return end - data.start - transferred;
220 	}
221 
222 	/***
223 	 * Appends <code>n</code> after this segment. Shortens this segment.
224 	 *
225 	 * <p>Modifies: <code>end</code>
226 	 */
227 	synchronized void append(OpenNapSegment next)
228 	{
229 		if (next.data.start 
230 			< this.data.start + this.transferred - OpenNapSegmentManager.OVERLAP) {
231 
232 			throw new IllegalArgumentException("invalid start");
233 		}
234 
235 		// shorten previous segment
236 		this.end = next.data.start + OpenNapSegmentManager.OVERLAP;
237 
238 		this.next = next;
239 		next.previous = this;
240 	}
241 
242 	/***
243 	 * Merges next into this segment by appending it.
244 	 */
245 	synchronized void merge(OpenNapSegment next, int overlap)
246 	{
247 		this.end = next.getEnd();
248 		this.transferred += next.getTransferred() - overlap;
249 
250 		this.next = next.getNext();
251 		if (this.next != null) {
252 			this.next.previous = this;
253 		}
254 
255 		next.previous = null;
256 		next.next = null;
257 	}
258 
259 
260 	/***
261 	 * <p>Modifies: <code>end</code>
262 	 */
263 	synchronized OpenNapSegment split(int overlap)
264 	{
265 		long oldEnd = end;
266 		long half = getRemaining() / 2;
267 
268 		if (half < MIN_SEGEMENT_SIZE) {
269 			return null;
270 		}
271 	
272 		// shrink
273 		end = data.start + transferred + half;
274 		OpenNapSegment s = new OpenNapSegment(end - overlap, oldEnd, getTotal());
275 
276 		s.next = this.next;
277 		if (s.next != null) {
278 			s.next.previous = s;
279 		}
280 		s.previous = this;
281 		this.next = s;
282 		return s;
283 	}
284 
285 	synchronized void trim()
286 	{
287 		end = data.start + transferred;
288 	}
289 }