Switch jgit library to the EDL (3-clause BSD)
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / transport / SideBandInputStream.java
blob9ccf5ada69d7d84dbb6b9ba89f880f4882a316a5
1 /*
2 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
9 * conditions are met:
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
22 * written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org.spearce.jgit.transport;
41 import java.io.IOException;
42 import java.io.InputStream;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
46 import org.spearce.jgit.errors.PackProtocolException;
47 import org.spearce.jgit.errors.TransportException;
48 import org.spearce.jgit.lib.Constants;
49 import org.spearce.jgit.lib.ProgressMonitor;
50 import org.spearce.jgit.util.NB;
52 /**
53 * Unmultiplexes the data portion of a side-band channel.
54 * <p>
55 * Reading from this input stream obtains data from channel 1, which is
56 * typically the bulk data stream.
57 * <p>
58 * Channel 2 is transparently unpacked and "scraped" to update a progress
59 * monitor. The scraping is performed behind the scenes as part of any of the
60 * read methods offered by this stream.
61 * <p>
62 * Channel 3 results in an exception being thrown, as the remote side has issued
63 * an unrecoverable error.
65 * @see PacketLineIn#sideband(ProgressMonitor)
67 class SideBandInputStream extends InputStream {
68 private static final int CH_DATA = 1;
70 private static final int CH_PROGRESS = 2;
72 private static final int CH_ERROR = 3;
74 private static Pattern P_UNBOUNDED = Pattern.compile(
75 ".*?([\\w ]+): (\\d+)(, done)?.*", Pattern.DOTALL);
77 private static Pattern P_BOUNDED = Pattern.compile(
78 ".*?([\\w ]+):.*\\((\\d+)/(\\d+)\\).*", Pattern.DOTALL);
80 private final PacketLineIn pckIn;
82 private final InputStream in;
84 private final ProgressMonitor monitor;
86 private String currentTask;
88 private int lastCnt;
90 private boolean eof;
92 private int channel;
94 private int available;
96 SideBandInputStream(final PacketLineIn aPckIn, final InputStream aIn,
97 final ProgressMonitor aProgress) {
98 pckIn = aPckIn;
99 in = aIn;
100 monitor = aProgress;
101 currentTask = "";
104 @Override
105 public int read() throws IOException {
106 needDataPacket();
107 if (eof)
108 return -1;
109 available--;
110 return in.read();
113 @Override
114 public int read(final byte[] b, int off, int len) throws IOException {
115 int r = 0;
116 while (len > 0) {
117 needDataPacket();
118 if (eof)
119 break;
120 final int n = in.read(b, off, Math.min(len, available));
121 if (n < 0)
122 break;
123 r += n;
124 off += n;
125 len -= n;
126 available -= n;
128 return eof && r == 0 ? -1 : r;
131 private void needDataPacket() throws IOException {
132 if (eof || (channel == CH_DATA && available > 0))
133 return;
134 for (;;) {
135 available = pckIn.readLength();
136 if (available == 0) {
137 eof = true;
138 return;
141 channel = in.read();
142 available -= 5; // length header plus channel indicator
143 if (available == 0)
144 continue;
146 switch (channel) {
147 case CH_DATA:
148 return;
149 case CH_PROGRESS:
150 progress(readString(available));
152 continue;
153 case CH_ERROR:
154 eof = true;
155 throw new TransportException("remote: " + readString(available));
156 default:
157 throw new PackProtocolException("Invalid channel " + channel);
162 private void progress(final String msg) {
163 Matcher matcher;
165 matcher = P_BOUNDED.matcher(msg);
166 if (matcher.matches()) {
167 final String taskname = matcher.group(1);
168 if (!currentTask.equals(taskname)) {
169 currentTask = taskname;
170 lastCnt = 0;
171 final int tot = Integer.parseInt(matcher.group(3));
172 monitor.beginTask(currentTask, tot);
174 final int cnt = Integer.parseInt(matcher.group(2));
175 monitor.update(cnt - lastCnt);
176 lastCnt = cnt;
177 return;
180 matcher = P_UNBOUNDED.matcher(msg);
181 if (matcher.matches()) {
182 final String taskname = matcher.group(1);
183 if (!currentTask.equals(taskname)) {
184 currentTask = taskname;
185 lastCnt = 0;
186 monitor.beginTask(currentTask, ProgressMonitor.UNKNOWN);
188 final int cnt = Integer.parseInt(matcher.group(2));
189 monitor.update(cnt - lastCnt);
190 lastCnt = cnt;
194 private String readString(final int len) throws IOException {
195 final byte[] raw = new byte[len];
196 NB.readFully(in, raw, 0, len);
197 return new String(raw, 0, len, Constants.CHARACTER_ENCODING);