# HG changeset patch # User Artem Tikhomirov # Date 1296772328 -3600 # Node ID c0cc2535462c08b8162adbf0dc188a0017a45d3b # Parent 46291ec605a012caf84db6d0004f176178308633 Introduced channels to pipeline (and easily filter) data streams diff -r 46291ec605a0 -r c0cc2535462c src/org/tmatesoft/hg/core/CatCommand.java --- a/src/org/tmatesoft/hg/core/CatCommand.java Thu Feb 03 22:13:55 2011 +0100 +++ b/src/org/tmatesoft/hg/core/CatCommand.java Thu Feb 03 23:32:08 2011 +0100 @@ -20,11 +20,10 @@ import static org.tmatesoft.hg.repo.HgRepository.TIP; import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.OutputStream; import org.tmatesoft.hg.repo.HgDataFile; import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.util.ByteChannel; /** * Command to obtain content of a @@ -61,26 +60,26 @@ return this; } - public void execute(OutputStream os) throws IOException /*TODO own exception type*/ { + public void execute(ByteChannel sink) throws Exception /*TODO own exception type*/ { if (localRevision == BAD_REVISION && revision == null) { throw new IllegalArgumentException("Either local file revision number or nodeid shall be specified"); } if (file == null) { throw new IllegalArgumentException("Name of the file is missing"); } - if (os == null) { + if (sink == null) { throw new IllegalArgumentException(); } HgDataFile dataFile = repo.getFileNode(file); if (!dataFile.exists()) { throw new FileNotFoundException(); } - byte[] content; + int revToExtract; if (revision != null) { - content = dataFile.content(revision); + revToExtract = dataFile.getLocalRevision(revision); } else { - content = dataFile.content(localRevision); + revToExtract = localRevision; } - os.write(content); + dataFile.content(revToExtract, sink); } } diff -r 46291ec605a0 -r c0cc2535462c src/org/tmatesoft/hg/core/RepositoryFacade.java --- a/src/org/tmatesoft/hg/core/RepositoryFacade.java Thu Feb 03 22:13:55 2011 +0100 +++ b/src/org/tmatesoft/hg/core/RepositoryFacade.java Thu Feb 03 23:32:08 2011 +0100 @@ -41,6 +41,13 @@ repo = new HgLookup().detect(repoLocation.getCanonicalPath()); return repo != null && !repo.isInvalid(); } + + public HgRepository getRepository() { + if (repo == null) { + throw new IllegalStateException("Call any of #init*() methods first first"); + } + return repo; + } public LogCommand createLogCommand() { return new LogCommand(repo/*, getCommandContext()*/); diff -r 46291ec605a0 -r c0cc2535462c src/org/tmatesoft/hg/internal/ByteArrayChannel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/internal/ByteArrayChannel.java Thu Feb 03 23:32:08 2011 +0100 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2011 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@svnkit.com + */ +package org.tmatesoft.hg.internal; + +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.List; + +import org.tmatesoft.hg.util.ByteChannel; + +/** + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class ByteArrayChannel implements ByteChannel { + private final List buffers; + private ByteBuffer target; + private byte[] result; + + public ByteArrayChannel() { + this(-1); + } + + public ByteArrayChannel(int size) { + if (size == -1) { + buffers = new LinkedList(); + } else { + if (size < 0) { + throw new IllegalArgumentException(String.valueOf(size)); + } + buffers = null; + target = ByteBuffer.allocate(size); + } + } + + // TODO document what happens on write after toArray() in each case + public int write(ByteBuffer buffer) throws Exception { + int rv = buffer.remaining(); + if (buffers == null) { + target.put(buffer); + } else { + ByteBuffer copy = ByteBuffer.allocate(rv); + copy.put(buffer); + buffers.add(copy); + } + return rv; + } + + public byte[] toArray() { + if (result != null) { + return result; + } + if (buffers == null) { + assert target.hasArray(); + // int total = target.position(); + // System.arraycopy(target.array(), new byte[total]); + // I don't want to duplicate byte[] for now + // although correct way of doing things is to make a copy and discard target + return target.array(); + } else { + int total = 0; + for (ByteBuffer bb : buffers) { + bb.flip(); + total += bb.limit(); + } + result = new byte[total]; + int off = 0; + for (ByteBuffer bb : buffers) { + bb.get(result, off, bb.limit()); + off += bb.limit(); + } + buffers.clear(); + return result; + } + } +} diff -r 46291ec605a0 -r c0cc2535462c src/org/tmatesoft/hg/internal/Filter.java --- a/src/org/tmatesoft/hg/internal/Filter.java Thu Feb 03 22:13:55 2011 +0100 +++ b/src/org/tmatesoft/hg/internal/Filter.java Thu Feb 03 23:32:08 2011 +0100 @@ -28,6 +28,8 @@ */ public interface Filter { + // returns a buffer ready to be read. may return original buffer. + // original buffer may not be fully consumed, #compact() might be operation to perform ByteBuffer filter(ByteBuffer src); interface Factory { diff -r 46291ec605a0 -r c0cc2535462c src/org/tmatesoft/hg/internal/FilterByteChannel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/internal/FilterByteChannel.java Thu Feb 03 23:32:08 2011 +0100 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@svnkit.com + */ +package org.tmatesoft.hg.internal; + +import java.nio.ByteBuffer; +import java.util.Collection; + +import org.tmatesoft.hg.util.ByteChannel; + +/** + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class FilterByteChannel implements ByteChannel { + private final Filter[] filters; + private final ByteChannel delegate; + + public FilterByteChannel(ByteChannel delegateChannel, Collection filtersToApply) { + if (delegateChannel == null || filtersToApply == null) { + throw new IllegalArgumentException(); + } + delegate = delegateChannel; + filters = filtersToApply.toArray(new Filter[filtersToApply.size()]); + } + + public int write(ByteBuffer buffer) throws Exception { + final int srcPos = buffer.position(); + ByteBuffer processed = buffer; + for (Filter f : filters) { + // each next filter consumes not more than previous + // hence total consumed equals position shift in the original buffer + processed = f.filter(processed); + } + delegate.write(processed); + return buffer.position() - srcPos; // consumed as much from original buffer + } + +} diff -r 46291ec605a0 -r c0cc2535462c src/org/tmatesoft/hg/repo/HgDataFile.java --- a/src/org/tmatesoft/hg/repo/HgDataFile.java Thu Feb 03 22:13:55 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgDataFile.java Thu Feb 03 23:32:08 2011 +0100 @@ -18,6 +18,7 @@ import static org.tmatesoft.hg.repo.HgRepository.TIP; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.TreeMap; @@ -25,6 +26,7 @@ import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.core.Path; import org.tmatesoft.hg.internal.RevlogStream; +import org.tmatesoft.hg.util.ByteChannel; @@ -42,11 +44,18 @@ private final Path path; private Metadata metadata; - /*package-local*/HgDataFile(HgRepository hgRepo, Path path, RevlogStream content) { + /*package-local*/HgDataFile(HgRepository hgRepo, Path filePath, RevlogStream content) { super(hgRepo, content); - this.path = path; + path = filePath; } - + + /*package-local*/HgDataFile(HgRepository hgRepo, Path filePath) { + super(hgRepo); + path = filePath; + } + + // exists is not the best name possible. now it means no file with such name was ever known to the repo. + // it might be confused with files existed before but lately removed. public boolean exists() { return content != null; // XXX need better impl } @@ -63,6 +72,21 @@ public byte[] content() { return content(TIP); } + + public void content(int revision, ByteChannel sink) throws /*TODO typed*/Exception { + byte[] content = content(revision); + ByteBuffer buf = ByteBuffer.allocate(512); + int left = content.length; + int offset = 0; + do { + buf.put(content, offset, Math.min(left, buf.remaining())); + buf.flip(); + int consumed = sink.write(buf); + buf.compact(); + offset += consumed; + left -= consumed; + } while (left > 0); + } // for data files need to check heading of the file content for possible metadata // @see http://mercurial.selenic.com/wiki/FileFormats#data.2BAC8- diff -r 46291ec605a0 -r c0cc2535462c src/org/tmatesoft/hg/repo/HgRepository.java --- a/src/org/tmatesoft/hg/repo/HgRepository.java Thu Feb 03 22:13:55 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgRepository.java Thu Feb 03 23:32:08 2011 +0100 @@ -139,13 +139,20 @@ String nPath = normalizePath.rewrite(path); String storagePath = dataPathHelper.rewrite(nPath); RevlogStream content = resolve(Path.create(storagePath)); - return new HgDataFile(this, Path.create(nPath), content); + Path p = Path.create(nPath); + if (content == null) { + return new HgDataFile(this, p); + } + return new HgDataFile(this, p, content); } public HgDataFile getFileNode(Path path) { String storagePath = dataPathHelper.rewrite(path.toString()); RevlogStream content = resolve(Path.create(storagePath)); - // XXX no content when no file? or HgDataFile.exists() to detect that? How about files that were removed in previous releases? + // XXX no content when no file? or HgDataFile.exists() to detect that? + if (content == null) { + return new HgDataFile(this, path); + } return new HgDataFile(this, path, content); } diff -r 46291ec605a0 -r c0cc2535462c src/org/tmatesoft/hg/repo/Revlog.java --- a/src/org/tmatesoft/hg/repo/Revlog.java Thu Feb 03 22:13:55 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/Revlog.java Thu Feb 03 23:32:08 2011 +0100 @@ -38,22 +38,28 @@ */ abstract class Revlog { - private final HgRepository hgRepo; + private final HgRepository repo; protected final RevlogStream content; - protected Revlog(HgRepository hgRepo, RevlogStream content) { + protected Revlog(HgRepository hgRepo, RevlogStream contentStream) { if (hgRepo == null) { throw new IllegalArgumentException(); } - if (content == null) { + if (contentStream == null) { throw new IllegalArgumentException(); } - this.hgRepo = hgRepo; - this.content = content; + repo = hgRepo; + content = contentStream; + } + + // invalid Revlog + protected Revlog(HgRepository hgRepo) { + repo = hgRepo; + content = null; } public final HgRepository getRepo() { - return hgRepo; + return repo; } public int getRevisionCount() { diff -r 46291ec605a0 -r c0cc2535462c src/org/tmatesoft/hg/util/ByteChannel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/util/ByteChannel.java Thu Feb 03 23:32:08 2011 +0100 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@svnkit.com + */ +package org.tmatesoft.hg.util; + +import java.nio.ByteBuffer; + +/** + * Much like {@link java.nio.channels.WritableByteChannel} except for thrown exception + * + * XXX Perhaps, we'll add CharChannel in the future to deal with character conversions/encodings + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public interface ByteChannel { + // XXX does int return value makes any sense given buffer keeps its read state + // not clear what retvalue should be in case some filtering happened inside write - i.e. return + // number of bytes consumed in + int write(ByteBuffer buffer) throws Exception /*FIXME Exception type*/; +} diff -r 46291ec605a0 -r c0cc2535462c test/org/tmatesoft/hg/test/TestByteChannel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/org/tmatesoft/hg/test/TestByteChannel.java Thu Feb 03 23:32:08 2011 +0100 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@svnkit.com + */ +package org.tmatesoft.hg.test; + +import java.util.Arrays; + +import org.junit.Assert; +import org.tmatesoft.hg.core.CatCommand; +import org.tmatesoft.hg.core.RepositoryFacade; +import org.tmatesoft.hg.internal.ByteArrayChannel; +import org.tmatesoft.hg.repo.HgDataFile; +import org.tmatesoft.hg.repo.HgRepository; + +/** + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class TestByteChannel { + + public static void main(String[] args) throws Exception { + RepositoryFacade rf = new RepositoryFacade(); + rf.init(); + HgDataFile file = rf.getRepository().getFileNode("COPYING"); + int rev = HgRepository.TIP; + byte[] oldAccess = file.content(rev); + ByteArrayChannel ch = new ByteArrayChannel(); + file.content(rev, ch); + byte[] newAccess = ch.toArray(); + Assert.assertArrayEquals(oldAccess, newAccess); + //CatCommand cmd = rf.createCatCommand(); + } +}