Commit | Line | Data |
---|---|---|
866e5b51 | 1 | /******************************************************************************* |
60ae41e1 | 2 | * Copyright (c) 2011, 2014 Ericsson, Ecole Polytechnique de Montreal and others |
866e5b51 FC |
3 | * |
4 | * All rights reserved. This program and the accompanying materials are made | |
5 | * available under the terms of the Eclipse Public License v1.0 which | |
6 | * accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
8 | * | |
9 | * Contributors: Matthew Khouzam - Initial API and implementation | |
10 | * Contributors: Simon Marchi - Initial API and implementation | |
11 | *******************************************************************************/ | |
12 | ||
486efb2e | 13 | package org.eclipse.linuxtools.ctf.core.trace; |
866e5b51 FC |
14 | |
15 | import java.io.File; | |
aa3b05ef | 16 | import java.io.IOException; |
f4a474e3 | 17 | import java.nio.ByteBuffer; |
866e5b51 | 18 | import java.nio.channels.FileChannel; |
aa3b05ef | 19 | import java.nio.channels.FileChannel.MapMode; |
b3151232 | 20 | import java.nio.file.StandardOpenOption; |
866e5b51 FC |
21 | import java.util.UUID; |
22 | ||
a4fa4e36 | 23 | import org.eclipse.jdt.annotation.NonNull; |
486efb2e | 24 | import org.eclipse.linuxtools.ctf.core.event.io.BitBuffer; |
a4fa4e36 MK |
25 | import org.eclipse.linuxtools.ctf.core.event.scope.IDefinitionScope; |
26 | import org.eclipse.linuxtools.ctf.core.event.scope.LexicalScope; | |
866e5b51 | 27 | import org.eclipse.linuxtools.ctf.core.event.types.Definition; |
21fb02fa MK |
28 | import org.eclipse.linuxtools.ctf.core.event.types.EnumDefinition; |
29 | import org.eclipse.linuxtools.ctf.core.event.types.FloatDefinition; | |
cc98c947 | 30 | import org.eclipse.linuxtools.ctf.core.event.types.IDefinition; |
866e5b51 | 31 | import org.eclipse.linuxtools.ctf.core.event.types.IntegerDefinition; |
21fb02fa | 32 | import org.eclipse.linuxtools.ctf.core.event.types.StringDefinition; |
a4fa4e36 | 33 | import org.eclipse.linuxtools.ctf.core.event.types.StructDeclaration; |
866e5b51 | 34 | import org.eclipse.linuxtools.ctf.core.event.types.StructDefinition; |
f4a474e3 | 35 | import org.eclipse.linuxtools.internal.ctf.core.SafeMappedByteBuffer; |
7b4f13e6 | 36 | import org.eclipse.linuxtools.internal.ctf.core.event.types.ArrayDefinition; |
486efb2e AM |
37 | import org.eclipse.linuxtools.internal.ctf.core.trace.StreamInputPacketIndex; |
38 | import org.eclipse.linuxtools.internal.ctf.core.trace.StreamInputPacketIndexEntry; | |
866e5b51 FC |
39 | |
40 | /** | |
41 | * <b><u>StreamInput</u></b> | |
42 | * <p> | |
43 | * Represents a trace file that belongs to a certain stream. | |
cf9a28da | 44 | * |
d84419e1 | 45 | * @since 3.0 |
866e5b51 | 46 | */ |
b3151232 | 47 | // TODO: remove AutoCloseable |
d84419e1 | 48 | public class CTFStreamInput implements IDefinitionScope, AutoCloseable { |
866e5b51 FC |
49 | |
50 | // ------------------------------------------------------------------------ | |
51 | // Attributes | |
52 | // ------------------------------------------------------------------------ | |
53 | ||
54 | /** | |
55 | * The associated Stream | |
56 | */ | |
d84419e1 | 57 | private final CTFStream fStream; |
866e5b51 | 58 | |
866e5b51 FC |
59 | /** |
60 | * Information on the file (used for debugging) | |
61 | */ | |
b3151232 | 62 | @NonNull |
f224bebc | 63 | private final File fFile; |
866e5b51 FC |
64 | |
65 | /** | |
66 | * The packet index of this input | |
67 | */ | |
f224bebc | 68 | private final StreamInputPacketIndex fIndex; |
866e5b51 | 69 | |
f224bebc | 70 | private long fTimestampEnd; |
866e5b51 | 71 | |
8e15b929 | 72 | /** |
bfe038ff MK |
73 | * Definition of trace packet header |
74 | */ | |
a4fa4e36 | 75 | private StructDeclaration fTracePacketHeaderDecl = null; |
bfe038ff | 76 | |
8e15b929 | 77 | /** |
bfe038ff MK |
78 | * Definition of trace stream packet context |
79 | */ | |
a4fa4e36 | 80 | private StructDeclaration fStreamPacketContextDecl = null; |
bfe038ff | 81 | |
8e15b929 | 82 | /** |
132a02b0 MK |
83 | * Total number of lost events in this stream |
84 | */ | |
f224bebc | 85 | private long fLostSoFar = 0; |
132a02b0 | 86 | |
866e5b51 FC |
87 | // ------------------------------------------------------------------------ |
88 | // Constructors | |
89 | // ------------------------------------------------------------------------ | |
90 | ||
91 | /** | |
92 | * Constructs a StreamInput. | |
93 | * | |
94 | * @param stream | |
95 | * The stream to which this StreamInput belongs to. | |
866e5b51 FC |
96 | * @param file |
97 | * Information about the trace file (for debugging purposes). | |
98 | */ | |
b3151232 | 99 | public CTFStreamInput(CTFStream stream, @NonNull File file) { |
f224bebc | 100 | fStream = stream; |
f224bebc MK |
101 | fFile = file; |
102 | fIndex = new StreamInputPacketIndex(); | |
866e5b51 FC |
103 | } |
104 | ||
8e15b929 MK |
105 | @Override |
106 | public void close() throws IOException { | |
8e15b929 MK |
107 | } |
108 | ||
866e5b51 FC |
109 | // ------------------------------------------------------------------------ |
110 | // Getters/Setters/Predicates | |
111 | // ------------------------------------------------------------------------ | |
112 | ||
9ac2eb62 MK |
113 | /** |
114 | * Gets the stream the streamInput wrapper is wrapping | |
21fb02fa | 115 | * |
9ac2eb62 MK |
116 | * @return the stream the streamInput wrapper is wrapping |
117 | */ | |
d84419e1 | 118 | public CTFStream getStream() { |
f224bebc | 119 | return fStream; |
866e5b51 FC |
120 | } |
121 | ||
9ac2eb62 | 122 | /** |
ecb12461 | 123 | * The common streamInput Index |
21fb02fa | 124 | * |
9ac2eb62 MK |
125 | * @return the stream input Index |
126 | */ | |
486efb2e | 127 | StreamInputPacketIndex getIndex() { |
f224bebc | 128 | return fIndex; |
866e5b51 FC |
129 | } |
130 | ||
9ac2eb62 MK |
131 | /** |
132 | * Gets the filename of the streamInput file. | |
21fb02fa | 133 | * |
9ac2eb62 MK |
134 | * @return the filename of the streaminput file. |
135 | */ | |
866e5b51 | 136 | public String getFilename() { |
f224bebc | 137 | return fFile.getName(); |
866e5b51 FC |
138 | } |
139 | ||
9ac2eb62 | 140 | /** |
ecb12461 | 141 | * Gets the last read timestamp of a stream. (this is not necessarily the |
21fb02fa MK |
142 | * last time in the stream.) |
143 | * | |
9ac2eb62 MK |
144 | * @return the last read timestamp |
145 | */ | |
866e5b51 | 146 | public long getTimestampEnd() { |
f224bebc | 147 | return fTimestampEnd; |
866e5b51 FC |
148 | } |
149 | ||
9ac2eb62 | 150 | /** |
21fb02fa MK |
151 | * Sets the last read timestamp of a stream. (this is not necessarily the |
152 | * last time in the stream.) | |
153 | * | |
154 | * @param timestampEnd | |
155 | * the last read timestamp | |
9ac2eb62 | 156 | */ |
866e5b51 | 157 | public void setTimestampEnd(long timestampEnd) { |
f224bebc | 158 | fTimestampEnd = timestampEnd; |
866e5b51 FC |
159 | } |
160 | ||
9ac2eb62 | 161 | /** |
ecb12461 | 162 | * Useless for streaminputs |
9ac2eb62 | 163 | */ |
866e5b51 | 164 | @Override |
a4fa4e36 MK |
165 | public LexicalScope getScopePath() { |
166 | return LexicalScope.STREAM; | |
866e5b51 FC |
167 | } |
168 | ||
169 | // ------------------------------------------------------------------------ | |
170 | // Operations | |
171 | // ------------------------------------------------------------------------ | |
172 | ||
173 | @Override | |
174 | public Definition lookupDefinition(String lookupPath) { | |
175 | /* TODO: lookup in different dynamic scopes is not supported yet. */ | |
176 | return null; | |
177 | } | |
178 | ||
179 | /** | |
180 | * Create the index for this trace file. | |
bfe038ff MK |
181 | */ |
182 | public void setupIndex() { | |
183 | ||
bfe038ff MK |
184 | /* |
185 | * The BitBuffer to extract data from the StreamInput | |
186 | */ | |
187 | BitBuffer bitBuffer = new BitBuffer(); | |
f224bebc | 188 | bitBuffer.setByteOrder(getStream().getTrace().getByteOrder()); |
bfe038ff MK |
189 | |
190 | /* | |
191 | * Create the definitions we need to read the packet headers + contexts | |
192 | */ | |
193 | if (getStream().getTrace().getPacketHeader() != null) { | |
a4fa4e36 | 194 | fTracePacketHeaderDecl = getStream().getTrace().getPacketHeader(); |
bfe038ff MK |
195 | } |
196 | ||
197 | if (getStream().getPacketContextDecl() != null) { | |
a4fa4e36 | 198 | fStreamPacketContextDecl = getStream().getPacketContextDecl(); |
bfe038ff MK |
199 | } |
200 | ||
201 | } | |
202 | ||
9ac2eb62 MK |
203 | /** |
204 | * Adds the next packet header index entry to the index of a stream input. | |
be6df2d8 | 205 | * |
733c614c MK |
206 | * <strong>This method is slow and can corrupt data if not used |
207 | * properly</strong> | |
208 | * | |
9ac2eb62 MK |
209 | * @return true if there are more packets to add |
210 | * @throws CTFReaderException | |
be6df2d8 | 211 | * If there was a problem reading the packed header |
9ac2eb62 | 212 | */ |
bfe038ff MK |
213 | public boolean addPacketHeaderIndex() throws CTFReaderException { |
214 | long currentPos = 0L; | |
f224bebc MK |
215 | if (!fIndex.getEntries().isEmpty()) { |
216 | StreamInputPacketIndexEntry pos = fIndex.getEntries().lastElement(); | |
bfe038ff MK |
217 | currentPos = computeNextOffset(pos); |
218 | } | |
219 | long fileSize = getStreamSize(); | |
220 | if (currentPos < fileSize) { | |
733c614c | 221 | |
bfe038ff MK |
222 | StreamInputPacketIndexEntry packetIndex = new StreamInputPacketIndexEntry( |
223 | currentPos); | |
b3151232 | 224 | createPacketIndexEntry(fileSize, currentPos, packetIndex); |
f224bebc | 225 | fIndex.addEntry(packetIndex); |
bfe038ff MK |
226 | return true; |
227 | } | |
228 | return false; | |
229 | } | |
230 | ||
bfe038ff | 231 | private long getStreamSize() { |
f224bebc | 232 | return fFile.length(); |
bfe038ff MK |
233 | } |
234 | ||
b3151232 | 235 | private long createPacketIndexEntry(long fileSizeBytes, long packetOffsetBytes, StreamInputPacketIndexEntry packetIndex) |
bfe038ff | 236 | throws CTFReaderException { |
0594c61c | 237 | |
b3151232 | 238 | long pos = readPacketHeader(fileSizeBytes, packetOffsetBytes, packetIndex); |
bfe038ff MK |
239 | |
240 | /* Basic validation */ | |
241 | if (packetIndex.getContentSizeBits() > packetIndex.getPacketSizeBits()) { | |
242 | throw new CTFReaderException("Content size > packet size"); //$NON-NLS-1$ | |
243 | } | |
866e5b51 | 244 | |
bfe038ff MK |
245 | if (packetIndex.getPacketSizeBits() > ((fileSizeBytes - packetIndex |
246 | .getOffsetBytes()) * 8)) { | |
cf9a28da | 247 | throw new CTFReaderException("Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$ |
bfe038ff MK |
248 | } |
249 | ||
250 | /* | |
251 | * Offset in the file, in bits | |
252 | */ | |
b3151232 | 253 | packetIndex.setDataOffsetBits(pos); |
bfe038ff MK |
254 | |
255 | /* | |
256 | * Update the counting packet offset | |
257 | */ | |
21fb02fa | 258 | return computeNextOffset(packetIndex); |
bfe038ff MK |
259 | } |
260 | ||
261 | /** | |
262 | * @param packetIndex | |
263 | * @return | |
264 | */ | |
265 | private static long computeNextOffset( | |
266 | StreamInputPacketIndexEntry packetIndex) { | |
267 | return packetIndex.getOffsetBytes() | |
268 | + ((packetIndex.getPacketSizeBits() + 7) / 8); | |
269 | } | |
270 | ||
b3151232 | 271 | private long readPacketHeader(long fileSizeBytes, |
733c614c | 272 | long packetOffsetBytes, StreamInputPacketIndexEntry packetIndex) throws CTFReaderException { |
b3151232 | 273 | long position = -1; |
aa3b05ef FC |
274 | /* |
275 | * Initial size, it should map at least the packet header + context | |
276 | * size. | |
277 | * | |
278 | * TODO: use a less arbitrary size. | |
279 | */ | |
280 | long mapSize = 4096; | |
281 | /* | |
282 | * If there is less data remaining than what we want to map, reduce the | |
283 | * map size. | |
284 | */ | |
285 | if ((fileSizeBytes - packetIndex.getOffsetBytes()) < mapSize) { | |
286 | mapSize = fileSizeBytes - packetIndex.getOffsetBytes(); | |
287 | } | |
288 | ||
289 | /* | |
290 | * Map the packet. | |
291 | */ | |
b3151232 | 292 | try (FileChannel fc = FileChannel.open(fFile.toPath(), StandardOpenOption.READ)) { |
f4a474e3 | 293 | ByteBuffer map = SafeMappedByteBuffer.map(fc, MapMode.READ_ONLY, packetOffsetBytes, mapSize); |
b3151232 MK |
294 | if (map == null) { |
295 | throw new CTFReaderException("Failed to allocate mapped byte buffer"); //$NON-NLS-1$ | |
296 | } | |
297 | /* | |
298 | * create a packet bit buffer to read the packet header | |
299 | */ | |
300 | BitBuffer bitBuffer = new BitBuffer(map); | |
301 | bitBuffer.setByteOrder(getStream().getTrace().getByteOrder()); | |
302 | /* | |
303 | * Read the trace packet header if it exists. | |
304 | */ | |
305 | if (fTracePacketHeaderDecl != null) { | |
306 | parseTracePacketHeader(fTracePacketHeaderDecl, bitBuffer); | |
307 | } | |
308 | ||
309 | /* | |
310 | * Read the stream packet context if it exists. | |
311 | */ | |
312 | if (fStreamPacketContextDecl != null) { | |
313 | parsePacketContext(fileSizeBytes, fStreamPacketContextDecl, | |
314 | bitBuffer, packetIndex); | |
315 | } else { | |
316 | setPacketContextNull(fileSizeBytes, packetIndex); | |
317 | } | |
318 | ||
319 | position = bitBuffer.position(); | |
aa3b05ef FC |
320 | } catch (IOException e) { |
321 | throw new CTFReaderException(e); | |
322 | } | |
b3151232 | 323 | return position; |
aa3b05ef | 324 | } |
bfe038ff | 325 | |
a4fa4e36 MK |
326 | private void parseTracePacketHeader(StructDeclaration tracePacketHeaderDecl, |
327 | @NonNull BitBuffer bitBuffer) throws CTFReaderException { | |
70f60307 | 328 | StructDefinition tracePacketHeaderDef = tracePacketHeaderDecl.createDefinition(fStream.getTrace(), LexicalScope.TRACE_PACKET_HEADER, bitBuffer); |
866e5b51 FC |
329 | |
330 | /* | |
bfe038ff | 331 | * Check the CTF magic number |
866e5b51 | 332 | */ |
bfe038ff MK |
333 | IntegerDefinition magicDef = (IntegerDefinition) tracePacketHeaderDef |
334 | .lookupDefinition("magic"); //$NON-NLS-1$ | |
335 | if (magicDef != null) { | |
336 | int magic = (int) magicDef.getValue(); | |
337 | if (magic != Utils.CTF_MAGIC) { | |
338 | throw new CTFReaderException( | |
339 | "CTF magic mismatch " + Integer.toHexString(magic) + " vs " + Integer.toHexString(Utils.CTF_MAGIC)); //$NON-NLS-1$//$NON-NLS-2$ | |
340 | } | |
866e5b51 FC |
341 | } |
342 | ||
343 | /* | |
bfe038ff | 344 | * Check the trace UUID |
866e5b51 | 345 | */ |
0594c61c AM |
346 | ArrayDefinition uuidDef = |
347 | (ArrayDefinition) tracePacketHeaderDef.lookupDefinition("uuid"); //$NON-NLS-1$ | |
bfe038ff | 348 | if (uuidDef != null) { |
a4fa4e36 | 349 | UUID uuid = Utils.getUUIDfromDefinition(uuidDef); |
866e5b51 | 350 | |
bfe038ff MK |
351 | if (!getStream().getTrace().getUUID().equals(uuid)) { |
352 | throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$ | |
866e5b51 | 353 | } |
bfe038ff | 354 | } |
866e5b51 | 355 | |
bfe038ff MK |
356 | /* |
357 | * Check that the stream id did not change | |
358 | */ | |
359 | IntegerDefinition streamIDDef = (IntegerDefinition) tracePacketHeaderDef | |
360 | .lookupDefinition("stream_id"); //$NON-NLS-1$ | |
361 | if (streamIDDef != null) { | |
362 | long streamID = streamIDDef.getValue(); | |
866e5b51 | 363 | |
bfe038ff | 364 | if (streamID != getStream().getId()) { |
cf9a28da | 365 | throw new CTFReaderException("Stream ID changing within a StreamInput"); //$NON-NLS-1$ |
866e5b51 | 366 | } |
bfe038ff MK |
367 | } |
368 | } | |
866e5b51 | 369 | |
b3151232 MK |
370 | /** |
371 | * Gets the wrapped file | |
372 | * | |
373 | * @return the file | |
374 | */ | |
375 | @NonNull | |
376 | File getFile() { | |
377 | return fFile; | |
378 | } | |
379 | ||
bfe038ff MK |
380 | private static void setPacketContextNull(long fileSizeBytes, |
381 | StreamInputPacketIndexEntry packetIndex) { | |
382 | /* | |
383 | * If there is no packet context, infer the content and packet size from | |
384 | * the file size (assume that there is only one packet and no padding) | |
385 | */ | |
47ca6c05 SM |
386 | packetIndex.setContentSizeBits(fileSizeBytes * 8); |
387 | packetIndex.setPacketSizeBits(fileSizeBytes * 8); | |
bfe038ff | 388 | } |
866e5b51 | 389 | |
bfe038ff | 390 | private void parsePacketContext(long fileSizeBytes, |
a4fa4e36 | 391 | StructDeclaration streamPacketContextDecl, @NonNull BitBuffer bitBuffer, |
db8e8f7d | 392 | StreamInputPacketIndexEntry packetIndex) throws CTFReaderException { |
70f60307 | 393 | StructDefinition streamPacketContextDef = streamPacketContextDecl.createDefinition(this, LexicalScope.STREAM_PACKET_CONTEXT, bitBuffer); |
866e5b51 | 394 | |
21fb02fa MK |
395 | for (String field : streamPacketContextDef.getDeclaration() |
396 | .getFieldsList()) { | |
cc98c947 | 397 | IDefinition id = streamPacketContextDef.lookupDefinition(field); |
21fb02fa MK |
398 | if (id instanceof IntegerDefinition) { |
399 | packetIndex.addAttribute(field, | |
400 | ((IntegerDefinition) id).getValue()); | |
401 | } else if (id instanceof FloatDefinition) { | |
402 | packetIndex.addAttribute(field, | |
403 | ((FloatDefinition) id).getValue()); | |
404 | } else if (id instanceof EnumDefinition) { | |
405 | packetIndex.addAttribute(field, | |
406 | ((EnumDefinition) id).getValue()); | |
407 | } else if (id instanceof StringDefinition) { | |
408 | packetIndex.addAttribute(field, | |
409 | ((StringDefinition) id).getValue()); | |
410 | } | |
21fb02fa MK |
411 | } |
412 | ||
413 | Long contentSize = (Long) packetIndex.lookupAttribute("content_size"); //$NON-NLS-1$ | |
414 | Long packetSize = (Long) packetIndex.lookupAttribute("packet_size"); //$NON-NLS-1$ | |
0594c61c AM |
415 | Long tsBegin = (Long) packetIndex.lookupAttribute("timestamp_begin"); //$NON-NLS-1$ |
416 | Long tsEnd = (Long) packetIndex.lookupAttribute("timestamp_end"); //$NON-NLS-1$ | |
21fb02fa MK |
417 | String device = (String) packetIndex.lookupAttribute("device"); //$NON-NLS-1$ |
418 | // LTTng Specific | |
0594c61c | 419 | Long cpuId = (Long) packetIndex.lookupAttribute("cpu_id"); //$NON-NLS-1$ |
cf9a28da | 420 | Long lostEvents = (Long) packetIndex.lookupAttribute("events_discarded"); //$NON-NLS-1$ |
132a02b0 MK |
421 | |
422 | /* Read the content size in bits */ | |
21fb02fa MK |
423 | if (contentSize != null) { |
424 | packetIndex.setContentSizeBits(contentSize.intValue()); | |
cf9a28da MK |
425 | } else if (packetSize != null) { |
426 | packetIndex.setContentSizeBits(packetSize.longValue()); | |
bfe038ff MK |
427 | } else { |
428 | packetIndex.setContentSizeBits((int) (fileSizeBytes * 8)); | |
429 | } | |
866e5b51 | 430 | |
132a02b0 | 431 | /* Read the packet size in bits */ |
21fb02fa MK |
432 | if (packetSize != null) { |
433 | packetIndex.setPacketSizeBits(packetSize.intValue()); | |
51598b21 MK |
434 | } else if (packetIndex.getContentSizeBits() != 0) { |
435 | packetIndex.setPacketSizeBits(packetIndex.getContentSizeBits()); | |
bfe038ff | 436 | } else { |
51598b21 | 437 | packetIndex.setPacketSizeBits((int) (fileSizeBytes * 8)); |
bfe038ff MK |
438 | } |
439 | ||
132a02b0 | 440 | /* Read the begin timestamp */ |
0594c61c AM |
441 | if (tsBegin != null) { |
442 | packetIndex.setTimestampBegin(tsBegin.longValue()); | |
bfe038ff MK |
443 | } |
444 | ||
132a02b0 | 445 | /* Read the end timestamp */ |
0594c61c | 446 | if (tsEnd != null) { |
ecb12461 | 447 | if (tsEnd == -1) { |
0594c61c | 448 | tsEnd = Long.MAX_VALUE; |
21fb02fa | 449 | } |
0594c61c | 450 | packetIndex.setTimestampEnd(tsEnd.longValue()); |
bfe038ff | 451 | setTimestampEnd(packetIndex.getTimestampEnd()); |
866e5b51 | 452 | } |
21fb02fa MK |
453 | |
454 | if (device != null) { | |
455 | packetIndex.setTarget(device); | |
456 | } | |
457 | ||
0594c61c AM |
458 | if (cpuId != null) { |
459 | packetIndex.setTarget("CPU" + cpuId.toString()); //$NON-NLS-1$ | |
21fb02fa | 460 | } |
132a02b0 MK |
461 | |
462 | if (lostEvents != null) { | |
f224bebc MK |
463 | packetIndex.setLostEvents(lostEvents - fLostSoFar); |
464 | fLostSoFar = lostEvents; | |
132a02b0 | 465 | } |
866e5b51 FC |
466 | } |
467 | ||
81c8e6f7 MK |
468 | @Override |
469 | public int hashCode() { | |
470 | final int prime = 31; | |
471 | int result = 1; | |
b3151232 | 472 | result = (prime * result) + fFile.hashCode(); |
81c8e6f7 MK |
473 | return result; |
474 | } | |
475 | ||
81c8e6f7 MK |
476 | @Override |
477 | public boolean equals(Object obj) { | |
478 | if (this == obj) { | |
479 | return true; | |
480 | } | |
481 | if (obj == null) { | |
482 | return false; | |
483 | } | |
d84419e1 | 484 | if (!(obj instanceof CTFStreamInput)) { |
81c8e6f7 MK |
485 | return false; |
486 | } | |
d84419e1 | 487 | CTFStreamInput other = (CTFStreamInput) obj; |
b3151232 | 488 | if (!fFile.equals(other.fFile)) { |
81c8e6f7 MK |
489 | return false; |
490 | } | |
491 | return true; | |
492 | } | |
493 | ||
866e5b51 | 494 | } |