Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/HgDataFile.java @ 277:74e7493a042a
Favor delegation over generalization
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Mon, 29 Aug 2011 23:14:59 +0200 |
| parents | 6355ecda1f08 |
| children | 981f9f50bb6c |
comparison
equal
deleted
inserted
replaced
| 276:6355ecda1f08 | 277:74e7493a042a |
|---|---|
| 32 import org.tmatesoft.hg.core.HgDataStreamException; | 32 import org.tmatesoft.hg.core.HgDataStreamException; |
| 33 import org.tmatesoft.hg.core.HgException; | 33 import org.tmatesoft.hg.core.HgException; |
| 34 import org.tmatesoft.hg.core.Nodeid; | 34 import org.tmatesoft.hg.core.Nodeid; |
| 35 import org.tmatesoft.hg.internal.DataAccess; | 35 import org.tmatesoft.hg.internal.DataAccess; |
| 36 import org.tmatesoft.hg.internal.FilterByteChannel; | 36 import org.tmatesoft.hg.internal.FilterByteChannel; |
| 37 import org.tmatesoft.hg.internal.FilterDataAccess; | |
| 37 import org.tmatesoft.hg.internal.IntMap; | 38 import org.tmatesoft.hg.internal.IntMap; |
| 38 import org.tmatesoft.hg.internal.RevlogStream; | 39 import org.tmatesoft.hg.internal.RevlogStream; |
| 39 import org.tmatesoft.hg.util.ByteChannel; | 40 import org.tmatesoft.hg.util.ByteChannel; |
| 40 import org.tmatesoft.hg.util.CancelSupport; | 41 import org.tmatesoft.hg.util.CancelSupport; |
| 41 import org.tmatesoft.hg.util.CancelledException; | 42 import org.tmatesoft.hg.util.CancelledException; |
| 199 throw new IllegalArgumentException(); | 200 throw new IllegalArgumentException(); |
| 200 } | 201 } |
| 201 if (metadata == null) { | 202 if (metadata == null) { |
| 202 metadata = new Metadata(); | 203 metadata = new Metadata(); |
| 203 } | 204 } |
| 204 ContentPipe insp; | 205 ErrorHandlingInspector insp; |
| 205 if (metadata.none(revision)) { | 206 if (metadata.none(revision)) { |
| 206 insp = new ContentPipe(sink, 0); | 207 insp = new ContentPipe(sink, 0); |
| 207 } else if (metadata.known(revision)) { | 208 } else if (metadata.known(revision)) { |
| 208 insp = new ContentPipe(sink, metadata.dataOffset(revision)); | 209 insp = new ContentPipe(sink, metadata.dataOffset(revision)); |
| 209 } else { | 210 } else { |
| 210 // do not know if there's metadata | 211 // do not know if there's metadata |
| 211 insp = new MetadataContentPipe(sink, metadata, getPath()); | 212 insp = new MetadataInspector(metadata, getPath(), new ContentPipe(sink, 0)); |
| 212 } | 213 } |
| 213 insp.checkCancelled(); | 214 insp.checkCancelled(); |
| 214 super.content.iterate(revision, revision, true, insp); | 215 super.content.iterate(revision, revision, true, insp); |
| 215 try { | 216 try { |
| 216 insp.checkFailed(); // XXX is there real need to throw IOException from ContentPipe? | 217 insp.checkFailed(); // XXX is there real need to throw IOException from ContentPipe? |
| 407 } | 408 } |
| 408 return null; | 409 return null; |
| 409 } | 410 } |
| 410 } | 411 } |
| 411 | 412 |
| 412 private static class MetadataContentPipe extends ContentPipe { | 413 private static class MetadataInspector extends ErrorHandlingInspector implements RevlogStream.Inspector { |
| 413 | |
| 414 private final Metadata metadata; | 414 private final Metadata metadata; |
| 415 private final Path fname; // need this only for error reporting | 415 private final Path fname; // need this only for error reporting |
| 416 | 416 private final RevlogStream.Inspector delegate; |
| 417 public MetadataContentPipe(ByteChannel sink, Metadata _metadata, Path file) { | 417 |
| 418 super(sink, 0); | 418 public MetadataInspector(Metadata _metadata, Path file, RevlogStream.Inspector chain) { |
| 419 metadata = _metadata; | 419 metadata = _metadata; |
| 420 fname = file; | 420 fname = file; |
| 421 } | 421 delegate = chain; |
| 422 | 422 setCancelSupport(CancelSupport.Factory.get(chain)); |
| 423 @Override | 423 } |
| 424 protected void prepare(int revisionNumber, DataAccess da) throws HgException, IOException { | 424 |
| 425 final int daLength = da.length(); | 425 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { |
| 426 if (daLength < 4 || da.readByte() != 1 || da.readByte() != 10) { | 426 try { |
| 427 metadata.recordNone(revisionNumber); | 427 final int daLength = data.length(); |
| 428 da.reset(); | 428 if (daLength < 4 || data.readByte() != 1 || data.readByte() != 10) { |
| 429 return; | 429 metadata.recordNone(revisionNumber); |
| 430 } | 430 data.reset(); |
| 431 } else { | |
| 432 ArrayList<MetadataEntry> _metadata = new ArrayList<MetadataEntry>(); | |
| 433 int offset = parseMetadata(data, daLength, _metadata); | |
| 434 metadata.add(revisionNumber, offset, _metadata); | |
| 435 // da is in prepared state (i.e. we consumed all bytes up to metadata end). | |
| 436 // However, it's not safe to assume delegate won't call da.reset() for some reason, | |
| 437 // and we need to ensure predictable result. | |
| 438 data.reset(); | |
| 439 data = new FilterDataAccess(data, offset, daLength - offset); | |
| 440 } | |
| 441 if (delegate != null) { | |
| 442 delegate.next(revisionNumber, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, nodeid, data); | |
| 443 } | |
| 444 } catch (IOException ex) { | |
| 445 recordFailure(ex); | |
| 446 } catch (HgDataStreamException ex) { | |
| 447 recordFailure(ex.setRevisionNumber(revisionNumber)); | |
| 448 } | |
| 449 } | |
| 450 | |
| 451 private int parseMetadata(DataAccess data, final int daLength, ArrayList<MetadataEntry> _metadata) throws IOException, HgDataStreamException { | |
| 431 int lastEntryStart = 2; | 452 int lastEntryStart = 2; |
| 432 int lastColon = -1; | 453 int lastColon = -1; |
| 433 ArrayList<MetadataEntry> _metadata = new ArrayList<MetadataEntry>(); | |
| 434 // XXX in fact, need smth like ByteArrayBuilder, similar to StringBuilder, | 454 // XXX in fact, need smth like ByteArrayBuilder, similar to StringBuilder, |
| 435 // which can't be used here because we can't convert bytes to chars as we read them | 455 // which can't be used here because we can't convert bytes to chars as we read them |
| 436 // (there might be multi-byte encoding), and we need to collect all bytes before converting to string | 456 // (there might be multi-byte encoding), and we need to collect all bytes before converting to string |
| 437 ByteArrayOutputStream bos = new ByteArrayOutputStream(); | 457 ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| 438 String key = null, value = null; | 458 String key = null, value = null; |
| 439 boolean byteOne = false; | 459 boolean byteOne = false; |
| 440 for (int i = 2; i < daLength; i++) { | 460 for (int i = 2; i < daLength; i++) { |
| 441 byte b = da.readByte(); | 461 byte b = data.readByte(); |
| 442 if (b == '\n') { | 462 if (b == '\n') { |
| 443 if (byteOne) { // i.e. \n follows 1 | 463 if (byteOne) { // i.e. \n follows 1 |
| 444 lastEntryStart = i+1; | 464 lastEntryStart = i+1; |
| 445 // XXX is it possible to have here incomplete key/value (i.e. if last pair didn't end with \n) | 465 // XXX is it possible to have here incomplete key/value (i.e. if last pair didn't end with \n) |
| 446 break; | 466 break; |
| 454 key = value = null; | 474 key = value = null; |
| 455 lastColon = -1; | 475 lastColon = -1; |
| 456 lastEntryStart = i+1; | 476 lastEntryStart = i+1; |
| 457 continue; | 477 continue; |
| 458 } | 478 } |
| 459 // byteOne has to be consumed up to this line, if not jet, consume it | 479 // byteOne has to be consumed up to this line, if not yet, consume it |
| 460 if (byteOne) { | 480 if (byteOne) { |
| 461 // insert 1 we've read on previous step into the byte builder | 481 // insert 1 we've read on previous step into the byte builder |
| 462 bos.write(1); | 482 bos.write(1); |
| 483 byteOne = false; | |
| 463 // fall-through to consume current byte | 484 // fall-through to consume current byte |
| 464 byteOne = false; | |
| 465 } | 485 } |
| 466 if (b == (int) ':') { | 486 if (b == (int) ':') { |
| 467 assert value == null; | 487 assert value == null; |
| 468 key = new String(bos.toByteArray()); | 488 key = new String(bos.toByteArray()); |
| 469 bos.reset(); | 489 bos.reset(); |
| 472 byteOne = true; | 492 byteOne = true; |
| 473 } else { | 493 } else { |
| 474 bos.write(b); | 494 bos.write(b); |
| 475 } | 495 } |
| 476 } | 496 } |
| 477 metadata.add(revisionNumber, lastEntryStart, _metadata); | 497 if (data.isEmpty() || !byteOne) { |
| 478 if (da.isEmpty() || !byteOne) { | 498 throw new HgDataStreamException(fname, "Metadata is not closed properly", null); |
| 479 throw new HgDataStreamException(fname, String.format("Metadata for revision %d is not closed properly", revisionNumber), null); | 499 } |
| 480 } | 500 return lastEntryStart; |
| 481 // da is in prepared state (i.e. we consumed all bytes up to metadata end). | |
| 482 } | 501 } |
| 483 } | 502 } |
| 484 } | 503 } |
