update code style

This commit is contained in:
huangyuhui
2016-01-01 11:03:09 +08:00
parent 1f7eb04215
commit b82243a9c0
298 changed files with 3902 additions and 3998 deletions

View File

@@ -174,15 +174,16 @@ public final class Launcher {
} }
} }
/* /*
static Object getShutdownHaltLock() { * static Object getShutdownHaltLock() {
try { * try {
Class z = Class.forName("java.lang.Shutdown"); * Class z = Class.forName("java.lang.Shutdown");
Field haltLock = z.getDeclaredField("haltLock"); * Field haltLock = z.getDeclaredField("haltLock");
haltLock.setAccessible(true); * haltLock.setAccessible(true);
return haltLock.get(null); * return haltLock.get(null);
} catch (Throwable ex) { * } catch (Throwable ex) {
ex.printStackTrace(); * ex.printStackTrace();
return new Object(); * return new Object();
} * }
}*/ * }
*/
} }

View File

@@ -34,6 +34,7 @@ public interface IPlugin {
* You can modify the application actions by this method. * You can modify the application actions by this method.
* *
* @param profile info to the Minecraft Loader * @param profile info to the Minecraft Loader
*
* @return For example, you can implement IMinecraftProvider to support * @return For example, you can implement IMinecraftProvider to support
* MultiMC * MultiMC
*/ */

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hellominecraft.launcher.launch; package org.jackhuang.hellominecraft.launcher.launch;
import java.io.IOException; import java.io.IOException;
import javax.swing.SwingUtilities;
import org.jackhuang.hellominecraft.C; import org.jackhuang.hellominecraft.C;
import org.jackhuang.hellominecraft.HMCLog; import org.jackhuang.hellominecraft.HMCLog;
import org.jackhuang.hellominecraft.launcher.launch.GameLauncher.DownloadLibraryJob; import org.jackhuang.hellominecraft.launcher.launch.GameLauncher.DownloadLibraryJob;

View File

@@ -111,7 +111,7 @@ public class GameLauncher {
return null; return null;
} }
List<String> lst = null; List<String> lst;
try { try {
lst = loader.makeLaunchingCommand(); lst = loader.makeLaunchingCommand();
} catch (Exception e) { } catch (Exception e) {

View File

@@ -39,8 +39,6 @@ public abstract class IMinecraftDownloadService extends IMinecraftService {
public abstract boolean downloadMinecraftVersionJson(String id); public abstract boolean downloadMinecraftVersionJson(String id);
/** /**
* Get the libraries that need to download. * Get the libraries that need to download.
* *

View File

@@ -74,6 +74,7 @@ public abstract class IMinecraftProvider {
/** /**
* *
* @param v should be resolved * @param v should be resolved
*
* @return libraries of resolved minecraft version v. * @return libraries of resolved minecraft version v.
*/ */
public abstract GameLauncher.DecompressLibraryJob getDecompressLibraries(MinecraftVersion v); public abstract GameLauncher.DecompressLibraryJob getDecompressLibraries(MinecraftVersion v);

View File

@@ -24,6 +24,7 @@ import org.jackhuang.hellominecraft.launcher.settings.Profile;
* @author huangyuhui * @author huangyuhui
*/ */
public abstract class IMinecraftService { public abstract class IMinecraftService {
public Profile profile; public Profile profile;
public IMinecraftService(Profile profile) { public IMinecraftService(Profile profile) {

View File

@@ -17,7 +17,6 @@
*/ */
package org.jackhuang.hellominecraft.launcher.utils.assets; package org.jackhuang.hellominecraft.launcher.utils.assets;
/** /**
* *
* @author huangyuhui * @author huangyuhui

View File

@@ -46,6 +46,7 @@ public abstract class IAuthenticator {
* @param info username & password * @param info username & password
* *
* @return login result * @return login result
*
* @throws * @throws
* org.jackhuang.hellominecraft.launcher.utils.auth.AuthenticationException * org.jackhuang.hellominecraft.launcher.utils.auth.AuthenticationException
*/ */

View File

@@ -30,7 +30,6 @@ public enum InstallerType {
this.id = id; this.id = id;
} }
public String getLocalizedName() { public String getLocalizedName() {
return this.name(); return this.name();
} }

View File

@@ -48,6 +48,7 @@ public abstract class InstallerVersionList implements Consumer<String[]> {
* Get installers you want. * Get installers you want.
* *
* @param mcVersion the installers to this Minecraft version. * @param mcVersion the installers to this Minecraft version.
*
* @return cached result. * @return cached result.
*/ */
protected abstract List<InstallerVersion> getVersionsImpl(String mcVersion); protected abstract List<InstallerVersion> getVersionsImpl(String mcVersion);
@@ -56,6 +57,7 @@ public abstract class InstallerVersionList implements Consumer<String[]> {
* Get installers you want, please cache this method's result to save time. * Get installers you want, please cache this method's result to save time.
* *
* @param mcVersion the installers to this Minecraft version. * @param mcVersion the installers to this Minecraft version.
*
* @return a copy of the cached data to prevent * @return a copy of the cached data to prevent
* ConcurrentModificationException. * ConcurrentModificationException.
*/ */

View File

@@ -76,9 +76,11 @@ public class ForgeInstaller extends Task {
FileUtils.copyFile(new File(from, profile.install.minecraft + ".jar"), FileUtils.copyFile(new File(from, profile.install.minecraft + ".jar"),
new File(to, profile.install.target + ".jar")); new File(to, profile.install.target + ".jar"));
HMCLog.log("Creating new version profile..." + profile.install.target + ".json"); HMCLog.log("Creating new version profile..." + profile.install.target + ".json");
/*for (MinecraftLibrary library : profile.versionInfo.libraries) /*
if (library.name.startsWith("net.minecraftforge:forge:")) * for (MinecraftLibrary library : profile.versionInfo.libraries)
library.url = installerVersion.universal;*/ * if (library.name.startsWith("net.minecraftforge:forge:"))
* library.url = installerVersion.universal;
*/
FileUtils.write(new File(to, profile.install.target + ".json"), C.gsonPrettyPrinting.toJson(profile.versionInfo)); FileUtils.write(new File(to, profile.install.target + ".json"), C.gsonPrettyPrinting.toJson(profile.versionInfo));
HMCLog.log("Extracting universal forge pack..." + profile.install.filePath); HMCLog.log("Extracting universal forge pack..." + profile.install.filePath);

View File

@@ -35,6 +35,7 @@ public abstract class IUpgrader implements Event<VersionNumber> {
* *
* @param nowVersion now launcher version * @param nowVersion now launcher version
* @param args Application CommandLine Arguments * @param args Application CommandLine Arguments
*
* @return true if it is needed to break the main thread to shutdown this * @return true if it is needed to break the main thread to shutdown this
* application. * application.
*/ */
@@ -45,6 +46,7 @@ public abstract class IUpgrader implements Event<VersionNumber> {
* *
* @param sender Should be VersionChecker * @param sender Should be VersionChecker
* @param number the newest version * @param number the newest version
*
* @return should return true * @return should return true
*/ */
@Override @Override

View File

@@ -40,6 +40,7 @@ import rx.Observable;
* @author huangyuhui * @author huangyuhui
*/ */
public class MinecraftDownloadService extends IMinecraftDownloadService { public class MinecraftDownloadService extends IMinecraftDownloadService {
MinecraftVersionManager mgr; MinecraftVersionManager mgr;
public MinecraftDownloadService(Profile p, MinecraftVersionManager mgr) { public MinecraftDownloadService(Profile p, MinecraftVersionManager mgr) {

View File

@@ -42,10 +42,9 @@ public class GameDownloadPanel extends AnimatedPanel implements Selectable {
} }
/** /**
* This method is called from within the constructor to * This method is called from within the constructor to initialize the form.
* initialize the form. * WARNING: Do NOT modify this code. The content of this method is always
* WARNING: Do NOT modify this code. The content of this method is * regenerated by the Form Editor.
* always regenerated by the Form Editor.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents

View File

@@ -39,10 +39,9 @@ public class ServerListCellRenderer extends javax.swing.JPanel implements ListCe
} }
/** /**
* This method is called from within the constructor to * This method is called from within the constructor to initialize the form.
* initialize the form. * WARNING: Do NOT modify this code. The content of this method is always
* WARNING: Do NOT modify this code. The content of this method is * regenerated by the Form Editor.
* always regenerated by the Form Editor.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents

View File

@@ -57,10 +57,9 @@ public class ServerListView extends javax.swing.JDialog {
public static final int FAILED_TO_SELECT = -1; public static final int FAILED_TO_SELECT = -1;
/** /**
* This method is called from within the constructor to * This method is called from within the constructor to initialize the form.
* initialize the form. * WARNING: Do NOT modify this code. The content of this method is always
* WARNING: Do NOT modify this code. The content of this method is * regenerated by the Form Editor.
* always regenerated by the Form Editor.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents

View File

@@ -32,7 +32,8 @@ public class DoubleTask extends Task {
@Override @Override
public void executeTask() throws Throwable { public void executeTask() throws Throwable {
a.executeTask(); b.executeTask(); a.executeTask();
b.executeTask();
} }
@Override @Override

View File

@@ -38,7 +38,8 @@ public class TaskObservable<T> extends TaskInfo {
@Override @Override
public void executeTask() { public void executeTask() {
r.subscribe(t->{}); r.subscribe(t -> {
});
} }
} }

View File

@@ -113,15 +113,13 @@ public class FileDownloadTask extends Task implements PreviousResult<File>, Prev
connection.connect(); connection.connect();
// Make sure response code is in the 200 range. // Make sure response code is in the 200 range.
if (connection.getResponseCode() / 100 != 2) { if (connection.getResponseCode() / 100 != 2)
throw new NetException(C.i18n("download.not_200") + " " + connection.getResponseCode()); throw new NetException(C.i18n("download.not_200") + " " + connection.getResponseCode());
}
// Check for valid content length. // Check for valid content length.
int contentLength = connection.getContentLength(); int contentLength = connection.getContentLength();
if (contentLength < 1) { if (contentLength < 1)
throw new NetException("The content length is invalid."); throw new NetException("The content length is invalid.");
}
filePath.getParentFile().mkdirs(); filePath.getParentFile().mkdirs();
@@ -179,7 +177,8 @@ public class FileDownloadTask extends Task implements PreviousResult<File>, Prev
closeFiles(); closeFiles();
} }
} }
if (failReason != null) throw failReason; if (failReason != null)
throw failReason;
} }
public static void download(String url, String file, DownloadListener dl) throws Throwable { public static void download(String url, String file, DownloadListener dl) throws Throwable {

View File

@@ -76,7 +76,8 @@ public class HTTPGetTask extends TaskInfo implements PreviousResult<String> {
t = new NetException("Failed to get " + url, ex); t = new NetException("Failed to get " + url, ex);
} }
} }
if (t != null) throw t; if (t != null)
throw t;
} }
@Override @Override

View File

@@ -32,22 +32,29 @@ public interface IUpdateChecker {
void checkOutdate(); void checkOutdate();
/** /**
* Get the <b>cached</b> newest version number, use "process" method to download! * Get the <b>cached</b> newest version number, use "process" method to
* download!
*
* @return the newest version number * @return the newest version number
*
* @see process * @see process
*/ */
VersionNumber getNewVersion(); VersionNumber getNewVersion();
/** /**
* Download the version number synchronously. * Download the version number synchronously. When you execute this method
* When you execute this method first, should leave "showMessage" false. * first, should leave "showMessage" false.
* @param showMessage If it is requested to warn the user that there is a new version. *
* @param showMessage If it is requested to warn the user that there is a
* new version.
*
* @return the process observable. * @return the process observable.
*/ */
Observable process(boolean showMessage); Observable process(boolean showMessage);
/** /**
* Get the download links. * Get the download links.
*
* @return a JSON, which contains the server response. * @return a JSON, which contains the server response.
*/ */
Observable<Map<String, String>> requestDownloadLink(); Observable<Map<String, String>> requestDownloadLink();

View File

@@ -53,7 +53,8 @@ public final class NetUtils {
public static String getStreamContent(InputStream is, String encoding) public static String getStreamContent(InputStream is, String encoding)
throws IOException { throws IOException {
if (is == null) return null; if (is == null)
return null;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
try (InputStreamReader br = new InputStreamReader(is, encoding)) { try (InputStreamReader br = new InputStreamReader(is, encoding)) {
int len; int len;

View File

@@ -54,10 +54,14 @@ public final class StrUtils {
cmdbuf.append("\\"); cmdbuf.append("\\");
cmdbuf.append('"'); cmdbuf.append('"');
} else if (s.endsWith("\"")) } else if (s.endsWith("\""))
/* The argument has already been quoted. */ /*
* The argument has already been quoted.
*/
cmdbuf.append(s); cmdbuf.append(s);
else else
/* Unmatched quote for the argument. */ /*
* Unmatched quote for the argument.
*/
throw new IllegalArgumentException(); throw new IllegalArgumentException();
else else
cmdbuf.append(s); cmdbuf.append(s);

View File

@@ -72,9 +72,9 @@ public class Java {
} }
/* /*
----------------------------------- * -----------------------------------
MAC OS X * MAC OS X
----------------------------------- * -----------------------------------
*/ */
public static List<Java> queryAllJDKInMac() { public static List<Java> queryAllJDKInMac() {
List<Java> ans = new ArrayList<>(); List<Java> ans = new ArrayList<>();
@@ -88,9 +88,9 @@ public class Java {
} }
/* /*
----------------------------------- * -----------------------------------
WINDOWS * WINDOWS
----------------------------------- * -----------------------------------
*/ */
public static List<Java> queryAllJavaHomeInWindowsByReg() { public static List<Java> queryAllJavaHomeInWindowsByReg() {
List<Java> ans = new ArrayList<>(); List<Java> ans = new ArrayList<>();

View File

@@ -33,31 +33,32 @@ public class LogWindowOutputStream extends OutputStream {
private final LogWindow txt; private final LogWindow txt;
private final Level sas; private final Level sas;
/* /*
private final CacheTask t = new CacheTask(); * private final CacheTask t = new CacheTask();
private class CacheTask extends TimerTask { * private class CacheTask extends TimerTask {
private final Object lock = new Object(); * private final Object lock = new Object();
private StringBuilder cachedString = new StringBuilder(); * private StringBuilder cachedString = new StringBuilder();
*
@Override * @Override
public void run() { * public void run() {
synchronized(lock) { * synchronized(lock) {
SwingUtilities.invokeLater(() -> { * SwingUtilities.invokeLater(() -> {
String t = txt.getText() + cachedString.toString().replace("\t", " "); * String t = txt.getText() + cachedString.toString().replace("\t", " ");
txt.setText(t); * txt.setText(t);
txt.setCaretPosition(t.length()); * txt.setCaretPosition(t.length());
cachedString = new StringBuilder(); * cachedString = new StringBuilder();
}); * });
} * }
} * }
*
void cache(String s) { * void cache(String s) {
synchronized(lock) { * synchronized(lock) {
cachedString.append(s); * cachedString.append(s);
} * }
} * }
}*/ * }
*/
public LogWindowOutputStream(LogWindow logWindow, Level l) { public LogWindowOutputStream(LogWindow logWindow, Level l) {
txt = logWindow; txt = logWindow;
this.sas = l; this.sas = l;

View File

@@ -21,14 +21,17 @@ abstract class BCJCoder implements FilterCoder {
return filterID >= 0x04 && filterID <= 0x09; return filterID >= 0x04 && filterID <= 0x09;
} }
@Override
public boolean changesSize() { public boolean changesSize() {
return false; return false;
} }
@Override
public boolean nonLastOK() { public boolean nonLastOK() {
return true; return true;
} }
@Override
public boolean lastOK() { public boolean lastOK() {
return false; return false;
} }

View File

@@ -21,23 +21,28 @@ class BCJDecoder extends BCJCoder implements FilterDecoder {
assert isBCJFilterID(filterID); assert isBCJFilterID(filterID);
this.filterID = filterID; this.filterID = filterID;
if (props.length == 0) switch (props.length) {
case 0:
startOffset = 0; startOffset = 0;
else if (props.length == 4) { break;
case 4:
int n = 0; int n = 0;
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
n |= (props[i] & 0xFF) << (i * 8); n |= (props[i] & 0xFF) << (i * 8);
startOffset = n; startOffset = n;
} else break;
default:
throw new UnsupportedOptionsException( throw new UnsupportedOptionsException(
"Unsupported BCJ filter properties"); "Unsupported BCJ filter properties");
} }
}
@Override
public int getMemoryUsage() { public int getMemoryUsage() {
return SimpleInputStream.getMemoryUsage(); return SimpleInputStream.getMemoryUsage();
} }
@Override
public InputStream getInputStream(InputStream in) { public InputStream getInputStream(InputStream in) {
SimpleFilter simpleFilter = null; SimpleFilter simpleFilter = null;

View File

@@ -18,9 +18,9 @@ abstract class BCJOptions extends FilterOptions {
} }
/** /**
* Sets the start offset for the address conversions. * Sets the start offset for the address conversions. Normally this is
* Normally this is useless so you shouldn't use this function. * useless so you shouldn't use this function. The default value is
* The default value is <code>0</code>. * <code>0</code>.
*/ */
public void setStartOffset(int startOffset) public void setStartOffset(int startOffset)
throws UnsupportedOptionsException { throws UnsupportedOptionsException {

View File

@@ -9,25 +9,25 @@
package org.tukaani.xz; package org.tukaani.xz;
/** /**
* Thrown when the compressed input data is corrupt. * Thrown when the compressed input data is corrupt. However, it is possible
* However, it is possible that some or all of the data * that some or all of the data already read from the input stream was corrupt
* already read from the input stream was corrupt too. * too.
*/ */
public class CorruptedInputException extends XZIOException { public class CorruptedInputException extends XZIOException {
private static final long serialVersionUID = 3L; private static final long serialVersionUID = 3L;
/** /**
* Creates a new CorruptedInputException with * Creates a new CorruptedInputException with the default error detail
* the default error detail message. * message.
*/ */
public CorruptedInputException() { public CorruptedInputException() {
super("Compressed data is corrupt"); super("Compressed data is corrupt");
} }
/** /**
* Creates a new CorruptedInputException with * Creates a new CorruptedInputException with the specified error detail
* the specified error detail message. * message.
* *
* @param s error detail message * @param s error detail message
*/ */

View File

@@ -22,10 +22,12 @@ class DeltaDecoder extends DeltaCoder implements FilterDecoder {
distance = (props[0] & 0xFF) + 1; distance = (props[0] & 0xFF) + 1;
} }
@Override
public int getMemoryUsage() { public int getMemoryUsage() {
return 1; return 1;
} }
@Override
public InputStream getInputStream(InputStream in) { public InputStream getInputStream(InputStream in) {
return new DeltaInputStream(in, distance); return new DeltaInputStream(in, distance);
} }

View File

@@ -15,9 +15,9 @@ import org.tukaani.xz.delta.DeltaDecoder;
/** /**
* Decodes raw Delta-filtered data (no XZ headers). * Decodes raw Delta-filtered data (no XZ headers).
* <p> * <p>
* The delta filter doesn't change the size of the data and thus it * The delta filter doesn't change the size of the data and thus it cannot have
* cannot have an end-of-payload marker. It will simply decode until * an end-of-payload marker. It will simply decode until its input stream
* its input stream indicates end of input. * indicates end of input.
*/ */
public class DeltaInputStream extends InputStream { public class DeltaInputStream extends InputStream {
@@ -41,12 +41,10 @@ public class DeltaInputStream extends InputStream {
/** /**
* Creates a new Delta decoder with the given delta calculation distance. * Creates a new Delta decoder with the given delta calculation distance.
* *
* @param in input stream from which Delta filtered data * @param in input stream from which Delta filtered data is read
* is read
* *
* @param distance delta calculation distance, must be in the * @param distance delta calculation distance, must be in the range
* range [<code>DISTANCE_MIN</code>, * [<code>DISTANCE_MIN</code>, <code>DISTANCE_MAX</code>]
* <code>DISTANCE_MAX</code>]
*/ */
public DeltaInputStream(InputStream in, int distance) { public DeltaInputStream(InputStream in, int distance) {
// Check for null because otherwise null isn't detect // Check for null because otherwise null isn't detect
@@ -61,11 +59,12 @@ public class DeltaInputStream extends InputStream {
/** /**
* Decode the next byte from this input stream. * Decode the next byte from this input stream.
* *
* @return the next decoded byte, or <code>-1</code> to indicate * @return the next decoded byte, or <code>-1</code> to indicate the end of
* the end of input on the input stream <code>in</code> * input on the input stream <code>in</code>
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@Override
public int read() throws IOException { public int read() throws IOException {
return read(tempBuf, 0, 1) == -1 ? -1 : (tempBuf[0] & 0xFF); return read(tempBuf, 0, 1) == -1 ? -1 : (tempBuf[0] & 0xFF);
} }
@@ -73,21 +72,22 @@ public class DeltaInputStream extends InputStream {
/** /**
* Decode into an array of bytes. * Decode into an array of bytes.
* <p> * <p>
* This calls <code>in.read(buf, off, len)</code> and defilters the * This calls <code>in.read(buf, off, len)</code> and defilters the returned
* returned data. * data.
* *
* @param buf target buffer for decoded data * @param buf target buffer for decoded data
* @param off start offset in <code>buf</code> * @param off start offset in <code>buf</code>
* @param len maximum number of bytes to read * @param len maximum number of bytes to read
* *
* @return number of bytes read, or <code>-1</code> to indicate * @return number of bytes read, or <code>-1</code> to indicate the end of
* the end of the input stream <code>in</code> * the input stream <code>in</code>
* *
* @throws XZIOException if the stream has been closed * @throws XZIOException if the stream has been closed
* *
* @throws IOException may be thrown by underlaying input * @throws IOException may be thrown by underlaying input stream
* stream <code>in</code> * <code>in</code>
*/ */
@Override
public int read(byte[] buf, int off, int len) throws IOException { public int read(byte[] buf, int off, int len) throws IOException {
if (len == 0) if (len == 0)
return 0; return 0;
@@ -118,6 +118,7 @@ public class DeltaInputStream extends InputStream {
* *
* @return the value returned by <code>in.available()</code> * @return the value returned by <code>in.available()</code>
*/ */
@Override
public int available() throws IOException { public int available() throws IOException {
if (in == null) if (in == null)
throw new XZIOException("Stream closed"); throw new XZIOException("Stream closed");
@@ -129,11 +130,12 @@ public class DeltaInputStream extends InputStream {
} }
/** /**
* Closes the stream and calls <code>in.close()</code>. * Closes the stream and calls <code>in.close()</code>. If the stream was
* If the stream was already closed, this does nothing. * already closed, this does nothing.
* *
* @throws IOException if thrown by <code>in.close()</code> * @throws IOException if thrown by <code>in.close()</code>
*/ */
@Override
public void close() throws IOException { public void close() throws IOException {
if (in != null) if (in != null)
try { try {

View File

@@ -11,19 +11,19 @@ package org.tukaani.xz;
import java.io.InputStream; import java.io.InputStream;
/** /**
* Delta filter options. The Delta filter can be used only as a non-last * Delta filter options. The Delta filter can be used only as a non-last filter
* filter in the chain, for example Delta + LZMA2. * in the chain, for example Delta + LZMA2.
* <p> * <p>
* Currently only simple byte-wise delta is supported. The only option * Currently only simple byte-wise delta is supported. The only option is the
* is the delta distance, which you should set to match your data. * delta distance, which you should set to match your data. It's not possible to
* It's not possible to provide a generic default value for it. * provide a generic default value for it.
* <p> * <p>
* For example, with distance = 2 and eight-byte input * For example, with distance = 2 and eight-byte input A1 B1 A2 B3 A3 B5 A4 B7,
* A1 B1 A2 B3 A3 B5 A4 B7, the output will be A1 B1 01 02 01 02 01 02. * the output will be A1 B1 01 02 01 02 01 02.
* <p> * <p>
* The Delta filter can be good with uncompressed bitmap images. It can * The Delta filter can be good with uncompressed bitmap images. It can also
* also help with PCM audio, although special-purpose compressors like * help with PCM audio, although special-purpose compressors like FLAC will give
* FLAC will give much smaller result at much better compression speed. * much smaller result at much better compression speed.
*/ */
public class DeltaOptions extends FilterOptions { public class DeltaOptions extends FilterOptions {
@@ -53,8 +53,8 @@ public class DeltaOptions extends FilterOptions {
} }
/** /**
* Sets the delta distance in bytes. The new distance must be in * Sets the delta distance in bytes. The new distance must be in the range
* the range [DISTANCE_MIN, DISTANCE_MAX]. * [DISTANCE_MIN, DISTANCE_MAX].
*/ */
public void setDistance(int distance) throws UnsupportedOptionsException { public void setDistance(int distance) throws UnsupportedOptionsException {
if (distance < DISTANCE_MIN || distance > DISTANCE_MAX) if (distance < DISTANCE_MIN || distance > DISTANCE_MAX)
@@ -72,26 +72,32 @@ public class DeltaOptions extends FilterOptions {
return distance; return distance;
} }
@Override
public int getEncoderMemoryUsage() { public int getEncoderMemoryUsage() {
return DeltaOutputStream.getMemoryUsage(); return DeltaOutputStream.getMemoryUsage();
} }
@Override
public FinishableOutputStream getOutputStream(FinishableOutputStream out) { public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
return new DeltaOutputStream(out, this); return new DeltaOutputStream(out, this);
} }
@Override
public int getDecoderMemoryUsage() { public int getDecoderMemoryUsage() {
return 1; return 1;
} }
@Override
public InputStream getInputStream(InputStream in) { public InputStream getInputStream(InputStream in) {
return new DeltaInputStream(in, distance); return new DeltaInputStream(in, distance);
} }
@Override
FilterEncoder getFilterEncoder() { FilterEncoder getFilterEncoder() {
return new DeltaEncoder(this); return new DeltaEncoder(this);
} }
@Override
public Object clone() { public Object clone() {
try { try {
return super.clone(); return super.clone();

View File

@@ -18,10 +18,9 @@ import java.io.IOException;
public abstract class FilterOptions implements Cloneable { public abstract class FilterOptions implements Cloneable {
/** /**
* Gets how much memory the encoder will need with * Gets how much memory the encoder will need with the given filter chain.
* the given filter chain. This function simply calls * This function simply calls <code>getEncoderMemoryUsage()</code> for every
* <code>getEncoderMemoryUsage()</code> for every filter * filter in the array and returns the sum of the returned values.
* in the array and returns the sum of the returned values.
*/ */
public static int getEncoderMemoryUsage(FilterOptions[] options) { public static int getEncoderMemoryUsage(FilterOptions[] options) {
int m = 0; int m = 0;
@@ -33,10 +32,9 @@ public abstract class FilterOptions implements Cloneable {
} }
/** /**
* Gets how much memory the decoder will need with * Gets how much memory the decoder will need with the given filter chain.
* the given filter chain. This function simply calls * This function simply calls <code>getDecoderMemoryUsage()</code> for every
* <code>getDecoderMemoryUsage()</code> for every filter * filter in the array and returns the sum of the returned values.
* in the array and returns the sum of the returned values.
*/ */
public static int getDecoderMemoryUsage(FilterOptions[] options) { public static int getDecoderMemoryUsage(FilterOptions[] options) {
int m = 0; int m = 0;
@@ -53,18 +51,18 @@ public abstract class FilterOptions implements Cloneable {
public abstract int getEncoderMemoryUsage(); public abstract int getEncoderMemoryUsage();
/** /**
* Gets a raw (no XZ headers) encoder output stream using these options. * Gets a raw (no XZ headers) encoder output stream using these options. Raw
* Raw streams are an advanced feature. In most cases you want to store * streams are an advanced feature. In most cases you want to store the
* the compressed data in the .xz container format instead of using * compressed data in the .xz container format instead of using a raw
* a raw stream. To use this filter in a .xz file, pass this object * stream. To use this filter in a .xz file, pass this object to
* to XZOutputStream. * XZOutputStream.
*/ */
public abstract FinishableOutputStream getOutputStream( public abstract FinishableOutputStream getOutputStream(
FinishableOutputStream out); FinishableOutputStream out);
/** /**
* Gets how much memory the decoder will need to decompress the data * Gets how much memory the decoder will need to decompress the data that
* that was encoded with these options. * was encoded with these options.
*/ */
public abstract int getDecoderMemoryUsage(); public abstract int getDecoderMemoryUsage();

View File

@@ -35,6 +35,7 @@ public class FinishableWrapperOutputStream extends FinishableOutputStream {
/** /**
* Calls {@link java.io.OutputStream#write(int) out.write(b)}. * Calls {@link java.io.OutputStream#write(int) out.write(b)}.
*/ */
@Override
public void write(int b) throws IOException { public void write(int b) throws IOException {
out.write(b); out.write(b);
} }
@@ -42,6 +43,7 @@ public class FinishableWrapperOutputStream extends FinishableOutputStream {
/** /**
* Calls {@link java.io.OutputStream#write(byte[]) out.write(buf)}. * Calls {@link java.io.OutputStream#write(byte[]) out.write(buf)}.
*/ */
@Override
public void write(byte[] buf) throws IOException { public void write(byte[] buf) throws IOException {
out.write(buf); out.write(buf);
} }
@@ -50,6 +52,7 @@ public class FinishableWrapperOutputStream extends FinishableOutputStream {
* Calls {@link java.io.OutputStream#write(byte[],int,int) * Calls {@link java.io.OutputStream#write(byte[],int,int)
* out.write(buf, off, len)}. * out.write(buf, off, len)}.
*/ */
@Override
public void write(byte[] buf, int off, int len) throws IOException { public void write(byte[] buf, int off, int len) throws IOException {
out.write(buf, off, len); out.write(buf, off, len);
} }
@@ -57,6 +60,7 @@ public class FinishableWrapperOutputStream extends FinishableOutputStream {
/** /**
* Calls {@link java.io.OutputStream#flush() out.flush()}. * Calls {@link java.io.OutputStream#flush() out.flush()}.
*/ */
@Override
public void flush() throws IOException { public void flush() throws IOException {
out.flush(); out.flush();
} }
@@ -64,6 +68,7 @@ public class FinishableWrapperOutputStream extends FinishableOutputStream {
/** /**
* Calls {@link java.io.OutputStream#close() out.close()}. * Calls {@link java.io.OutputStream#close() out.close()}.
*/ */
@Override
public void close() throws IOException { public void close() throws IOException {
out.close(); out.close();
} }

View File

@@ -25,10 +25,12 @@ class LZMA2Decoder extends LZMA2Coder implements FilterDecoder {
dictSize <<= (props[0] >>> 1) + 11; dictSize <<= (props[0] >>> 1) + 11;
} }
@Override
public int getMemoryUsage() { public int getMemoryUsage() {
return LZMA2InputStream.getMemoryUsage(dictSize); return LZMA2InputStream.getMemoryUsage(dictSize);
} }
@Override
public InputStream getInputStream(InputStream in) { public InputStream getInputStream(InputStream in) {
return new LZMA2InputStream(in, dictSize); return new LZMA2InputStream(in, dictSize);
} }

View File

@@ -32,18 +32,22 @@ class LZMA2Encoder extends LZMA2Coder implements FilterEncoder {
this.options = (LZMA2Options) options.clone(); this.options = (LZMA2Options) options.clone();
} }
@Override
public long getFilterID() { public long getFilterID() {
return FILTER_ID; return FILTER_ID;
} }
@Override
public byte[] getFilterProps() { public byte[] getFilterProps() {
return props; return props;
} }
@Override
public boolean supportsFlushing() { public boolean supportsFlushing() {
return true; return true;
} }
@Override
public FinishableOutputStream getOutputStream(FinishableOutputStream out) { public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
return options.getOutputStream(out); return options.getOutputStream(out);
} }

View File

@@ -24,8 +24,8 @@ public class LZMA2InputStream extends InputStream {
/** /**
* Smallest valid LZMA2 dictionary size. * Smallest valid LZMA2 dictionary size.
* <p> * <p>
* Very tiny dictionaries would be a performance problem, so * Very tiny dictionaries would be a performance problem, so the minimum is
* the minimum is 4 KiB. * 4 KiB.
*/ */
public static final int DICT_SIZE_MIN = 4096; public static final int DICT_SIZE_MIN = 4096;
@@ -33,11 +33,11 @@ public class LZMA2InputStream extends InputStream {
* Largest dictionary size supported by this implementation. * Largest dictionary size supported by this implementation.
* <p> * <p>
* The LZMA2 algorithm allows dictionaries up to one byte less than 4 GiB. * The LZMA2 algorithm allows dictionaries up to one byte less than 4 GiB.
* This implementation supports only 16 bytes less than 2 GiB for raw * This implementation supports only 16 bytes less than 2 GiB for raw LZMA2
* LZMA2 streams, and for .xz files the maximum is 1.5 GiB. This * streams, and for .xz files the maximum is 1.5 GiB. This limitation is due
* limitation is due to Java using signed 32-bit integers for array * to Java using signed 32-bit integers for array indexing. The limitation
* indexing. The limitation shouldn't matter much in practice since so * shouldn't matter much in practice since so huge dictionaries are not
* huge dictionaries are not normally used. * normally used.
*/ */
public static final int DICT_SIZE_MAX = Integer.MAX_VALUE & ~15; public static final int DICT_SIZE_MAX = Integer.MAX_VALUE & ~15;
@@ -62,12 +62,11 @@ public class LZMA2InputStream extends InputStream {
private final byte[] tempBuf = new byte[1]; private final byte[] tempBuf = new byte[1];
/** /**
* Gets approximate decompressor memory requirements as kibibytes for * Gets approximate decompressor memory requirements as kibibytes for the
* the given dictionary size. * given dictionary size.
* *
* @param dictSize LZMA2 dictionary size as bytes, must be * @param dictSize LZMA2 dictionary size as bytes, must be in the range
* in the range [<code>DICT_SIZE_MIN</code>, * [<code>DICT_SIZE_MIN</code>, <code>DICT_SIZE_MAX</code>]
* <code>DICT_SIZE_MAX</code>]
* *
* @return approximate memory requirements as kibibytes (KiB) * @return approximate memory requirements as kibibytes (KiB)
*/ */
@@ -91,27 +90,25 @@ public class LZMA2InputStream extends InputStream {
} }
/** /**
* Creates a new input stream that decompresses raw LZMA2 data * Creates a new input stream that decompresses raw LZMA2 data from
* from <code>in</code>. * <code>in</code>.
* <p> * <p>
* The caller needs to know the dictionary size used when compressing; * The caller needs to know the dictionary size used when compressing; the
* the dictionary size isn't stored as part of a raw LZMA2 stream. * dictionary size isn't stored as part of a raw LZMA2 stream.
* <p> * <p>
* Specifying a too small dictionary size will prevent decompressing * Specifying a too small dictionary size will prevent decompressing the
* the stream. Specifying a too big dictionary is waste of memory but * stream. Specifying a too big dictionary is waste of memory but
* decompression will work. * decompression will work.
* <p> * <p>
* There is no need to specify a dictionary bigger than * There is no need to specify a dictionary bigger than the uncompressed
* the uncompressed size of the data even if a bigger dictionary * size of the data even if a bigger dictionary was used when compressing.
* was used when compressing. If you know the uncompressed size * If you know the uncompressed size of the data, this might allow saving
* of the data, this might allow saving some memory. * some memory.
* *
* @param in input stream from which LZMA2-compressed * @param in input stream from which LZMA2-compressed data is read
* data is read
* *
* @param dictSize LZMA2 dictionary size as bytes, must be * @param dictSize LZMA2 dictionary size as bytes, must be in the range
* in the range [<code>DICT_SIZE_MIN</code>, * [<code>DICT_SIZE_MIN</code>, <code>DICT_SIZE_MAX</code>]
* <code>DICT_SIZE_MAX</code>]
*/ */
public LZMA2InputStream(InputStream in, int dictSize) { public LZMA2InputStream(InputStream in, int dictSize) {
this(in, dictSize, null); this(in, dictSize, null);
@@ -120,20 +117,18 @@ public class LZMA2InputStream extends InputStream {
/** /**
* Creates a new LZMA2 decompressor using a preset dictionary. * Creates a new LZMA2 decompressor using a preset dictionary.
* <p> * <p>
* This is like <code>LZMA2InputStream(InputStream, int)</code> except * This is like <code>LZMA2InputStream(InputStream, int)</code> except that
* that the dictionary may be initialized using a preset dictionary. * the dictionary may be initialized using a preset dictionary. If a preset
* If a preset dictionary was used when compressing the data, the * dictionary was used when compressing the data, the same preset dictionary
* same preset dictionary must be provided when decompressing. * must be provided when decompressing.
* *
* @param in input stream from which LZMA2-compressed * @param in input stream from which LZMA2-compressed data is read
* data is read
* *
* @param dictSize LZMA2 dictionary size as bytes, must be * @param dictSize LZMA2 dictionary size as bytes, must be in the range
* in the range [<code>DICT_SIZE_MIN</code>, * [<code>DICT_SIZE_MIN</code>, <code>DICT_SIZE_MAX</code>]
* <code>DICT_SIZE_MAX</code>]
* *
* @param presetDict preset dictionary or <code>null</code> * @param presetDict preset dictionary or <code>null</code> to use no preset
* to use no preset dictionary * dictionary
*/ */
public LZMA2InputStream(InputStream in, int dictSize, byte[] presetDict) { public LZMA2InputStream(InputStream in, int dictSize, byte[] presetDict) {
// Check for null because otherwise null isn't detect // Check for null because otherwise null isn't detect
@@ -151,19 +146,18 @@ public class LZMA2InputStream extends InputStream {
/** /**
* Decompresses the next byte from this input stream. * Decompresses the next byte from this input stream.
* <p> * <p>
* Reading lots of data with <code>read()</code> from this input stream * Reading lots of data with <code>read()</code> from this input stream may
* may be inefficient. Wrap it in <code>java.io.BufferedInputStream</code> * be inefficient. Wrap it in <code>java.io.BufferedInputStream</code> if
* if you need to read lots of data one byte at a time. * you need to read lots of data one byte at a time.
* *
* @return the next decompressed byte, or <code>-1</code> * @return the next decompressed byte, or <code>-1</code> to indicate the
* to indicate the end of the compressed stream * end of the compressed stream
* *
* @throws CorruptedInputException * @throws CorruptedInputException
* *
* @throws XZIOException if the stream has been closed * @throws XZIOException if the stream has been closed
* *
* @throws EOFException * @throws EOFException compressed input is truncated or corrupt
* compressed input is truncated or corrupt
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -174,24 +168,23 @@ public class LZMA2InputStream extends InputStream {
/** /**
* Decompresses into an array of bytes. * Decompresses into an array of bytes.
* <p> * <p>
* If <code>len</code> is zero, no bytes are read and <code>0</code> * If <code>len</code> is zero, no bytes are read and <code>0</code> is
* is returned. Otherwise this will block until <code>len</code> * returned. Otherwise this will block until <code>len</code> bytes have
* bytes have been decompressed, the end of the LZMA2 stream is reached, * been decompressed, the end of the LZMA2 stream is reached, or an
* or an exception is thrown. * exception is thrown.
* *
* @param buf target buffer for uncompressed data * @param buf target buffer for uncompressed data
* @param off start offset in <code>buf</code> * @param off start offset in <code>buf</code>
* @param len maximum number of uncompressed bytes to read * @param len maximum number of uncompressed bytes to read
* *
* @return number of bytes read, or <code>-1</code> to indicate * @return number of bytes read, or <code>-1</code> to indicate the end of
* the end of the compressed stream * the compressed stream
* *
* @throws CorruptedInputException * @throws CorruptedInputException
* *
* @throws XZIOException if the stream has been closed * @throws XZIOException if the stream has been closed
* *
* @throws EOFException * @throws EOFException compressed input is truncated or corrupt
* compressed input is truncated or corrupt
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -311,20 +304,18 @@ public class LZMA2InputStream extends InputStream {
} }
/** /**
* Returns the number of uncompressed bytes that can be read * Returns the number of uncompressed bytes that can be read without
* without blocking. The value is returned with an assumption * blocking. The value is returned with an assumption that the compressed
* that the compressed input data will be valid. If the compressed * input data will be valid. If the compressed data is corrupt,
* data is corrupt, <code>CorruptedInputException</code> may get * <code>CorruptedInputException</code> may get thrown before the number of
* thrown before the number of bytes claimed to be available have * bytes claimed to be available have been read from this input stream.
* been read from this input stream.
* <p> * <p>
* In LZMA2InputStream, the return value will be non-zero when the * In LZMA2InputStream, the return value will be non-zero when the
* decompressor is in the middle of an LZMA2 chunk. The return value * decompressor is in the middle of an LZMA2 chunk. The return value will
* will then be the number of uncompressed bytes remaining from that * then be the number of uncompressed bytes remaining from that chunk.
* chunk.
* *
* @return the number of uncompressed bytes that can be read * @return the number of uncompressed bytes that can be read without
* without blocking * blocking
*/ */
public int available() throws IOException { public int available() throws IOException {
if (in == null) if (in == null)
@@ -337,8 +328,8 @@ public class LZMA2InputStream extends InputStream {
} }
/** /**
* Closes the stream and calls <code>in.close()</code>. * Closes the stream and calls <code>in.close()</code>. If the stream was
* If the stream was already closed, this does nothing. * already closed, this does nothing.
* *
* @throws IOException if thrown by <code>in.close()</code> * @throws IOException if thrown by <code>in.close()</code>
*/ */

View File

@@ -16,9 +16,8 @@ import org.tukaani.xz.lzma.LZMAEncoder;
/** /**
* LZMA2 compression options. * LZMA2 compression options.
* <p> * <p>
* While this allows setting the LZMA2 compression options in detail, * While this allows setting the LZMA2 compression options in detail, often you
* often you only need <code>LZMA2Options()</code> or * only need <code>LZMA2Options()</code> or <code>LZMA2Options(int)</code>.
* <code>LZMA2Options(int)</code>.
*/ */
public class LZMA2Options extends FilterOptions { public class LZMA2Options extends FilterOptions {
@@ -45,13 +44,13 @@ public class LZMA2Options extends FilterOptions {
/** /**
* Maximum dictionary size for compression is 768 MiB. * Maximum dictionary size for compression is 768 MiB.
* <p> * <p>
* The decompressor supports bigger dictionaries, up to almost 2 GiB. * The decompressor supports bigger dictionaries, up to almost 2 GiB. With
* With HC4 the encoder would support dictionaries bigger than 768 MiB. * HC4 the encoder would support dictionaries bigger than 768 MiB. The 768
* The 768 MiB limit comes from the current implementation of BT4 where * MiB limit comes from the current implementation of BT4 where we would
* we would otherwise hit the limits of signed ints in array indexing. * otherwise hit the limits of signed ints in array indexing.
* <p> * <p>
* If you really need bigger dictionary for decompression, * If you really need bigger dictionary for decompression, use
* use {@link LZMA2InputStream} directly. * {@link LZMA2InputStream} directly.
*/ */
public static final int DICT_SIZE_MAX = 768 << 20; public static final int DICT_SIZE_MAX = 768 << 20;
@@ -86,20 +85,20 @@ public class LZMA2Options extends FilterOptions {
public static final int PB_DEFAULT = 2; public static final int PB_DEFAULT = 2;
/** /**
* Compression mode: uncompressed. * Compression mode: uncompressed. The data is wrapped into a LZMA2 stream
* The data is wrapped into a LZMA2 stream without compression. * without compression.
*/ */
public static final int MODE_UNCOMPRESSED = 0; public static final int MODE_UNCOMPRESSED = 0;
/** /**
* Compression mode: fast. * Compression mode: fast. This is usually combined with a hash chain match
* This is usually combined with a hash chain match finder. * finder.
*/ */
public static final int MODE_FAST = LZMAEncoder.MODE_FAST; public static final int MODE_FAST = LZMAEncoder.MODE_FAST;
/** /**
* Compression mode: normal. * Compression mode: normal. This is usually combined with a binary tree
* This is usually combined with a binary tree match finder. * match finder.
*/ */
public static final int MODE_NORMAL = LZMAEncoder.MODE_NORMAL; public static final int MODE_NORMAL = LZMAEncoder.MODE_NORMAL;
@@ -140,8 +139,8 @@ public class LZMA2Options extends FilterOptions {
private int depthLimit; private int depthLimit;
/** /**
* Creates new LZMA2 options and sets them to the default values. * Creates new LZMA2 options and sets them to the default values. This is
* This is equivalent to <code>LZMA2Options(PRESET_DEFAULT)</code>. * equivalent to <code>LZMA2Options(PRESET_DEFAULT)</code>.
*/ */
public LZMA2Options() { public LZMA2Options() {
try { try {
@@ -155,8 +154,7 @@ public class LZMA2Options extends FilterOptions {
/** /**
* Creates new LZMA2 options and sets them to the given preset. * Creates new LZMA2 options and sets them to the given preset.
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException <code>preset</code> is not supported
* <code>preset</code> is not supported
*/ */
public LZMA2Options(int preset) throws UnsupportedOptionsException { public LZMA2Options(int preset) throws UnsupportedOptionsException {
setPreset(preset); setPreset(preset);
@@ -165,8 +163,7 @@ public class LZMA2Options extends FilterOptions {
/** /**
* Creates new LZMA2 options and sets them to the given custom values. * Creates new LZMA2 options and sets them to the given custom values.
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException unsupported options were specified
* unsupported options were specified
*/ */
public LZMA2Options(int dictSize, int lc, int lp, int pb, int mode, public LZMA2Options(int dictSize, int lc, int lp, int pb, int mode,
int niceLen, int mf, int depthLimit) int niceLen, int mf, int depthLimit)
@@ -183,18 +180,17 @@ public class LZMA2Options extends FilterOptions {
/** /**
* Sets the compression options to the given preset. * Sets the compression options to the given preset.
* <p> * <p>
* The presets 0-3 are fast presets with medium compression. * The presets 0-3 are fast presets with medium compression. The presets 4-6
* The presets 4-6 are fairly slow presets with high compression. * are fairly slow presets with high compression. The default preset
* The default preset (<code>PRESET_DEFAULT</code>) is 6. * (<code>PRESET_DEFAULT</code>) is 6.
* <p> * <p>
* The presets 7-9 are like the preset 6 but use bigger dictionaries * The presets 7-9 are like the preset 6 but use bigger dictionaries and
* and have higher compressor and decompressor memory requirements. * have higher compressor and decompressor memory requirements. Unless the
* Unless the uncompressed size of the file exceeds 8&nbsp;MiB, * uncompressed size of the file exceeds 8&nbsp;MiB, 16&nbsp;MiB, or
* 16&nbsp;MiB, or 32&nbsp;MiB, it is waste of memory to use the * 32&nbsp;MiB, it is waste of memory to use the presets 7, 8, or 9,
* presets 7, 8, or 9, respectively. * respectively.
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException <code>preset</code> is not supported
* <code>preset</code> is not supported
*/ */
public void setPreset(int preset) throws UnsupportedOptionsException { public void setPreset(int preset) throws UnsupportedOptionsException {
if (preset < 0 || preset > 9) if (preset < 0 || preset > 9)
@@ -227,12 +223,11 @@ public class LZMA2Options extends FilterOptions {
* However, using a dictioanary bigger than the size of the uncompressed * However, using a dictioanary bigger than the size of the uncompressed
* data is waste of memory. * data is waste of memory.
* <p> * <p>
* Any value in the range [DICT_SIZE_MIN, DICT_SIZE_MAX] is valid, * Any value in the range [DICT_SIZE_MIN, DICT_SIZE_MAX] is valid, but sizes
* but sizes of 2^n and 2^n&nbsp;+&nbsp;2^(n-1) bytes are somewhat * of 2^n and 2^n&nbsp;+&nbsp;2^(n-1) bytes are somewhat recommended.
* recommended.
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException <code>dictSize</code> is not
* <code>dictSize</code> is not supported * supported
*/ */
public void setDictSize(int dictSize) throws UnsupportedOptionsException { public void setDictSize(int dictSize) throws UnsupportedOptionsException {
if (dictSize < DICT_SIZE_MIN) if (dictSize < DICT_SIZE_MIN)
@@ -256,18 +251,18 @@ public class LZMA2Options extends FilterOptions {
} }
/** /**
* Sets a preset dictionary. Use null to disable the use of * Sets a preset dictionary. Use null to disable the use of a preset
* a preset dictionary. By default there is no preset dictionary. * dictionary. By default there is no preset dictionary.
* <p> * <p>
* <b>The .xz format doesn't support a preset dictionary for now. * <b>The .xz format doesn't support a preset dictionary for now. Do not set
* Do not set a preset dictionary unless you use raw LZMA2.</b> * a preset dictionary unless you use raw LZMA2.</b>
* <p> * <p>
* Preset dictionary can be useful when compressing many similar, * Preset dictionary can be useful when compressing many similar, relatively
* relatively small chunks of data independently from each other. * small chunks of data independently from each other. A preset dictionary
* A preset dictionary should contain typical strings that occur in * should contain typical strings that occur in the files being compressed.
* the files being compressed. The most probable strings should be * The most probable strings should be near the end of the preset
* near the end of the preset dictionary. The preset dictionary used * dictionary. The preset dictionary used for compression is also needed for
* for compression is also needed for decompression. * decompression.
*/ */
public void setPresetDict(byte[] presetDict) { public void setPresetDict(byte[] presetDict) {
this.presetDict = presetDict; this.presetDict = presetDict;
@@ -283,12 +278,11 @@ public class LZMA2Options extends FilterOptions {
/** /**
* Sets the number of literal context bits and literal position bits. * Sets the number of literal context bits and literal position bits.
* <p> * <p>
* The sum of <code>lc</code> and <code>lp</code> is limited to 4. * The sum of <code>lc</code> and <code>lp</code> is limited to 4. Trying to
* Trying to exceed it will throw an exception. This function lets * exceed it will throw an exception. This function lets you change both at
* you change both at the same time. * the same time.
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException <code>lc</code> and <code>lp</code>
* <code>lc</code> and <code>lp</code>
* are invalid * are invalid
*/ */
public void setLcLp(int lc, int lp) throws UnsupportedOptionsException { public void setLcLp(int lc, int lp) throws UnsupportedOptionsException {
@@ -305,28 +299,25 @@ public class LZMA2Options extends FilterOptions {
/** /**
* Sets the number of literal context bits. * Sets the number of literal context bits.
* <p> * <p>
* All bytes that cannot be encoded as matches are encoded as literals. * All bytes that cannot be encoded as matches are encoded as literals. That
* That is, literals are simply 8-bit bytes that are encoded one at * is, literals are simply 8-bit bytes that are encoded one at a time.
* a time.
* <p> * <p>
* The literal coding makes an assumption that the highest <code>lc</code> * The literal coding makes an assumption that the highest <code>lc</code>
* bits of the previous uncompressed byte correlate with the next byte. * bits of the previous uncompressed byte correlate with the next byte. For
* For example, in typical English text, an upper-case letter is often * example, in typical English text, an upper-case letter is often followed
* followed by a lower-case letter, and a lower-case letter is usually * by a lower-case letter, and a lower-case letter is usually followed by
* followed by another lower-case letter. In the US-ASCII character set, * another lower-case letter. In the US-ASCII character set, the highest
* the highest three bits are 010 for upper-case letters and 011 for * three bits are 010 for upper-case letters and 011 for lower-case letters.
* lower-case letters. When <code>lc</code> is at least 3, the literal * When <code>lc</code> is at least 3, the literal coding can take advantage
* coding can take advantage of this property in the uncompressed data. * of this property in the uncompressed data.
* <p> * <p>
* The default value (3) is usually good. If you want maximum compression, * The default value (3) is usually good. If you want maximum compression,
* try <code>setLc(4)</code>. Sometimes it helps a little, and sometimes it * try <code>setLc(4)</code>. Sometimes it helps a little, and sometimes it
* makes compression worse. If it makes it worse, test for example * makes compression worse. If it makes it worse, test for example
* <code>setLc(2)</code> too. * <code>setLc(2)</code> too.
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException <code>lc</code> is invalid, or the
* <code>lc</code> is invalid, or the sum * sum of <code>lc</code> and <code>lp</code> exceed LC_LP_MAX
* of <code>lc</code> and <code>lp</code>
* exceed LC_LP_MAX
*/ */
public void setLc(int lc) throws UnsupportedOptionsException { public void setLc(int lc) throws UnsupportedOptionsException {
setLcLp(lc, lp); setLcLp(lc, lp);
@@ -335,14 +326,12 @@ public class LZMA2Options extends FilterOptions {
/** /**
* Sets the number of literal position bits. * Sets the number of literal position bits.
* <p> * <p>
* This affets what kind of alignment in the uncompressed data is * This affets what kind of alignment in the uncompressed data is assumed
* assumed when encoding literals. See {@link #setPb(int) setPb} for * when encoding literals. See {@link #setPb(int) setPb} for more
* more information about alignment. * information about alignment.
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException <code>lp</code> is invalid, or the
* <code>lp</code> is invalid, or the sum * sum of <code>lc</code> and <code>lp</code> exceed LC_LP_MAX
* of <code>lc</code> and <code>lp</code>
* exceed LC_LP_MAX
*/ */
public void setLp(int lp) throws UnsupportedOptionsException { public void setLp(int lp) throws UnsupportedOptionsException {
setLcLp(lc, lp); setLcLp(lc, lp);
@@ -365,26 +354,23 @@ public class LZMA2Options extends FilterOptions {
/** /**
* Sets the number of position bits. * Sets the number of position bits.
* <p> * <p>
* This affects what kind of alignment in the uncompressed data is * This affects what kind of alignment in the uncompressed data is assumed
* assumed in general. The default (2) means four-byte alignment * in general. The default (2) means four-byte alignment (2^<code>pb</code>
* (2^<code>pb</code> = 2^2 = 4), which is often a good choice when * = 2^2 = 4), which is often a good choice when there's no better guess.
* there's no better guess.
* <p> * <p>
* When the alignment is known, setting the number of position bits * When the alignment is known, setting the number of position bits
* accordingly may reduce the file size a little. For example with text * accordingly may reduce the file size a little. For example with text
* files having one-byte alignment (US-ASCII, ISO-8859-*, UTF-8), using * files having one-byte alignment (US-ASCII, ISO-8859-*, UTF-8), using
* <code>setPb(0)</code> can improve compression slightly. For UTF-16 * <code>setPb(0)</code> can improve compression slightly. For UTF-16 text,
* text, <code>setPb(1)</code> is a good choice. If the alignment is * <code>setPb(1)</code> is a good choice. If the alignment is an odd number
* an odd number like 3 bytes, <code>setPb(0)</code> might be the best * like 3 bytes, <code>setPb(0)</code> might be the best choice.
* choice.
* <p> * <p>
* Even though the assumed alignment can be adjusted with * Even though the assumed alignment can be adjusted with <code>setPb</code>
* <code>setPb</code> and <code>setLp</code>, LZMA2 still slightly favors * and <code>setLp</code>, LZMA2 still slightly favors 16-byte alignment. It
* 16-byte alignment. It might be worth taking into account when designing * might be worth taking into account when designing file formats that are
* file formats that are likely to be often compressed with LZMA2. * likely to be often compressed with LZMA2.
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException <code>pb</code> is invalid
* <code>pb</code> is invalid
*/ */
public void setPb(int pb) throws UnsupportedOptionsException { public void setPb(int pb) throws UnsupportedOptionsException {
if (pb < 0 || pb > PB_MAX) if (pb < 0 || pb > PB_MAX)
@@ -404,20 +390,19 @@ public class LZMA2Options extends FilterOptions {
/** /**
* Sets the compression mode. * Sets the compression mode.
* <p> * <p>
* This specifies the method to analyze the data produced by * This specifies the method to analyze the data produced by a match finder.
* a match finder. The default is <code>MODE_FAST</code> for presets * The default is <code>MODE_FAST</code> for presets 0-3 and
* 0-3 and <code>MODE_NORMAL</code> for presets 4-9. * <code>MODE_NORMAL</code> for presets 4-9.
* <p> * <p>
* Usually <code>MODE_FAST</code> is used with Hash Chain match finders * Usually <code>MODE_FAST</code> is used with Hash Chain match finders and
* and <code>MODE_NORMAL</code> with Binary Tree match finders. This is * <code>MODE_NORMAL</code> with Binary Tree match finders. This is also
* also what the presets do. * what the presets do.
* <p> * <p>
* The special mode <code>MODE_UNCOMPRESSED</code> doesn't try to * The special mode <code>MODE_UNCOMPRESSED</code> doesn't try to compress
* compress the data at all (and doesn't use a match finder) and will * the data at all (and doesn't use a match finder) and will simply wrap it
* simply wrap it in uncompressed LZMA2 chunks. * in uncompressed LZMA2 chunks.
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException <code>mode</code> is not supported
* <code>mode</code> is not supported
*/ */
public void setMode(int mode) throws UnsupportedOptionsException { public void setMode(int mode) throws UnsupportedOptionsException {
if (mode < MODE_UNCOMPRESSED || mode > MODE_NORMAL) if (mode < MODE_UNCOMPRESSED || mode > MODE_NORMAL)
@@ -435,14 +420,12 @@ public class LZMA2Options extends FilterOptions {
} }
/** /**
* Sets the nice length of matches. * Sets the nice length of matches. Once a match of at least
* Once a match of at least <code>niceLen</code> bytes is found, * <code>niceLen</code> bytes is found, the algorithm stops looking for
* the algorithm stops looking for better matches. Higher values tend * better matches. Higher values tend to give better compression at the
* to give better compression at the expense of speed. The default * expense of speed. The default depends on the preset.
* depends on the preset.
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException <code>niceLen</code> is invalid
* <code>niceLen</code> is invalid
*/ */
public void setNiceLen(int niceLen) throws UnsupportedOptionsException { public void setNiceLen(int niceLen) throws UnsupportedOptionsException {
if (niceLen < NICE_LEN_MIN) if (niceLen < NICE_LEN_MIN)
@@ -468,13 +451,12 @@ public class LZMA2Options extends FilterOptions {
/** /**
* Sets the match finder type. * Sets the match finder type.
* <p> * <p>
* Match finder has a major effect on compression speed, memory usage, * Match finder has a major effect on compression speed, memory usage, and
* and compression ratio. Usually Hash Chain match finders are faster * compression ratio. Usually Hash Chain match finders are faster than
* than Binary Tree match finders. The default depends on the preset: * Binary Tree match finders. The default depends on the preset: 0-3 use
* 0-3 use <code>MF_HC4</code> and 4-9 use <code>MF_BT4</code>. * <code>MF_HC4</code> and 4-9 use <code>MF_BT4</code>.
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException <code>mf</code> is not supported
* <code>mf</code> is not supported
*/ */
public void setMatchFinder(int mf) throws UnsupportedOptionsException { public void setMatchFinder(int mf) throws UnsupportedOptionsException {
if (mf != MF_HC4 && mf != MF_BT4) if (mf != MF_HC4 && mf != MF_BT4)
@@ -494,18 +476,17 @@ public class LZMA2Options extends FilterOptions {
/** /**
* Sets the match finder search depth limit. * Sets the match finder search depth limit.
* <p> * <p>
* The default is a special value of <code>0</code> which indicates that * The default is a special value of <code>0</code> which indicates that the
* the depth limit should be automatically calculated by the selected * depth limit should be automatically calculated by the selected match
* match finder from the nice length of matches. * finder from the nice length of matches.
* <p> * <p>
* Reasonable depth limit for Hash Chain match finders is 4-100 and * Reasonable depth limit for Hash Chain match finders is 4-100 and 16-1000
* 16-1000 for Binary Tree match finders. Using very high values can * for Binary Tree match finders. Using very high values can make the
* make the compressor extremely slow with some files. Avoid settings * compressor extremely slow with some files. Avoid settings higher than
* higher than 1000 unless you are prepared to interrupt the compression * 1000 unless you are prepared to interrupt the compression in case it is
* in case it is taking far too long. * taking far too long.
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException <code>depthLimit</code> is invalid
* <code>depthLimit</code> is invalid
*/ */
public void setDepthLimit(int depthLimit) public void setDepthLimit(int depthLimit)
throws UnsupportedOptionsException { throws UnsupportedOptionsException {
@@ -540,12 +521,12 @@ public class LZMA2Options extends FilterOptions {
* Gets how much memory the LZMA2 decoder will need to decompress the data * Gets how much memory the LZMA2 decoder will need to decompress the data
* that was encoded with these options and stored in a .xz file. * that was encoded with these options and stored in a .xz file.
* <p> * <p>
* The returned value may bigger than the value returned by a direct call * The returned value may bigger than the value returned by a direct call to
* to {@link LZMA2InputStream#getMemoryUsage(int)} if the dictionary size * {@link LZMA2InputStream#getMemoryUsage(int)} if the dictionary size is
* is not 2^n or 2^n&nbsp;+&nbsp;2^(n-1) bytes. This is because the .xz * not 2^n or 2^n&nbsp;+&nbsp;2^(n-1) bytes. This is because the .xz headers
* headers store the dictionary size in such a format and other values * store the dictionary size in such a format and other values are rounded
* are rounded up to the next such value. Such rounding is harmess except * up to the next such value. Such rounding is harmess except it might waste
* it might waste some memory if an unsual dictionary size is used. * some memory if an unsual dictionary size is used.
* <p> * <p>
* If you use raw LZMA2 streams and unusual dictioanary size, call * If you use raw LZMA2 streams and unusual dictioanary size, call
* {@link LZMA2InputStream#getMemoryUsage} directly to get raw decoder * {@link LZMA2InputStream#getMemoryUsage} directly to get raw decoder

View File

@@ -20,16 +20,16 @@ import org.tukaani.xz.lzma.LZMADecoder;
* Decompresses legacy .lzma files and raw LZMA streams (no .lzma header). * Decompresses legacy .lzma files and raw LZMA streams (no .lzma header).
* <p> * <p>
* <b>IMPORTANT:</b> In contrast to other classes in this package, this class * <b>IMPORTANT:</b> In contrast to other classes in this package, this class
* reads data from its input stream one byte at a time. If the input stream * reads data from its input stream one byte at a time. If the input stream is
* is for example {@link java.io.FileInputStream}, wrapping it into * for example {@link java.io.FileInputStream}, wrapping it into
* {@link java.io.BufferedInputStream} tends to improve performance a lot. * {@link java.io.BufferedInputStream} tends to improve performance a lot. This
* This is not automatically done by this class because there may be use * is not automatically done by this class because there may be use cases where
* cases where it is desired that this class won't read any bytes past * it is desired that this class won't read any bytes past the end of the LZMA
* the end of the LZMA stream. * stream.
* <p> * <p>
* Even when using <code>BufferedInputStream</code>, the performance tends * Even when using <code>BufferedInputStream</code>, the performance tends to be
* to be worse (maybe 10-20&nbsp;% slower) than with {@link LZMA2InputStream} * worse (maybe 10-20&nbsp;% slower) than with {@link LZMA2InputStream} or
* or {@link XZInputStream} (when the .xz file contains LZMA2-compressed data). * {@link XZInputStream} (when the .xz file contains LZMA2-compressed data).
* *
* @since 1.4 * @since 1.4
*/ */
@@ -39,10 +39,10 @@ public class LZMAInputStream extends InputStream {
* Largest dictionary size supported by this implementation. * Largest dictionary size supported by this implementation.
* <p> * <p>
* LZMA allows dictionaries up to one byte less than 4 GiB. This * LZMA allows dictionaries up to one byte less than 4 GiB. This
* implementation supports only 16 bytes less than 2 GiB. This * implementation supports only 16 bytes less than 2 GiB. This limitation is
* limitation is due to Java using signed 32-bit integers for array * due to Java using signed 32-bit integers for array indexing. The
* indexing. The limitation shouldn't matter much in practice since so * limitation shouldn't matter much in practice since so huge dictionaries
* huge dictionaries are not normally used. * are not normally used.
*/ */
public static final int DICT_SIZE_MAX = Integer.MAX_VALUE & ~15; public static final int DICT_SIZE_MAX = Integer.MAX_VALUE & ~15;
@@ -56,33 +56,29 @@ public class LZMAInputStream extends InputStream {
private final byte[] tempBuf = new byte[1]; private final byte[] tempBuf = new byte[1];
/** /**
* Number of uncompressed bytes left to be decompressed, or -1 if * Number of uncompressed bytes left to be decompressed, or -1 if the end
* the end marker is used. * marker is used.
*/ */
private long remainingSize; private long remainingSize;
private IOException exception = null; private IOException exception = null;
/** /**
* Gets approximate decompressor memory requirements as kibibytes for * Gets approximate decompressor memory requirements as kibibytes for the
* the given dictionary size and LZMA properties byte (lc, lp, and pb). * given dictionary size and LZMA properties byte (lc, lp, and pb).
* *
* @param dictSize LZMA dictionary size as bytes, should be * @param dictSize LZMA dictionary size as bytes, should be in the range
* in the range [<code>0</code>, * [<code>0</code>, <code>DICT_SIZE_MAX</code>]
* <code>DICT_SIZE_MAX</code>]
* *
* @param propsByte LZMA properties byte that encodes the values * @param propsByte LZMA properties byte that encodes the values of lc, lp,
* of lc, lp, and pb * and pb
* *
* @return approximate memory requirements as kibibytes (KiB) * @return approximate memory requirements as kibibytes (KiB)
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException if <code>dictSize</code> is outside
* if <code>dictSize</code> is outside * the range [<code>0</code>, <code>DICT_SIZE_MAX</code>]
* the range [<code>0</code>,
* <code>DICT_SIZE_MAX</code>]
* *
* @throws CorruptedInputException * @throws CorruptedInputException if <code>propsByte</code> is invalid
* if <code>propsByte</code> is invalid
*/ */
public static int getMemoryUsage(int dictSize, byte propsByte) public static int getMemoryUsage(int dictSize, byte propsByte)
throws UnsupportedOptionsException, CorruptedInputException { throws UnsupportedOptionsException, CorruptedInputException {
@@ -102,18 +98,17 @@ public class LZMAInputStream extends InputStream {
} }
/** /**
* Gets approximate decompressor memory requirements as kibibytes for * Gets approximate decompressor memory requirements as kibibytes for the
* the given dictionary size, lc, and lp. Note that pb isn't needed. * given dictionary size, lc, and lp. Note that pb isn't needed.
* *
* @param dictSize LZMA dictionary size as bytes, must be * @param dictSize LZMA dictionary size as bytes, must be in the range
* in the range [<code>0</code>, * [<code>0</code>, <code>DICT_SIZE_MAX</code>]
* <code>DICT_SIZE_MAX</code>]
* *
* @param lc number of literal context bits, must be * @param lc number of literal context bits, must be in the range [0,
* in the range [0, 8] * 8]
* *
* @param lp number of literal position bits, must be * @param lp number of literal position bits, must be in the range [0,
* in the range [0, 4] * 4]
* *
* @return approximate memory requirements as kibibytes (KiB) * @return approximate memory requirements as kibibytes (KiB)
*/ */
@@ -156,25 +151,22 @@ public class LZMAInputStream extends InputStream {
} }
/** /**
* Creates a new .lzma file format decompressor without * Creates a new .lzma file format decompressor without a memory usage
* a memory usage limit. * limit.
* *
* @param in input stream from which .lzma data is read; * @param in input stream from which .lzma data is read; it might be a good
* it might be a good idea to wrap it in * idea to wrap it in <code>BufferedInputStream</code>, see the note at the
* <code>BufferedInputStream</code>, see the * top of this page
* note at the top of this page
* *
* @throws CorruptedInputException * @throws CorruptedInputException file is corrupt or perhaps not in the
* file is corrupt or perhaps not in * .lzma format at all
* the .lzma format at all
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException dictionary size or uncompressed size
* dictionary size or uncompressed size is too * is too big for this implementation
* big for this implementation
* *
* @throws EOFException * @throws EOFException file is truncated or perhaps not in
* file is truncated or perhaps not in * the .lzma format
* the .lzma format at all * at all
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -183,32 +175,28 @@ public class LZMAInputStream extends InputStream {
} }
/** /**
* Creates a new .lzma file format decompressor with an optional * Creates a new .lzma file format decompressor with an optional memory
* memory usage limit. * usage limit.
* *
* @param in input stream from which .lzma data is read; * @param in input stream from which .lzma data is read; it might
* it might be a good idea to wrap it in * be a good
* <code>BufferedInputStream</code>, see the * idea to wrap it in <code>BufferedInputStream</code>, see the note at the
* note at the top of this page * top of this page
* *
* @param memoryLimit memory usage limit in kibibytes (KiB) * @param memoryLimit memory usage limit in kibibytes (KiB) or
* or <code>-1</code> to impose no * <code>-1</code> to impose no memory usage limit
* memory usage limit
* *
* @throws CorruptedInputException * @throws CorruptedInputException file is corrupt or perhaps not in the
* file is corrupt or perhaps not in * .lzma format at all
* the .lzma format at all
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException dictionary size or uncompressed size
* dictionary size or uncompressed size is too * is too big for this implementation
* big for this implementation
* *
* @throws MemoryLimitException * @throws MemoryLimitException memory usage limit was exceeded
* memory usage limit was exceeded
* *
* @throws EOFException * @throws EOFException file is truncated or perhaps not in
* file is truncated or perhaps not in * the .lzma format
* the .lzma format at all * at all
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -244,45 +232,43 @@ public class LZMAInputStream extends InputStream {
* Creates a new input stream that decompresses raw LZMA data (no .lzma * Creates a new input stream that decompresses raw LZMA data (no .lzma
* header) from <code>in</code>. * header) from <code>in</code>.
* <p> * <p>
* The caller needs to know if the "end of payload marker (EOPM)" alias * The caller needs to know if the "end of payload marker (EOPM)" alias "end
* "end of stream marker (EOS marker)" alias "end marker" present. * of stream marker (EOS marker)" alias "end marker" present. If the end
* If the end marker isn't used, the caller must know the exact * marker isn't used, the caller must know the exact uncompressed size of
* uncompressed size of the stream. * the stream.
* <p> * <p>
* The caller also needs to provide the LZMA properties byte that encodes * The caller also needs to provide the LZMA properties byte that encodes
* the number of literal context bits (lc), literal position bits (lp), * the number of literal context bits (lc), literal position bits (lp), and
* and position bits (pb). * position bits (pb).
* <p> * <p>
* The dictionary size used when compressing is also needed. Specifying * The dictionary size used when compressing is also needed. Specifying a
* a too small dictionary size will prevent decompressing the stream. * too small dictionary size will prevent decompressing the stream.
* Specifying a too big dictionary is waste of memory but decompression * Specifying a too big dictionary is waste of memory but decompression will
* will work. * work.
* <p> * <p>
* There is no need to specify a dictionary bigger than * There is no need to specify a dictionary bigger than the uncompressed
* the uncompressed size of the data even if a bigger dictionary * size of the data even if a bigger dictionary was used when compressing.
* was used when compressing. If you know the uncompressed size * If you know the uncompressed size of the data, this might allow saving
* of the data, this might allow saving some memory. * some memory.
* *
* @param in input stream from which compressed * @param in input stream from which compressed data is read
* data is read
* *
* @param uncompSize uncompressed size of the LZMA stream or -1 * @param uncompSize uncompressed size of the LZMA stream or -1 if the end
* if the end marker is used in the LZMA stream * marker is used in the LZMA stream
* *
* @param propsByte LZMA properties byte that has the encoded * @param propsByte LZMA properties byte that has the encoded values for
* values for literal context bits (lc), literal * literal context bits (lc), literal position bits (lp), and position bits
* position bits (lp), and position bits (pb) * (pb)
* *
* @param dictSize dictionary size as bytes, must be in the range * @param dictSize dictionary size as bytes, must be in the range
* [<code>0</code>, <code>DICT_SIZE_MAX</code>] * [<code>0</code>, <code>DICT_SIZE_MAX</code>]
* *
* @throws CorruptedInputException * @throws CorruptedInputException if <code>propsByte</code> is invalid
* if <code>propsByte</code> is invalid or * or
* the first input byte is not 0x00 * the first input byte is not 0x00
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException dictionary size or uncompressed size
* dictionary size or uncompressed size is too * is too big for this implementation
* big for this implementation
* *
* *
*/ */
@@ -295,29 +281,27 @@ public class LZMAInputStream extends InputStream {
* Creates a new input stream that decompresses raw LZMA data (no .lzma * Creates a new input stream that decompresses raw LZMA data (no .lzma
* header) from <code>in</code> optionally with a preset dictionary. * header) from <code>in</code> optionally with a preset dictionary.
* *
* @param in input stream from which LZMA-compressed * @param in input stream from which LZMA-compressed data is read
* data is read
* *
* @param uncompSize uncompressed size of the LZMA stream or -1 * @param uncompSize uncompressed size of the LZMA stream or -1 if the end
* if the end marker is used in the LZMA stream * marker is used in the LZMA stream
* *
* @param propsByte LZMA properties byte that has the encoded * @param propsByte LZMA properties byte that has the encoded values for
* values for literal context bits (lc), literal * literal context bits (lc), literal position bits (lp), and position bits
* position bits (lp), and position bits (pb) * (pb)
* *
* @param dictSize dictionary size as bytes, must be in the range * @param dictSize dictionary size as bytes, must be in the range
* [<code>0</code>, <code>DICT_SIZE_MAX</code>] * [<code>0</code>, <code>DICT_SIZE_MAX</code>]
* *
* @param presetDict preset dictionary or <code>null</code> * @param presetDict preset dictionary or <code>null</code> to use no preset
* to use no preset dictionary * dictionary
* *
* @throws CorruptedInputException * @throws CorruptedInputException if <code>propsByte</code> is invalid
* if <code>propsByte</code> is invalid or * or
* the first input byte is not 0x00 * the first input byte is not 0x00
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException dictionary size or uncompressed size
* dictionary size or uncompressed size is too * is too big for this implementation
* big for this implementation
* *
* @throws EOFException file is truncated or corrupt * @throws EOFException file is truncated or corrupt
* *
@@ -333,29 +317,26 @@ public class LZMAInputStream extends InputStream {
* Creates a new input stream that decompresses raw LZMA data (no .lzma * Creates a new input stream that decompresses raw LZMA data (no .lzma
* header) from <code>in</code> optionally with a preset dictionary. * header) from <code>in</code> optionally with a preset dictionary.
* *
* @param in input stream from which LZMA-compressed * @param in input stream from which LZMA-compressed data is read
* data is read
* *
* @param uncompSize uncompressed size of the LZMA stream or -1 * @param uncompSize uncompressed size of the LZMA stream or -1 if the end
* if the end marker is used in the LZMA stream * marker is used in the LZMA stream
* *
* @param lc number of literal context bits, must be * @param lc number of literal context bits, must be in the range
* in the range [0, 8] * [0, 8]
* *
* @param lp number of literal position bits, must be * @param lp number of literal position bits, must be in the range
* in the range [0, 4] * [0, 4]
* *
* @param pb number position bits, must be * @param pb number position bits, must be in the range [0, 4]
* in the range [0, 4]
* *
* @param dictSize dictionary size as bytes, must be in the range * @param dictSize dictionary size as bytes, must be in the range
* [<code>0</code>, <code>DICT_SIZE_MAX</code>] * [<code>0</code>, <code>DICT_SIZE_MAX</code>]
* *
* @param presetDict preset dictionary or <code>null</code> * @param presetDict preset dictionary or <code>null</code> to use no preset
* to use no preset dictionary * dictionary
* *
* @throws CorruptedInputException * @throws CorruptedInputException if the first input byte is not 0x00
* if the first input byte is not 0x00
* *
* @throws EOFException file is truncated or corrupt * @throws EOFException file is truncated or corrupt
* *
@@ -424,19 +405,18 @@ public class LZMAInputStream extends InputStream {
/** /**
* Decompresses the next byte from this input stream. * Decompresses the next byte from this input stream.
* <p> * <p>
* Reading lots of data with <code>read()</code> from this input stream * Reading lots of data with <code>read()</code> from this input stream may
* may be inefficient. Wrap it in <code>java.io.BufferedInputStream</code> * be inefficient. Wrap it in <code>java.io.BufferedInputStream</code> if
* if you need to read lots of data one byte at a time. * you need to read lots of data one byte at a time.
* *
* @return the next decompressed byte, or <code>-1</code> * @return the next decompressed byte, or <code>-1</code> to indicate the
* to indicate the end of the compressed stream * end of the compressed stream
* *
* @throws CorruptedInputException * @throws CorruptedInputException
* *
* @throws XZIOException if the stream has been closed * @throws XZIOException if the stream has been closed
* *
* @throws EOFException * @throws EOFException compressed input is truncated or corrupt
* compressed input is truncated or corrupt
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -447,17 +427,17 @@ public class LZMAInputStream extends InputStream {
/** /**
* Decompresses into an array of bytes. * Decompresses into an array of bytes.
* <p> * <p>
* If <code>len</code> is zero, no bytes are read and <code>0</code> * If <code>len</code> is zero, no bytes are read and <code>0</code> is
* is returned. Otherwise this will block until <code>len</code> * returned. Otherwise this will block until <code>len</code> bytes have
* bytes have been decompressed, the end of the LZMA stream is reached, * been decompressed, the end of the LZMA stream is reached, or an exception
* or an exception is thrown. * is thrown.
* *
* @param buf target buffer for uncompressed data * @param buf target buffer for uncompressed data
* @param off start offset in <code>buf</code> * @param off start offset in <code>buf</code>
* @param len maximum number of uncompressed bytes to read * @param len maximum number of uncompressed bytes to read
* *
* @return number of bytes read, or <code>-1</code> to indicate * @return number of bytes read, or <code>-1</code> to indicate the end of
* the end of the compressed stream * the compressed stream
* *
* @throws CorruptedInputException * @throws CorruptedInputException
* *
@@ -552,8 +532,8 @@ public class LZMAInputStream extends InputStream {
} }
/** /**
* Closes the stream and calls <code>in.close()</code>. * Closes the stream and calls <code>in.close()</code>. If the stream was
* If the stream was already closed, this does nothing. * already closed, this does nothing.
* *
* @throws IOException if thrown by <code>in.close()</code> * @throws IOException if thrown by <code>in.close()</code>
*/ */

View File

@@ -9,11 +9,11 @@
package org.tukaani.xz; package org.tukaani.xz;
/** /**
* Thrown when the memory usage limit given to the XZ decompressor * Thrown when the memory usage limit given to the XZ decompressor would be
* would be exceeded. * exceeded.
* <p> * <p>
* The amount of memory required and the memory usage limit are * The amount of memory required and the memory usage limit are included in the
* included in the error detail message in human readable format. * error detail message in human readable format.
*/ */
public class MemoryLimitException extends XZIOException { public class MemoryLimitException extends XZIOException {
@@ -25,8 +25,8 @@ public class MemoryLimitException extends XZIOException {
/** /**
* Creates a new MemoryLimitException. * Creates a new MemoryLimitException.
* <p> * <p>
* The amount of memory needed and the memory usage limit are * The amount of memory needed and the memory usage limit are included in
* included in the error detail message. * the error detail message.
* *
* @param memoryNeeded amount of memory needed as kibibytes (KiB) * @param memoryNeeded amount of memory needed as kibibytes (KiB)
* @param memoryLimit specified memory usage limit as kibibytes (KiB) * @param memoryLimit specified memory usage limit as kibibytes (KiB)
@@ -49,8 +49,8 @@ public class MemoryLimitException extends XZIOException {
} }
/** /**
* Gets what the memory usage limit was at the time the exception * Gets what the memory usage limit was at the time the exception was
* was created. * created.
* *
* @return memory usage limit as kibibytes (KiB) * @return memory usage limit as kibibytes (KiB)
*/ */

View File

@@ -14,14 +14,14 @@ import java.io.IOException;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
/** /**
* Wraps a {@link java.io.RandomAccessFile RandomAccessFile} * Wraps a {@link java.io.RandomAccessFile RandomAccessFile} in a
* in a SeekableInputStream. * SeekableInputStream.
*/ */
public class SeekableFileInputStream extends SeekableInputStream { public class SeekableFileInputStream extends SeekableInputStream {
/** /**
* The RandomAccessFile that has been wrapped * The RandomAccessFile that has been wrapped into a
* into a SeekableFileInputStream. * SeekableFileInputStream.
*/ */
protected RandomAccessFile randomAccessFile; protected RandomAccessFile randomAccessFile;
@@ -33,8 +33,8 @@ public class SeekableFileInputStream extends SeekableInputStream {
} }
/** /**
* Creates a new seekable input stream that reads from a file with * Creates a new seekable input stream that reads from a file with the
* the specified name. * specified name.
*/ */
public SeekableFileInputStream(String name) throws FileNotFoundException { public SeekableFileInputStream(String name) throws FileNotFoundException {
randomAccessFile = new RandomAccessFile(name, "r"); randomAccessFile = new RandomAccessFile(name, "r");
@@ -63,8 +63,7 @@ public class SeekableFileInputStream extends SeekableInputStream {
} }
/** /**
* Calls * Calls null null null null null null null null null null null {@link RandomAccessFile#read(byte[],int,int)
* {@link RandomAccessFile#read(byte[],int,int)
* randomAccessFile.read(buf, off, len)}. * randomAccessFile.read(buf, off, len)}.
*/ */
public int read(byte[] buf, int off, int len) throws IOException { public int read(byte[] buf, int off, int len) throws IOException {

View File

@@ -19,22 +19,21 @@ public abstract class SeekableInputStream extends InputStream {
/** /**
* Seeks <code>n</code> bytes forward in this stream. * Seeks <code>n</code> bytes forward in this stream.
* <p> * <p>
* This will not seek past the end of the file. If the current position * This will not seek past the end of the file. If the current position is
* is already at or past the end of the file, this doesn't seek at all * already at or past the end of the file, this doesn't seek at all and
* and returns <code>0</code>. Otherwise, if skipping <code>n</code> bytes * returns <code>0</code>. Otherwise, if skipping <code>n</code> bytes would
* would cause the position to exceed the stream size, this will do * cause the position to exceed the stream size, this will do equivalent of
* equivalent of <code>seek(length())</code> and the return value will * <code>seek(length())</code> and the return value will be adjusted
* be adjusted accordingly. * accordingly.
* <p> * <p>
* If <code>n</code> is negative, the position isn't changed and * If <code>n</code> is negative, the position isn't changed and the return
* the return value is <code>0</code>. It doesn't seek backward * value is <code>0</code>. It doesn't seek backward because it would
* because it would conflict with the specification of * conflict with the specification of
* {@link java.io.InputStream#skip(long) InputStream.skip}. * {@link java.io.InputStream#skip(long) InputStream.skip}.
* *
* @return <code>0</code> if <code>n</code> is negative, * @return <code>0</code> if <code>n</code> is negative, less than
* less than <code>n</code> if skipping <code>n</code> * <code>n</code> if skipping <code>n</code> bytes would seek past the end
* bytes would seek past the end of the file, * of the file, <code>n</code> otherwise
* <code>n</code> otherwise
* *
* @throws IOException might be thrown by {@link #seek(long)} * @throws IOException might be thrown by {@link #seek(long)}
*/ */
@@ -68,14 +67,14 @@ public abstract class SeekableInputStream extends InputStream {
* Seeks to the specified absolute position in the stream. * Seeks to the specified absolute position in the stream.
* <p> * <p>
* Seeking past the end of the file should be supported by the subclasses * Seeking past the end of the file should be supported by the subclasses
* unless there is a good reason to do otherwise. If one has seeked * unless there is a good reason to do otherwise. If one has seeked past the
* past the end of the stream, <code>read</code> will return * end of the stream, <code>read</code> will return <code>-1</code> to
* <code>-1</code> to indicate end of stream. * indicate end of stream.
* *
* @param pos new read position in the stream * @param pos new read position in the stream
* *
* @throws IOException if <code>pos</code> is negative or if * @throws IOException if <code>pos</code> is negative or if a
* a stream-specific I/O error occurs * stream-specific I/O error occurs
*/ */
public abstract void seek(long pos) throws IOException; public abstract void seek(long pos) throws IOException;
} }

View File

@@ -20,53 +20,52 @@ import org.tukaani.xz.index.IndexDecoder;
import org.tukaani.xz.index.BlockInfo; import org.tukaani.xz.index.BlockInfo;
/** /**
* Decompresses a .xz file in random access mode. * Decompresses a .xz file in random access mode. This supports decompressing
* This supports decompressing concatenated .xz files. * concatenated .xz files.
* <p> * <p>
* Each .xz file consist of one or more Streams. Each Stream consist of zero * Each .xz file consist of one or more Streams. Each Stream consist of zero or
* or more Blocks. Each Stream contains an Index of Streams' Blocks. * more Blocks. Each Stream contains an Index of Streams' Blocks. The Indexes
* The Indexes from all Streams are loaded in RAM by a constructor of this * from all Streams are loaded in RAM by a constructor of this class. A typical
* class. A typical .xz file has only one Stream, and parsing its Index will * .xz file has only one Stream, and parsing its Index will need only three or
* need only three or four seeks. * four seeks.
* <p> * <p>
* To make random access possible, the data in a .xz file must be splitted * To make random access possible, the data in a .xz file must be splitted into
* into multiple Blocks of reasonable size. Decompression can only start at * multiple Blocks of reasonable size. Decompression can only start at a Block
* a Block boundary. When seeking to an uncompressed position that is not at * boundary. When seeking to an uncompressed position that is not at a Block
* a Block boundary, decompression starts at the beginning of the Block and * boundary, decompression starts at the beginning of the Block and throws away
* throws away data until the target position is reached. Thus, smaller Blocks * data until the target position is reached. Thus, smaller Blocks mean faster
* mean faster seeks to arbitrary uncompressed positions. On the other hand, * seeks to arbitrary uncompressed positions. On the other hand, smaller Blocks
* smaller Blocks mean worse compression. So one has to make a compromise * mean worse compression. So one has to make a compromise between random access
* between random access speed and compression ratio. * speed and compression ratio.
* <p> * <p>
* Implementation note: This class uses linear search to locate the correct * Implementation note: This class uses linear search to locate the correct
* Stream from the data structures in RAM. It was the simplest to implement * Stream from the data structures in RAM. It was the simplest to implement and
* and should be fine as long as there aren't too many Streams. The correct * should be fine as long as there aren't too many Streams. The correct Block
* Block inside a Stream is located using binary search and thus is fast * inside a Stream is located using binary search and thus is fast even with a
* even with a huge number of Blocks. * huge number of Blocks.
* *
* <h4>Memory usage</h4> * <h4>Memory usage</h4>
* <p> * <p>
* The amount of memory needed for the Indexes is taken into account when * The amount of memory needed for the Indexes is taken into account when
* checking the memory usage limit. Each Stream is calculated to need at * checking the memory usage limit. Each Stream is calculated to need at least
* least 1&nbsp;KiB of memory and each Block 16 bytes of memory, rounded up * 1&nbsp;KiB of memory and each Block 16 bytes of memory, rounded up to the
* to the next kibibyte. So unless the file has a huge number of Streams or * next kibibyte. So unless the file has a huge number of Streams or Blocks,
* Blocks, these don't take significant amount of memory. * these don't take significant amount of memory.
* *
* <h4>Creating random-accessible .xz files</h4> * <h4>Creating random-accessible .xz files</h4>
* <p> * <p>
* When using {@link XZOutputStream}, a new Block can be started by calling * When using {@link XZOutputStream}, a new Block can be started by calling its
* its {@link XZOutputStream#endBlock() endBlock} method. If you know * {@link XZOutputStream#endBlock() endBlock} method. If you know that the
* that the decompressor will only need to seek to certain uncompressed * decompressor will only need to seek to certain uncompressed positions, it can
* positions, it can be a good idea to start a new Block at (some of) these * be a good idea to start a new Block at (some of) these positions (and only at
* positions (and only at these positions to get better compression ratio). * these positions to get better compression ratio).
* <p> * <p>
* liblzma in XZ Utils supports starting a new Block with * liblzma in XZ Utils supports starting a new Block with
* <code>LZMA_FULL_FLUSH</code>. XZ Utils 5.1.1alpha added threaded * <code>LZMA_FULL_FLUSH</code>. XZ Utils 5.1.1alpha added threaded compression
* compression which creates multi-Block .xz files. XZ Utils 5.1.1alpha * which creates multi-Block .xz files. XZ Utils 5.1.1alpha also added the
* also added the option <code>--block-size=SIZE</code> to the xz command * option <code>--block-size=SIZE</code> to the xz command line tool. XZ Utils
* line tool. XZ Utils 5.1.2alpha added a partial implementation of * 5.1.2alpha added a partial implementation of <code>--block-list=SIZES</code>
* <code>--block-list=SIZES</code> which allows specifying sizes of * which allows specifying sizes of individual Blocks.
* individual Blocks.
* *
* @see SeekableFileInputStream * @see SeekableFileInputStream
* @see XZInputStream * @see XZInputStream
@@ -80,22 +79,21 @@ public class SeekableXZInputStream extends SeekableInputStream {
private SeekableInputStream in; private SeekableInputStream in;
/** /**
* Memory usage limit after the memory usage of the IndexDecoders have * Memory usage limit after the memory usage of the IndexDecoders have been
* been substracted. * substracted.
*/ */
private final int memoryLimit; private final int memoryLimit;
/** /**
* Memory usage of the IndexDecoders. * Memory usage of the IndexDecoders.
* <code>memoryLimit + indexMemoryUsage</code> equals the original * <code>memoryLimit + indexMemoryUsage</code> equals the original memory
* memory usage limit that was passed to the constructor. * usage limit that was passed to the constructor.
*/ */
private int indexMemoryUsage = 0; private int indexMemoryUsage = 0;
/** /**
* List of IndexDecoders, one for each Stream in the file. * List of IndexDecoders, one for each Stream in the file. The list is in
* The list is in reverse order: The first element is * reverse order: The first element is the last Stream in the file.
* the last Stream in the file.
*/ */
private final ArrayList streams = new ArrayList(); private final ArrayList streams = new ArrayList();
@@ -120,20 +118,20 @@ public class SeekableXZInputStream extends SeekableInputStream {
private int blockCount = 0; private int blockCount = 0;
/** /**
* Size and position information about the current Block. * Size and position information about the current Block. If there are no
* If there are no Blocks, all values will be <code>-1</code>. * Blocks, all values will be <code>-1</code>.
*/ */
private final BlockInfo curBlockInfo; private final BlockInfo curBlockInfo;
/** /**
* Temporary (and cached) information about the Block whose information * Temporary (and cached) information about the Block whose information is
* is queried via <code>getBlockPos</code> and related functions. * queried via <code>getBlockPos</code> and related functions.
*/ */
private final BlockInfo queriedBlockInfo; private final BlockInfo queriedBlockInfo;
/** /**
* Integrity Check in the current XZ Stream. The constructor leaves * Integrity Check in the current XZ Stream. The constructor leaves this to
* this to point to the Check of the first Stream. * point to the Check of the first Stream.
*/ */
private Check check; private Check check;
@@ -153,14 +151,14 @@ public class SeekableXZInputStream extends SeekableInputStream {
private long seekPos; private long seekPos;
/** /**
* True when <code>seek(long)</code> has been called but the actual * True when <code>seek(long)</code> has been called but the actual seeking
* seeking hasn't been done yet. * hasn't been done yet.
*/ */
private boolean seekNeeded = false; private boolean seekNeeded = false;
/** /**
* True when end of the file was reached. This can be cleared by * True when end of the file was reached. This can be cleared by calling
* calling <code>seek(long)</code>. * <code>seek(long)</code>.
*/ */
private boolean endReached = false; private boolean endReached = false;
@@ -170,32 +168,28 @@ public class SeekableXZInputStream extends SeekableInputStream {
private IOException exception = null; private IOException exception = null;
/** /**
* Temporary buffer for read(). This avoids reallocating memory * Temporary buffer for read(). This avoids reallocating memory on every
* on every read() call. * read() call.
*/ */
private final byte[] tempBuf = new byte[1]; private final byte[] tempBuf = new byte[1];
/** /**
* Creates a new seekable XZ decompressor without a memory usage limit. * Creates a new seekable XZ decompressor without a memory usage limit.
* *
* @param in seekable input stream containing one or more * @param in seekable input stream containing one or more XZ Streams; the
* XZ Streams; the whole input stream is used * whole input stream is used
* *
* @throws XZFormatException * @throws XZFormatException input is not in the XZ format
* input is not in the XZ format
* *
* @throws CorruptedInputException * @throws CorruptedInputException XZ data is corrupt or truncated
* XZ data is corrupt or truncated
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException XZ headers seem valid but they
* XZ headers seem valid but they specify * specify options not supported by this implementation
* options not supported by this implementation
* *
* @throws EOFException * @throws EOFException less than 6 bytes of input was
* less than 6 bytes of input was available * available from
* from <code>in</code>, or (unlikely) the size * <code>in</code>, or (unlikely) the size of the underlying stream got
* of the underlying stream got smaller while * smaller while this was reading from it
* this was reading from it
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -205,35 +199,31 @@ public class SeekableXZInputStream extends SeekableInputStream {
} }
/** /**
* Creates a new seekable XZ decomporessor with an optional * Creates a new seekable XZ decomporessor with an optional memory usage
* memory usage limit. * limit.
* *
* @param in seekable input stream containing one or more * @param in seekable input stream containing one or more XZ
* XZ Streams; the whole input stream is used * Streams; the
* whole input stream is used
* *
* @param memoryLimit memory usage limit in kibibytes (KiB) * @param memoryLimit memory usage limit in kibibytes (KiB) or
* or <code>-1</code> to impose no * <code>-1</code> to impose no memory usage limit
* memory usage limit
* *
* @throws XZFormatException * @throws XZFormatException input is not in the XZ format
* input is not in the XZ format
* *
* @throws CorruptedInputException * @throws CorruptedInputException XZ data is corrupt or truncated
* XZ data is corrupt or truncated
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException XZ headers seem valid but they
* XZ headers seem valid but they specify * specify options not supported by this implementation
* options not supported by this implementation
* *
* @throws MemoryLimitException * @throws MemoryLimitException decoded XZ Indexes would need more
* decoded XZ Indexes would need more memory * memory
* than allowed by the memory usage limit * than allowed by the memory usage limit
* *
* @throws EOFException * @throws EOFException less than 6 bytes of input was
* less than 6 bytes of input was available * available from
* from <code>in</code>, or (unlikely) the size * <code>in</code>, or (unlikely) the size of the underlying stream got
* of the underlying stream got smaller while * smaller while this was reading from it
* this was reading from it
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -391,13 +381,11 @@ public class SeekableXZInputStream extends SeekableInputStream {
} }
/** /**
* Gets the types of integrity checks used in the .xz file. * Gets the types of integrity checks used in the .xz file. Multiple checks
* Multiple checks are possible only if there are multiple * are possible only if there are multiple concatenated XZ Streams.
* concatenated XZ Streams.
* <p> * <p>
* The returned value has a bit set for every check type that is present. * The returned value has a bit set for every check type that is present.
* For example, if CRC64 and SHA-256 were used, the return value is * For example, if CRC64 and SHA-256 were used, the return value is <code>(1&nbsp;&lt;&lt;&nbsp;XZ.CHECK_CRC64)
* <code>(1&nbsp;&lt;&lt;&nbsp;XZ.CHECK_CRC64)
* | (1&nbsp;&lt;&lt;&nbsp;XZ.CHECK_SHA256)</code>. * | (1&nbsp;&lt;&lt;&nbsp;XZ.CHECK_SHA256)</code>.
*/ */
public int getCheckTypes() { public int getCheckTypes() {
@@ -405,22 +393,21 @@ public class SeekableXZInputStream extends SeekableInputStream {
} }
/** /**
* Gets the amount of memory in kibibytes (KiB) used by * Gets the amount of memory in kibibytes (KiB) used by the data structures
* the data structures needed to locate the XZ Blocks. * needed to locate the XZ Blocks. This is usually useless information but
* This is usually useless information but since it is calculated * since it is calculated for memory usage limit anyway, it is nice to make
* for memory usage limit anyway, it is nice to make it available to too. * it available to too.
*/ */
public int getIndexMemoryUsage() { public int getIndexMemoryUsage() {
return indexMemoryUsage; return indexMemoryUsage;
} }
/** /**
* Gets the uncompressed size of the largest XZ Block in bytes. * Gets the uncompressed size of the largest XZ Block in bytes. This can be
* This can be useful if you want to check that the file doesn't * useful if you want to check that the file doesn't have huge XZ Blocks
* have huge XZ Blocks which could make seeking to arbitrary offsets * which could make seeking to arbitrary offsets very slow. Note that huge
* very slow. Note that huge Blocks don't automatically mean that * Blocks don't automatically mean that seeking would be slow, for example,
* seeking would be slow, for example, seeking to the beginning of * seeking to the beginning of any Block is always fast.
* any Block is always fast.
*/ */
public long getLargestBlockSize() { public long getLargestBlockSize() {
return largestBlockSize; return largestBlockSize;
@@ -473,9 +460,9 @@ public class SeekableXZInputStream extends SeekableInputStream {
} }
/** /**
* Gets the position where the given compressed Block starts in * Gets the position where the given compressed Block starts in the
* the underlying .xz file. * underlying .xz file. This information is rarely useful to the users of
* This information is rarely useful to the users of this class. * this class.
* *
* @throws IndexOutOfBoundsException if * @throws IndexOutOfBoundsException if
* <code>blockNumber&nbsp;&lt;&nbsp;0</code> or * <code>blockNumber&nbsp;&lt;&nbsp;0</code> or
@@ -489,9 +476,9 @@ public class SeekableXZInputStream extends SeekableInputStream {
} }
/** /**
* Gets the compressed size of the given Block. * Gets the compressed size of the given Block. This together with the
* This together with the uncompressed size can be used to calculate * uncompressed size can be used to calculate the compression ratio of the
* the compression ratio of the specific Block. * specific Block.
* *
* @throws IndexOutOfBoundsException if * @throws IndexOutOfBoundsException if
* <code>blockNumber&nbsp;&lt;&nbsp;0</code> or * <code>blockNumber&nbsp;&lt;&nbsp;0</code> or
@@ -524,8 +511,7 @@ public class SeekableXZInputStream extends SeekableInputStream {
* Gets the number of the Block that contains the byte at the given * Gets the number of the Block that contains the byte at the given
* uncompressed position. * uncompressed position.
* *
* @throws IndexOutOfBoundsException if * @throws IndexOutOfBoundsException if <code>pos&nbsp;&lt;&nbsp;0</code> or
* <code>pos&nbsp;&lt;&nbsp;0</code> or
* <code>pos&nbsp;&gt;=&nbsp;length()</code>. * <code>pos&nbsp;&gt;=&nbsp;length()</code>.
* *
* @since 1.3 * @since 1.3
@@ -538,8 +524,8 @@ public class SeekableXZInputStream extends SeekableInputStream {
/** /**
* Decompresses the next byte from this input stream. * Decompresses the next byte from this input stream.
* *
* @return the next decompressed byte, or <code>-1</code> * @return the next decompressed byte, or <code>-1</code> to indicate the
* to indicate the end of the compressed stream * end of the compressed stream
* *
* @throws CorruptedInputException * @throws CorruptedInputException
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException
@@ -556,16 +542,16 @@ public class SeekableXZInputStream extends SeekableInputStream {
/** /**
* Decompresses into an array of bytes. * Decompresses into an array of bytes.
* <p> * <p>
* If <code>len</code> is zero, no bytes are read and <code>0</code> * If <code>len</code> is zero, no bytes are read and <code>0</code> is
* is returned. Otherwise this will try to decompress <code>len</code> * returned. Otherwise this will try to decompress <code>len</code> bytes of
* bytes of uncompressed data. Less than <code>len</code> bytes may * uncompressed data. Less than <code>len</code> bytes may be read only in
* be read only in the following situations: * the following situations:
* <ul> * <ul>
* <li>The end of the compressed data was reached successfully.</li> * <li>The end of the compressed data was reached successfully.</li>
* <li>An error is detected after at least one but less than * <li>An error is detected after at least one but less than
* <code>len</code> bytes have already been successfully * <code>len</code> bytes have already been successfully decompressed. The
* decompressed. The next call with non-zero <code>len</code> * next call with non-zero <code>len</code> will immediately throw the
* will immediately throw the pending exception.</li> * pending exception.</li>
* <li>An exception is thrown.</li> * <li>An exception is thrown.</li>
* </ul> * </ul>
* *
@@ -573,8 +559,8 @@ public class SeekableXZInputStream extends SeekableInputStream {
* @param off start offset in <code>buf</code> * @param off start offset in <code>buf</code>
* @param len maximum number of uncompressed bytes to read * @param len maximum number of uncompressed bytes to read
* *
* @return number of bytes read, or <code>-1</code> to indicate * @return number of bytes read, or <code>-1</code> to indicate the end of
* the end of the compressed stream * the compressed stream
* *
* @throws CorruptedInputException * @throws CorruptedInputException
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException
@@ -639,15 +625,14 @@ public class SeekableXZInputStream extends SeekableInputStream {
} }
/** /**
* Returns the number of uncompressed bytes that can be read * Returns the number of uncompressed bytes that can be read without
* without blocking. The value is returned with an assumption * blocking. The value is returned with an assumption that the compressed
* that the compressed input data will be valid. If the compressed * input data will be valid. If the compressed data is corrupt,
* data is corrupt, <code>CorruptedInputException</code> may get * <code>CorruptedInputException</code> may get thrown before the number of
* thrown before the number of bytes claimed to be available have * bytes claimed to be available have been read from this input stream.
* been read from this input stream.
* *
* @return the number of uncompressed bytes that can be read * @return the number of uncompressed bytes that can be read without
* without blocking * blocking
*/ */
public int available() throws IOException { public int available() throws IOException {
if (in == null) if (in == null)
@@ -663,8 +648,8 @@ public class SeekableXZInputStream extends SeekableInputStream {
} }
/** /**
* Closes the stream and calls <code>in.close()</code>. * Closes the stream and calls <code>in.close()</code>. If the stream was
* If the stream was already closed, this does nothing. * already closed, this does nothing.
* *
* @throws IOException if thrown by <code>in.close()</code> * @throws IOException if thrown by <code>in.close()</code>
*/ */
@@ -678,8 +663,8 @@ public class SeekableXZInputStream extends SeekableInputStream {
} }
/** /**
* Gets the uncompressed size of this input stream. If there are multiple * Gets the uncompressed size of this input stream. If there are multiple XZ
* XZ Streams, the total uncompressed size of all XZ Streams is returned. * Streams, the total uncompressed size of all XZ Streams is returned.
*/ */
public long length() { public long length() {
return uncompressedSize; return uncompressedSize;
@@ -698,20 +683,19 @@ public class SeekableXZInputStream extends SeekableInputStream {
} }
/** /**
* Seeks to the specified absolute uncompressed position in the stream. * Seeks to the specified absolute uncompressed position in the stream. This
* This only stores the new position, so this function itself is always * only stores the new position, so this function itself is always very
* very fast. The actual seek is done when <code>read</code> is called * fast. The actual seek is done when <code>read</code> is called to read at
* to read at least one byte. * least one byte.
* <p> * <p>
* Seeking past the end of the stream is possible. In that case * Seeking past the end of the stream is possible. In that case
* <code>read</code> will return <code>-1</code> to indicate * <code>read</code> will return <code>-1</code> to indicate the end of the
* the end of the stream. * stream.
* *
* @param pos new uncompressed read position * @param pos new uncompressed read position
* *
* @throws XZIOException * @throws XZIOException if <code>pos</code> is negative, or if stream has
* if <code>pos</code> is negative, or * been closed
* if stream has been closed
*/ */
public void seek(long pos) throws IOException { public void seek(long pos) throws IOException {
if (in == null) if (in == null)
@@ -727,10 +711,9 @@ public class SeekableXZInputStream extends SeekableInputStream {
/** /**
* Seeks to the beginning of the given XZ Block. * Seeks to the beginning of the given XZ Block.
* *
* @throws XZIOException * @throws XZIOException if <code>blockNumber&nbsp;&lt;&nbsp;0</code> or
* if <code>blockNumber&nbsp;&lt;&nbsp;0</code> or * <code>blockNumber&nbsp;&gt;=&nbsp;getBlockCount()</code>, or if stream
* <code>blockNumber&nbsp;&gt;=&nbsp;getBlockCount()</code>, * has been closed
* or if stream has been closed
* *
* @since 1.3 * @since 1.3
*/ */
@@ -749,8 +732,8 @@ public class SeekableXZInputStream extends SeekableInputStream {
} }
/** /**
* Does the actual seeking. This is also called when <code>read</code> * Does the actual seeking. This is also called when <code>read</code> needs
* needs a new Block to decode. * a new Block to decode.
*/ */
private void seek() throws IOException { private void seek() throws IOException {
// If seek(long) wasn't called, we simply need to get the next Block // If seek(long) wasn't called, we simply need to get the next Block
@@ -844,8 +827,8 @@ public class SeekableXZInputStream extends SeekableInputStream {
} }
/** /**
* Locates the given Block and stores information about it * Locates the given Block and stores information about it to
* to <code>info</code>. * <code>info</code>.
*/ */
private void locateBlockByNumber(BlockInfo info, int blockNumber) { private void locateBlockByNumber(BlockInfo info, int blockNumber) {
// Validate. // Validate.

View File

@@ -18,11 +18,11 @@ import org.tukaani.xz.index.IndexHash;
import org.tukaani.xz.check.Check; import org.tukaani.xz.check.Check;
/** /**
* Decompresses exactly one XZ Stream in streamed mode (no seeking). * Decompresses exactly one XZ Stream in streamed mode (no seeking). The
* The decompression stops after the first XZ Stream has been decompressed, * decompression stops after the first XZ Stream has been decompressed, and the
* and the read position in the input stream is left at the first byte * read position in the input stream is left at the first byte after the end of
* after the end of the XZ Stream. This can be useful when XZ data has * the XZ Stream. This can be useful when XZ data has been stored inside some
* been stored inside some other file format or protocol. * other file format or protocol.
* <p> * <p>
* Unless you know what you are doing, don't use this class to decompress * Unless you know what you are doing, don't use this class to decompress
* standalone .xz files. For that purpose, use <code>XZInputStream</code>. * standalone .xz files. For that purpose, use <code>XZInputStream</code>.
@@ -30,11 +30,11 @@ import org.tukaani.xz.check.Check;
* <h4>When uncompressed size is known beforehand</h4> * <h4>When uncompressed size is known beforehand</h4>
* <p> * <p>
* If you are decompressing complete XZ streams and your application knows * If you are decompressing complete XZ streams and your application knows
* exactly how much uncompressed data there should be, it is good to try * exactly how much uncompressed data there should be, it is good to try reading
* reading one more byte by calling <code>read()</code> and checking * one more byte by calling <code>read()</code> and checking that it returns
* that it returns <code>-1</code>. This way the decompressor will parse the * <code>-1</code>. This way the decompressor will parse the file footers and
* file footers and verify the integrity checks, giving the caller more * verify the integrity checks, giving the caller more confidence that the
* confidence that the uncompressed data is valid. * uncompressed data is valid.
* *
* @see XZInputStream * @see XZInputStream
*/ */
@@ -52,29 +52,25 @@ public class SingleXZInputStream extends InputStream {
private final byte[] tempBuf = new byte[1]; private final byte[] tempBuf = new byte[1];
/** /**
* Creates a new XZ decompressor that decompresses exactly one * Creates a new XZ decompressor that decompresses exactly one XZ Stream
* XZ Stream from <code>in</code> without a memory usage limit. * from <code>in</code> without a memory usage limit.
* <p> * <p>
* This constructor reads and parses the XZ Stream Header (12 bytes) * This constructor reads and parses the XZ Stream Header (12 bytes) from
* from <code>in</code>. The header of the first Block is not read * <code>in</code>. The header of the first Block is not read until
* until <code>read</code> is called. * <code>read</code> is called.
* *
* @param in input stream from which XZ-compressed * @param in input stream from which XZ-compressed data is read
* data is read
* *
* @throws XZFormatException * @throws XZFormatException input is not in the XZ format
* input is not in the XZ format
* *
* @throws CorruptedInputException * @throws CorruptedInputException XZ header CRC32 doesn't match
* XZ header CRC32 doesn't match
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException XZ header is valid but specifies
* XZ header is valid but specifies options * options not supported by this implementation
* not supported by this implementation
* *
* @throws EOFException * @throws EOFException less than 12 bytes of input was
* less than 12 bytes of input was available * available from
* from <code>in</code> * <code>in</code>
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -83,32 +79,27 @@ public class SingleXZInputStream extends InputStream {
} }
/** /**
* Creates a new XZ decompressor that decompresses exactly one * Creates a new XZ decompressor that decompresses exactly one XZ Stream
* XZ Stream from <code>in</code> with an optional memory usage limit. * from <code>in</code> with an optional memory usage limit.
* <p> * <p>
* This is identical to <code>SingleXZInputStream(InputStream)</code> * This is identical to <code>SingleXZInputStream(InputStream)</code> except
* except that this takes also the <code>memoryLimit</code> argument. * that this takes also the <code>memoryLimit</code> argument.
* *
* @param in input stream from which XZ-compressed * @param in input stream from which XZ-compressed data is read
* data is read
* *
* @param memoryLimit memory usage limit in kibibytes (KiB) * @param memoryLimit memory usage limit in kibibytes (KiB) or
* or <code>-1</code> to impose no * <code>-1</code> to impose no memory usage limit
* memory usage limit
* *
* @throws XZFormatException * @throws XZFormatException input is not in the XZ format
* input is not in the XZ format
* *
* @throws CorruptedInputException * @throws CorruptedInputException XZ header CRC32 doesn't match
* XZ header CRC32 doesn't match
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException XZ header is valid but specifies
* XZ header is valid but specifies options * options not supported by this implementation
* not supported by this implementation
* *
* @throws EOFException * @throws EOFException less than 12 bytes of input was
* less than 12 bytes of input was available * available from
* from <code>in</code> * <code>in</code>
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -158,12 +149,12 @@ public class SingleXZInputStream extends InputStream {
/** /**
* Decompresses the next byte from this input stream. * Decompresses the next byte from this input stream.
* <p> * <p>
* Reading lots of data with <code>read()</code> from this input stream * Reading lots of data with <code>read()</code> from this input stream may
* may be inefficient. Wrap it in {@link java.io.BufferedInputStream} * be inefficient. Wrap it in {@link java.io.BufferedInputStream} if you
* if you need to read lots of data one byte at a time. * need to read lots of data one byte at a time.
* *
* @return the next decompressed byte, or <code>-1</code> * @return the next decompressed byte, or <code>-1</code> to indicate the
* to indicate the end of the compressed stream * end of the compressed stream
* *
* @throws CorruptedInputException * @throws CorruptedInputException
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException
@@ -171,8 +162,8 @@ public class SingleXZInputStream extends InputStream {
* *
* @throws XZIOException if the stream has been closed * @throws XZIOException if the stream has been closed
* *
* @throws EOFException * @throws EOFException compressed input is truncated or
* compressed input is truncated or corrupt * corrupt
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -183,16 +174,16 @@ public class SingleXZInputStream extends InputStream {
/** /**
* Decompresses into an array of bytes. * Decompresses into an array of bytes.
* <p> * <p>
* If <code>len</code> is zero, no bytes are read and <code>0</code> * If <code>len</code> is zero, no bytes are read and <code>0</code> is
* is returned. Otherwise this will try to decompress <code>len</code> * returned. Otherwise this will try to decompress <code>len</code> bytes of
* bytes of uncompressed data. Less than <code>len</code> bytes may * uncompressed data. Less than <code>len</code> bytes may be read only in
* be read only in the following situations: * the following situations:
* <ul> * <ul>
* <li>The end of the compressed data was reached successfully.</li> * <li>The end of the compressed data was reached successfully.</li>
* <li>An error is detected after at least one but less <code>len</code> * <li>An error is detected after at least one but less <code>len</code>
* bytes have already been successfully decompressed. * bytes have already been successfully decompressed. The next call with
* The next call with non-zero <code>len</code> will immediately * non-zero <code>len</code> will immediately throw the pending
* throw the pending exception.</li> * exception.</li>
* <li>An exception is thrown.</li> * <li>An exception is thrown.</li>
* </ul> * </ul>
* *
@@ -200,8 +191,8 @@ public class SingleXZInputStream extends InputStream {
* @param off start offset in <code>buf</code> * @param off start offset in <code>buf</code>
* @param len maximum number of uncompressed bytes to read * @param len maximum number of uncompressed bytes to read
* *
* @return number of bytes read, or <code>-1</code> to indicate * @return number of bytes read, or <code>-1</code> to indicate the end of
* the end of the compressed stream * the compressed stream
* *
* @throws CorruptedInputException * @throws CorruptedInputException
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException
@@ -209,8 +200,8 @@ public class SingleXZInputStream extends InputStream {
* *
* @throws XZIOException if the stream has been closed * @throws XZIOException if the stream has been closed
* *
* @throws EOFException * @throws EOFException compressed input is truncated or
* compressed input is truncated or corrupt * corrupt
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -279,15 +270,14 @@ public class SingleXZInputStream extends InputStream {
} }
/** /**
* Returns the number of uncompressed bytes that can be read * Returns the number of uncompressed bytes that can be read without
* without blocking. The value is returned with an assumption * blocking. The value is returned with an assumption that the compressed
* that the compressed input data will be valid. If the compressed * input data will be valid. If the compressed data is corrupt,
* data is corrupt, <code>CorruptedInputException</code> may get * <code>CorruptedInputException</code> may get thrown before the number of
* thrown before the number of bytes claimed to be available have * bytes claimed to be available have been read from this input stream.
* been read from this input stream.
* *
* @return the number of uncompressed bytes that can be read * @return the number of uncompressed bytes that can be read without
* without blocking * blocking
*/ */
public int available() throws IOException { public int available() throws IOException {
if (in == null) if (in == null)
@@ -300,8 +290,8 @@ public class SingleXZInputStream extends InputStream {
} }
/** /**
* Closes the stream and calls <code>in.close()</code>. * Closes the stream and calls <code>in.close()</code>. If the stream was
* If the stream was already closed, this does nothing. * already closed, this does nothing.
* *
* @throws IOException if thrown by <code>in.close()</code> * @throws IOException if thrown by <code>in.close()</code>
*/ */

View File

@@ -9,23 +9,23 @@
package org.tukaani.xz; package org.tukaani.xz;
/** /**
* Thrown when compression options not supported by this implementation * Thrown when compression options not supported by this implementation are
* are detected. Some other implementation might support those options. * detected. Some other implementation might support those options.
*/ */
public class UnsupportedOptionsException extends XZIOException { public class UnsupportedOptionsException extends XZIOException {
private static final long serialVersionUID = 3L; private static final long serialVersionUID = 3L;
/** /**
* Creates a new UnsupportedOptionsException with null * Creates a new UnsupportedOptionsException with null as its error detail
* as its error detail message. * message.
*/ */
public UnsupportedOptionsException() { public UnsupportedOptionsException() {
} }
/** /**
* Creates a new UnsupportedOptionsException with the given * Creates a new UnsupportedOptionsException with the given error detail
* error detail message. * message.
* *
* @param s error detail message * @param s error detail message
*/ */

View File

@@ -14,8 +14,8 @@ package org.tukaani.xz;
public class XZ { public class XZ {
/** /**
* XZ Header Magic Bytes begin a XZ file. * XZ Header Magic Bytes begin a XZ file. This can be useful to detect XZ
* This can be useful to detect XZ compressed data. * compressed data.
*/ */
public static final byte[] HEADER_MAGIC = { public static final byte[] HEADER_MAGIC = {
(byte) 0xFD, '7', 'z', 'X', 'Z', '\0' }; (byte) 0xFD, '7', 'z', 'X', 'Z', '\0' };
@@ -28,9 +28,9 @@ public class XZ {
/** /**
* Integrity check ID indicating that no integrity check is calculated. * Integrity check ID indicating that no integrity check is calculated.
* <p> * <p>
* Omitting the integrity check is strongly discouraged except when * Omitting the integrity check is strongly discouraged except when the
* the integrity of the data will be verified by other means anyway, * integrity of the data will be verified by other means anyway, and
* and calculating the check twice would be useless. * calculating the check twice would be useless.
*/ */
public static final int CHECK_NONE = 0; public static final int CHECK_NONE = 0;

View File

@@ -9,9 +9,8 @@
package org.tukaani.xz; package org.tukaani.xz;
/** /**
* Generic {@link java.io.IOException IOException} specific to this package. * Generic {@link java.io.IOException IOException} specific to this package. The
* The other IOExceptions in this package extend * other IOExceptions in this package extend from <code>XZIOException</code>.
* from <code>XZIOException</code>.
*/ */
public class XZIOException extends java.io.IOException { public class XZIOException extends java.io.IOException {

View File

@@ -17,9 +17,9 @@ import org.tukaani.xz.common.DecoderUtil;
/** /**
* Decompresses a .xz file in streamed mode (no seeking). * Decompresses a .xz file in streamed mode (no seeking).
* <p> * <p>
* Use this to decompress regular standalone .xz files. This reads from * Use this to decompress regular standalone .xz files. This reads from its
* its input stream until the end of the input or until an error occurs. * input stream until the end of the input or until an error occurs. This
* This supports decompressing concatenated .xz files. * supports decompressing concatenated .xz files.
* *
* <h4>Typical use cases</h4> * <h4>Typical use cases</h4>
* <p> * <p>
@@ -30,18 +30,18 @@ import org.tukaani.xz.common.DecoderUtil;
* XZInputStream inxz = new XZInputStream(infile); * XZInputStream inxz = new XZInputStream(infile);
* </pre></blockquote> * </pre></blockquote>
* <p> * <p>
* It's important to keep in mind that decompressor memory usage depends * It's important to keep in mind that decompressor memory usage depends on the
* on the settings used to compress the file. The worst-case memory usage * settings used to compress the file. The worst-case memory usage of
* of XZInputStream is currently 1.5&nbsp;GiB. Still, very few files will * XZInputStream is currently 1.5&nbsp;GiB. Still, very few files will require
* require more than about 65&nbsp;MiB because that's how much decompressing * more than about 65&nbsp;MiB because that's how much decompressing a file
* a file created with the highest preset level will need, and only a few * created with the highest preset level will need, and only a few people use
* people use settings other than the predefined presets. * settings other than the predefined presets.
* <p> * <p>
* It is possible to specify a memory usage limit for * It is possible to specify a memory usage limit for
* <code>XZInputStream</code>. If decompression requires more memory than * <code>XZInputStream</code>. If decompression requires more memory than the
* the specified limit, MemoryLimitException will be thrown when reading * specified limit, MemoryLimitException will be thrown when reading from the
* from the stream. For example, the following sets the memory usage limit * stream. For example, the following sets the memory usage limit to
* to 100&nbsp;MiB: * 100&nbsp;MiB:
* <p> * <p>
* <blockquote><pre> * <blockquote><pre>
* InputStream infile = new FileInputStream("foo.xz"); * InputStream infile = new FileInputStream("foo.xz");
@@ -50,13 +50,12 @@ import org.tukaani.xz.common.DecoderUtil;
* *
* <h4>When uncompressed size is known beforehand</h4> * <h4>When uncompressed size is known beforehand</h4>
* <p> * <p>
* If you are decompressing complete files and your application knows * If you are decompressing complete files and your application knows exactly
* exactly how much uncompressed data there should be, it is good to try * how much uncompressed data there should be, it is good to try reading one
* reading one more byte by calling <code>read()</code> and checking * more byte by calling <code>read()</code> and checking that it returns
* that it returns <code>-1</code>. This way the decompressor will parse the * <code>-1</code>. This way the decompressor will parse the file footers and
* file footers and verify the integrity checks, giving the caller more * verify the integrity checks, giving the caller more confidence that the
* confidence that the uncompressed data is valid. (This advice seems to * uncompressed data is valid. (This advice seems to apply to
* apply to
* {@link java.util.zip.GZIPInputStream java.util.zip.GZIPInputStream} too.) * {@link java.util.zip.GZIPInputStream java.util.zip.GZIPInputStream} too.)
* *
* @see SingleXZInputStream * @see SingleXZInputStream
@@ -74,26 +73,22 @@ public class XZInputStream extends InputStream {
/** /**
* Creates a new XZ decompressor without a memory usage limit. * Creates a new XZ decompressor without a memory usage limit.
* <p> * <p>
* This constructor reads and parses the XZ Stream Header (12 bytes) * This constructor reads and parses the XZ Stream Header (12 bytes) from
* from <code>in</code>. The header of the first Block is not read * <code>in</code>. The header of the first Block is not read until
* until <code>read</code> is called. * <code>read</code> is called.
* *
* @param in input stream from which XZ-compressed * @param in input stream from which XZ-compressed data is read
* data is read
* *
* @throws XZFormatException * @throws XZFormatException input is not in the XZ format
* input is not in the XZ format
* *
* @throws CorruptedInputException * @throws CorruptedInputException XZ header CRC32 doesn't match
* XZ header CRC32 doesn't match
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException XZ header is valid but specifies
* XZ header is valid but specifies options * options not supported by this implementation
* not supported by this implementation
* *
* @throws EOFException * @throws EOFException less than 12 bytes of input was
* less than 12 bytes of input was available * available from
* from <code>in</code> * <code>in</code>
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -104,29 +99,24 @@ public class XZInputStream extends InputStream {
/** /**
* Creates a new XZ decompressor with an optional memory usage limit. * Creates a new XZ decompressor with an optional memory usage limit.
* <p> * <p>
* This is identical to <code>XZInputStream(InputStream)</code> except * This is identical to <code>XZInputStream(InputStream)</code> except that
* that this takes also the <code>memoryLimit</code> argument. * this takes also the <code>memoryLimit</code> argument.
* *
* @param in input stream from which XZ-compressed * @param in input stream from which XZ-compressed data is read
* data is read
* *
* @param memoryLimit memory usage limit in kibibytes (KiB) * @param memoryLimit memory usage limit in kibibytes (KiB) or
* or <code>-1</code> to impose no * <code>-1</code> to impose no memory usage limit
* memory usage limit
* *
* @throws XZFormatException * @throws XZFormatException input is not in the XZ format
* input is not in the XZ format
* *
* @throws CorruptedInputException * @throws CorruptedInputException XZ header CRC32 doesn't match
* XZ header CRC32 doesn't match
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException XZ header is valid but specifies
* XZ header is valid but specifies options * options not supported by this implementation
* not supported by this implementation
* *
* @throws EOFException * @throws EOFException less than 12 bytes of input was
* less than 12 bytes of input was available * available from
* from <code>in</code> * <code>in</code>
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -139,12 +129,12 @@ public class XZInputStream extends InputStream {
/** /**
* Decompresses the next byte from this input stream. * Decompresses the next byte from this input stream.
* <p> * <p>
* Reading lots of data with <code>read()</code> from this input stream * Reading lots of data with <code>read()</code> from this input stream may
* may be inefficient. Wrap it in {@link java.io.BufferedInputStream} * be inefficient. Wrap it in {@link java.io.BufferedInputStream} if you
* if you need to read lots of data one byte at a time. * need to read lots of data one byte at a time.
* *
* @return the next decompressed byte, or <code>-1</code> * @return the next decompressed byte, or <code>-1</code> to indicate the
* to indicate the end of the compressed stream * end of the compressed stream
* *
* @throws CorruptedInputException * @throws CorruptedInputException
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException
@@ -152,8 +142,8 @@ public class XZInputStream extends InputStream {
* *
* @throws XZIOException if the stream has been closed * @throws XZIOException if the stream has been closed
* *
* @throws EOFException * @throws EOFException compressed input is truncated or
* compressed input is truncated or corrupt * corrupt
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -164,16 +154,16 @@ public class XZInputStream extends InputStream {
/** /**
* Decompresses into an array of bytes. * Decompresses into an array of bytes.
* <p> * <p>
* If <code>len</code> is zero, no bytes are read and <code>0</code> * If <code>len</code> is zero, no bytes are read and <code>0</code> is
* is returned. Otherwise this will try to decompress <code>len</code> * returned. Otherwise this will try to decompress <code>len</code> bytes of
* bytes of uncompressed data. Less than <code>len</code> bytes may * uncompressed data. Less than <code>len</code> bytes may be read only in
* be read only in the following situations: * the following situations:
* <ul> * <ul>
* <li>The end of the compressed data was reached successfully.</li> * <li>The end of the compressed data was reached successfully.</li>
* <li>An error is detected after at least one but less <code>len</code> * <li>An error is detected after at least one but less <code>len</code>
* bytes have already been successfully decompressed. * bytes have already been successfully decompressed. The next call with
* The next call with non-zero <code>len</code> will immediately * non-zero <code>len</code> will immediately throw the pending
* throw the pending exception.</li> * exception.</li>
* <li>An exception is thrown.</li> * <li>An exception is thrown.</li>
* </ul> * </ul>
* *
@@ -181,8 +171,8 @@ public class XZInputStream extends InputStream {
* @param off start offset in <code>buf</code> * @param off start offset in <code>buf</code>
* @param len maximum number of uncompressed bytes to read * @param len maximum number of uncompressed bytes to read
* *
* @return number of bytes read, or <code>-1</code> to indicate * @return number of bytes read, or <code>-1</code> to indicate the end of
* the end of the compressed stream * the compressed stream
* *
* @throws CorruptedInputException * @throws CorruptedInputException
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException
@@ -190,8 +180,8 @@ public class XZInputStream extends InputStream {
* *
* @throws XZIOException if the stream has been closed * @throws XZIOException if the stream has been closed
* *
* @throws EOFException * @throws EOFException compressed input is truncated or
* compressed input is truncated or corrupt * corrupt
* *
* @throws IOException may be thrown by <code>in</code> * @throws IOException may be thrown by <code>in</code>
*/ */
@@ -276,15 +266,14 @@ public class XZInputStream extends InputStream {
} }
/** /**
* Returns the number of uncompressed bytes that can be read * Returns the number of uncompressed bytes that can be read without
* without blocking. The value is returned with an assumption * blocking. The value is returned with an assumption that the compressed
* that the compressed input data will be valid. If the compressed * input data will be valid. If the compressed data is corrupt,
* data is corrupt, <code>CorruptedInputException</code> may get * <code>CorruptedInputException</code> may get thrown before the number of
* thrown before the number of bytes claimed to be available have * bytes claimed to be available have been read from this input stream.
* been read from this input stream.
* *
* @return the number of uncompressed bytes that can be read * @return the number of uncompressed bytes that can be read without
* without blocking * blocking
*/ */
public int available() throws IOException { public int available() throws IOException {
if (in == null) if (in == null)
@@ -297,8 +286,8 @@ public class XZInputStream extends InputStream {
} }
/** /**
* Closes the stream and calls <code>in.close()</code>. * Closes the stream and calls <code>in.close()</code>. If the stream was
* If the stream was already closed, this does nothing. * already closed, this does nothing.
* *
* @throws IOException if thrown by <code>in.close()</code> * @throws IOException if thrown by <code>in.close()</code>
*/ */

View File

@@ -20,25 +20,24 @@ import org.tukaani.xz.index.IndexEncoder;
* *
* <h4>Examples</h4> * <h4>Examples</h4>
* <p> * <p>
* Getting an output stream to compress with LZMA2 using the default * Getting an output stream to compress with LZMA2 using the default settings
* settings and the default integrity check type (CRC64): * and the default integrity check type (CRC64):
* <p> * <p>
* <blockquote><pre> * <blockquote><pre>
* FileOutputStream outfile = new FileOutputStream("foo.xz"); * FileOutputStream outfile = new FileOutputStream("foo.xz");
* XZOutputStream outxz = new XZOutputStream(outfile, new LZMA2Options()); * XZOutputStream outxz = new XZOutputStream(outfile, new LZMA2Options());
* </pre></blockquote> * </pre></blockquote>
* <p> * <p>
* Using the preset level <code>8</code> for LZMA2 (the default * Using the preset level <code>8</code> for LZMA2 (the default is
* is <code>6</code>) and SHA-256 instead of CRC64 for integrity checking: * <code>6</code>) and SHA-256 instead of CRC64 for integrity checking:
* <p> * <p>
* <blockquote><pre> * <blockquote><pre>
* XZOutputStream outxz = new XZOutputStream(outfile, new LZMA2Options(8), * XZOutputStream outxz = new XZOutputStream(outfile, new LZMA2Options(8),
* XZ.CHECK_SHA256); * XZ.CHECK_SHA256);
* </pre></blockquote> * </pre></blockquote>
* <p> * <p>
* Using the x86 BCJ filter together with LZMA2 to compress x86 executables * Using the x86 BCJ filter together with LZMA2 to compress x86 executables and
* and printing the memory usage information before creating the * printing the memory usage information before creating the XZOutputStream:
* XZOutputStream:
* <p> * <p>
* <blockquote><pre> * <blockquote><pre>
* X86Options x86 = new X86Options(); * X86Options x86 = new X86Options();
@@ -64,9 +63,9 @@ public class XZOutputStream extends FinishableOutputStream {
private FilterEncoder[] filters; private FilterEncoder[] filters;
/** /**
* True if the current filter chain supports flushing. * True if the current filter chain supports flushing. If it doesn't support
* If it doesn't support flushing, <code>flush()</code> * flushing, <code>flush()</code> will use <code>endBlock()</code> as a
* will use <code>endBlock()</code> as a fallback. * fallback.
*/ */
private boolean filtersSupportFlushing; private boolean filtersSupportFlushing;
@@ -76,19 +75,17 @@ public class XZOutputStream extends FinishableOutputStream {
private final byte[] tempBuf = new byte[1]; private final byte[] tempBuf = new byte[1];
/** /**
* Creates a new XZ compressor using one filter and CRC64 as * Creates a new XZ compressor using one filter and CRC64 as the integrity
* the integrity check. This constructor is equivalent to passing * check. This constructor is equivalent to passing a single-member
* a single-member FilterOptions array to * FilterOptions array to
* <code>XZOutputStream(OutputStream, FilterOptions[])</code>. * <code>XZOutputStream(OutputStream, FilterOptions[])</code>.
* *
* @param out output stream to which the compressed data * @param out output stream to which the compressed data will be
* will be written * written
* *
* @param filterOptions * @param filterOptions filter options to use
* filter options to use
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException invalid filter chain
* invalid filter chain
* *
* @throws IOException may be thrown from <code>out</code> * @throws IOException may be thrown from <code>out</code>
*/ */
@@ -98,22 +95,20 @@ public class XZOutputStream extends FinishableOutputStream {
} }
/** /**
* Creates a new XZ compressor using one filter and the specified * Creates a new XZ compressor using one filter and the specified integrity
* integrity check type. This constructor is equivalent to * check type. This constructor is equivalent to passing a single-member
* passing a single-member FilterOptions array to * FilterOptions array to
* <code>XZOutputStream(OutputStream, FilterOptions[], int)</code>. * <code>XZOutputStream(OutputStream, FilterOptions[], int)</code>.
* *
* @param out output stream to which the compressed data * @param out output stream to which the compressed data will be
* will be written * written
* *
* @param filterOptions * @param filterOptions filter options to use
* filter options to use
* *
* @param checkType type of the integrity check, * @param checkType type of the integrity check, for example
* for example XZ.CHECK_CRC32 * XZ.CHECK_CRC32
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException invalid filter chain
* invalid filter chain
* *
* @throws IOException may be thrown from <code>out</code> * @throws IOException may be thrown from <code>out</code>
*/ */
@@ -123,18 +118,16 @@ public class XZOutputStream extends FinishableOutputStream {
} }
/** /**
* Creates a new XZ compressor using 1-4 filters and CRC64 as * Creates a new XZ compressor using 1-4 filters and CRC64 as the integrity
* the integrity check. This constructor is equivalent * check. This constructor is equivalent
* <code>XZOutputStream(out, filterOptions, XZ.CHECK_CRC64)</code>. * <code>XZOutputStream(out, filterOptions, XZ.CHECK_CRC64)</code>.
* *
* @param out output stream to which the compressed data * @param out output stream to which the compressed data will be
* will be written * written
* *
* @param filterOptions * @param filterOptions array of filter options to use
* array of filter options to use
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException invalid filter chain
* invalid filter chain
* *
* @throws IOException may be thrown from <code>out</code> * @throws IOException may be thrown from <code>out</code>
*/ */
@@ -144,20 +137,18 @@ public class XZOutputStream extends FinishableOutputStream {
} }
/** /**
* Creates a new XZ compressor using 1-4 filters and the specified * Creates a new XZ compressor using 1-4 filters and the specified integrity
* integrity check type. * check type.
* *
* @param out output stream to which the compressed data * @param out output stream to which the compressed data will be
* will be written * written
* *
* @param filterOptions * @param filterOptions array of filter options to use
* array of filter options to use
* *
* @param checkType type of the integrity check, * @param checkType type of the integrity check, for example
* for example XZ.CHECK_CRC32 * XZ.CHECK_CRC32
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException invalid filter chain
* invalid filter chain
* *
* @throws IOException may be thrown from <code>out</code> * @throws IOException may be thrown from <code>out</code>
*/ */
@@ -173,16 +164,14 @@ public class XZOutputStream extends FinishableOutputStream {
} }
/** /**
* Updates the filter chain with a single filter. * Updates the filter chain with a single filter. This is equivalent to
* This is equivalent to passing a single-member FilterOptions array * passing a single-member FilterOptions array to
* to <code>updateFilters(FilterOptions[])</code>. * <code>updateFilters(FilterOptions[])</code>.
* *
* @param filterOptions * @param filterOptions new filter to use
* new filter to use
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException unsupported filter chain, or trying
* unsupported filter chain, or trying to change * to change the filter chain in the middle of a Block
* the filter chain in the middle of a Block
*/ */
public void updateFilters(FilterOptions filterOptions) public void updateFilters(FilterOptions filterOptions)
throws XZIOException { throws XZIOException {
@@ -194,17 +183,15 @@ public class XZOutputStream extends FinishableOutputStream {
/** /**
* Updates the filter chain with 1-4 filters. * Updates the filter chain with 1-4 filters.
* <p> * <p>
* Currently this cannot be used to update e.g. LZMA2 options in the * Currently this cannot be used to update e.g. LZMA2 options in the middle
* middle of a XZ Block. Use <code>endBlock()</code> to finish the * of a XZ Block. Use <code>endBlock()</code> to finish the current XZ Block
* current XZ Block before calling this function. The new filter chain * before calling this function. The new filter chain will then be used for
* will then be used for the next XZ Block. * the next XZ Block.
* *
* @param filterOptions * @param filterOptions new filter chain to use
* new filter chain to use
* *
* @throws UnsupportedOptionsException * @throws UnsupportedOptionsException unsupported filter chain, or trying
* unsupported filter chain, or trying to change * to change the filter chain in the middle of a Block
* the filter chain in the middle of a Block
*/ */
public void updateFilters(FilterOptions[] filterOptions) public void updateFilters(FilterOptions[] filterOptions)
throws XZIOException { throws XZIOException {
@@ -230,12 +217,10 @@ public class XZOutputStream extends FinishableOutputStream {
/** /**
* Writes one byte to be compressed. * Writes one byte to be compressed.
* *
* @throws XZIOException * @throws XZIOException XZ Stream has grown too big
* XZ Stream has grown too big
* *
* @throws XZIOException * @throws XZIOException <code>finish()</code> or <code>close()</code> was
* <code>finish()</code> or <code>close()</code> * already called
* was already called
* *
* @throws IOException may be thrown by the underlying output stream * @throws IOException may be thrown by the underlying output stream
*/ */
@@ -245,26 +230,22 @@ public class XZOutputStream extends FinishableOutputStream {
} }
/** /**
* Writes an array of bytes to be compressed. * Writes an array of bytes to be compressed. The compressors tend to do
* The compressors tend to do internal buffering and thus the written * internal buffering and thus the written data won't be readable from the
* data won't be readable from the compressed output immediately. * compressed output immediately. Use <code>flush()</code> to force
* Use <code>flush()</code> to force everything written so far to * everything written so far to be written to the underlaying output stream,
* be written to the underlaying output stream, but be aware that * but be aware that flushing reduces compression ratio.
* flushing reduces compression ratio.
* *
* @param buf buffer of bytes to be written * @param buf buffer of bytes to be written
* @param off start offset in <code>buf</code> * @param off start offset in <code>buf</code>
* @param len number of bytes to write * @param len number of bytes to write
* *
* @throws XZIOException * @throws XZIOException XZ Stream has grown too big: total file size about
* XZ Stream has grown too big: total file size * 8&nbsp;EiB or the Index field exceeds 16&nbsp;GiB; you shouldn't reach
* about 8&nbsp;EiB or the Index field exceeds * these sizes in practice
* 16&nbsp;GiB; you shouldn't reach these sizes
* in practice
* *
* @throws XZIOException * @throws XZIOException <code>finish()</code> or <code>close()</code> was
* <code>finish()</code> or <code>close()</code> * already called and len &gt; 0
* was already called and len &gt; 0
* *
* @throws IOException may be thrown by the underlying output stream * @throws IOException may be thrown by the underlying output stream
*/ */
@@ -290,27 +271,25 @@ public class XZOutputStream extends FinishableOutputStream {
} }
/** /**
* Finishes the current XZ Block (but not the whole XZ Stream). * Finishes the current XZ Block (but not the whole XZ Stream). This doesn't
* This doesn't flush the stream so it's possible that not all data will * flush the stream so it's possible that not all data will be
* be decompressible from the output stream when this function returns. * decompressible from the output stream when this function returns. Call
* Call also <code>flush()</code> if flushing is wanted in addition to * also <code>flush()</code> if flushing is wanted in addition to finishing
* finishing the current XZ Block. * the current XZ Block.
* <p> * <p>
* If there is no unfinished Block open, this function will do nothing. * If there is no unfinished Block open, this function will do nothing. (No
* (No empty XZ Block will be created.) * empty XZ Block will be created.)
* <p> * <p>
* This function can be useful, for example, to create * This function can be useful, for example, to create random-accessible .xz
* random-accessible .xz files. * files.
* <p> * <p>
* Starting a new XZ Block means that the encoder state is reset. * Starting a new XZ Block means that the encoder state is reset. Doing this
* Doing this very often will increase the size of the compressed * very often will increase the size of the compressed file a lot (more than
* file a lot (more than plain <code>flush()</code> would do). * plain <code>flush()</code> would do).
* *
* @throws XZIOException * @throws XZIOException XZ Stream has grown too big
* XZ Stream has grown too big
* *
* @throws XZIOException * @throws XZIOException stream finished or closed
* stream finished or closed
* *
* @throws IOException may be thrown by the underlying output stream * @throws IOException may be thrown by the underlying output stream
*/ */
@@ -337,24 +316,21 @@ public class XZOutputStream extends FinishableOutputStream {
} }
/** /**
* Flushes the encoder and calls <code>out.flush()</code>. * Flushes the encoder and calls <code>out.flush()</code>. All buffered
* All buffered pending data will then be decompressible from * pending data will then be decompressible from the output stream.
* the output stream.
* <p> * <p>
* Calling this function very often may increase the compressed * Calling this function very often may increase the compressed file size a
* file size a lot. The filter chain options may affect the size * lot. The filter chain options may affect the size increase too. For
* increase too. For example, with LZMA2 the HC4 match finder has * example, with LZMA2 the HC4 match finder has smaller penalty with
* smaller penalty with flushing than BT4. * flushing than BT4.
* <p> * <p>
* Some filters don't support flushing. If the filter chain has * Some filters don't support flushing. If the filter chain has such a
* such a filter, <code>flush()</code> will call <code>endBlock()</code> * filter, <code>flush()</code> will call <code>endBlock()</code> before
* before flushing. * flushing.
* *
* @throws XZIOException * @throws XZIOException XZ Stream has grown too big
* XZ Stream has grown too big
* *
* @throws XZIOException * @throws XZIOException stream finished or closed
* stream finished or closed
* *
* @throws IOException may be thrown by the underlying output stream * @throws IOException may be thrown by the underlying output stream
*/ */
@@ -384,20 +360,19 @@ public class XZOutputStream extends FinishableOutputStream {
} }
/** /**
* Finishes compression without closing the underlying stream. * Finishes compression without closing the underlying stream. No more data
* No more data can be written to this stream after finishing * can be written to this stream after finishing (calling <code>write</code>
* (calling <code>write</code> with an empty buffer is OK). * with an empty buffer is OK).
* <p> * <p>
* Repeated calls to <code>finish()</code> do nothing unless * Repeated calls to <code>finish()</code> do nothing unless an exception
* an exception was thrown by this stream earlier. In that case * was thrown by this stream earlier. In that case the same exception is
* the same exception is thrown again. * thrown again.
* <p> * <p>
* After finishing, the stream may be closed normally with * After finishing, the stream may be closed normally with
* <code>close()</code>. If the stream will be closed anyway, there * <code>close()</code>. If the stream will be closed anyway, there usually
* usually is no need to call <code>finish()</code> separately. * is no need to call <code>finish()</code> separately.
* *
* @throws XZIOException * @throws XZIOException XZ Stream has grown too big
* XZ Stream has grown too big
* *
* @throws IOException may be thrown by the underlying output stream * @throws IOException may be thrown by the underlying output stream
*/ */
@@ -423,14 +398,13 @@ public class XZOutputStream extends FinishableOutputStream {
} }
/** /**
* Finishes compression and closes the underlying stream. * Finishes compression and closes the underlying stream. The underlying
* The underlying stream <code>out</code> is closed even if finishing * stream <code>out</code> is closed even if finishing fails. If both
* fails. If both finishing and closing fail, the exception thrown * finishing and closing fail, the exception thrown by <code>finish()</code>
* by <code>finish()</code> is thrown and the exception from the failed * is thrown and the exception from the failed <code>out.close()</code> is
* <code>out.close()</code> is lost. * lost.
* *
* @throws XZIOException * @throws XZIOException XZ Stream has grown too big
* XZ Stream has grown too big
* *
* @throws IOException may be thrown by the underlying output stream * @throws IOException may be thrown by the underlying output stream
*/ */

View File

@@ -28,8 +28,8 @@ final class HC4 extends LZEncoder {
} }
/** /**
* Creates a new LZEncoder with the HC4 match finder. * Creates a new LZEncoder with the HC4 match finder. See
* See <code>LZEncoder.getInstance</code> for parameter descriptions. * <code>LZEncoder.getInstance</code> for parameter descriptions.
*/ */
HC4(int dictSize, int beforeSizeMin, int readAheadMax, HC4(int dictSize, int beforeSizeMin, int readAheadMax,
int niceLen, int matchLenMax, int depthLimit) { int niceLen, int matchLenMax, int depthLimit) {
@@ -54,8 +54,8 @@ final class HC4 extends LZEncoder {
} }
/** /**
* Moves to the next byte, checks that there is enough available space, * Moves to the next byte, checks that there is enough available space, and
* and possibly normalizes the hash tables and the hash chain. * possibly normalizes the hash tables and the hash chain.
* *
* @return number of bytes available, including the current byte * @return number of bytes available, including the current byte
*/ */

View File

@@ -18,16 +18,16 @@ public abstract class LZEncoder {
public static final int MF_BT4 = 0x14; public static final int MF_BT4 = 0x14;
/** /**
* Number of bytes to keep available before the current byte * Number of bytes to keep available before the current byte when moving the
* when moving the LZ window. * LZ window.
*/ */
private final int keepSizeBefore; private final int keepSizeBefore;
/** /**
* Number of bytes that must be available, the current byte included, * Number of bytes that must be available, the current byte included, to
* to make hasEnoughData return true. Flushing and finishing are * make hasEnoughData return true. Flushing and finishing are naturally
* naturally exceptions to this since there cannot be any data after * exceptions to this since there cannot be any data after the end of the
* the end of the uncompressed input. * uncompressed input.
*/ */
private final int keepSizeAfter; private final int keepSizeAfter;
@@ -63,8 +63,8 @@ public abstract class LZEncoder {
} }
/** /**
* Gets approximate memory usage of the LZEncoder base structure and * Gets approximate memory usage of the LZEncoder base structure and the
* the match finder as kibibytes. * match finder as kibibytes.
*/ */
public static int getMemoryUsage( public static int getMemoryUsage(
int dictSize, int extraSizeBefore, int extraSizeAfter, int dictSize, int extraSizeBefore, int extraSizeAfter,
@@ -94,17 +94,15 @@ public abstract class LZEncoder {
* <p> * <p>
* @param dictSize dictionary size * @param dictSize dictionary size
* *
* @param extraSizeBefore * @param extraSizeBefore number of bytes to keep available in the history
* number of bytes to keep available in the * in addition to dictSize
* history in addition to dictSize
* *
* @param extraSizeAfter * @param extraSizeAfter number of bytes that must be available after
* number of bytes that must be available * current position + matchLenMax
* after current position + matchLenMax
* *
* @param niceLen if a match of at least <code>niceLen</code> * @param niceLen if a match of at least <code>niceLen</code> bytes
* bytes is found, be happy with it and don't * is
* stop looking for longer matches * found, be happy with it and don't stop looking for longer matches
* *
* @param matchLenMax don't test for matches longer than * @param matchLenMax don't test for matches longer than
* <code>matchLenMax</code> bytes * <code>matchLenMax</code> bytes
@@ -145,9 +143,9 @@ public abstract class LZEncoder {
} }
/** /**
* Sets a preset dictionary. If a preset dictionary is wanted, this * Sets a preset dictionary. If a preset dictionary is wanted, this function
* function must be called immediately after creating the LZEncoder * must be called immediately after creating the LZEncoder before any data
* before any data has been encoded. * has been encoded.
*/ */
public void setPresetDict(int dictSize, byte[] presetDict) { public void setPresetDict(int dictSize, byte[] presetDict) {
assert !isStarted(); assert !isStarted();
@@ -165,8 +163,8 @@ public abstract class LZEncoder {
} }
/** /**
* Moves data from the end of the buffer to the beginning, discarding * Moves data from the end of the buffer to the beginning, discarding old
* old data and making space for new input. * data and making space for new input.
*/ */
private void moveWindow() { private void moveWindow() {
// Align the move to a multiple of 16 bytes. LZMA2 needs this // Align the move to a multiple of 16 bytes. LZMA2 needs this
@@ -212,8 +210,8 @@ public abstract class LZEncoder {
} }
/** /**
* Process pending bytes remaining from preset dictionary initialization * Process pending bytes remaining from preset dictionary initialization or
* or encoder flush operation. * encoder flush operation.
*/ */
private void processPendingBytes() { private void processPendingBytes() {
// After flushing or setting a preset dictionary there will be // After flushing or setting a preset dictionary there will be
@@ -234,16 +232,16 @@ public abstract class LZEncoder {
} }
/** /**
* Returns true if at least one byte has already been run through * Returns true if at least one byte has already been run through the match
* the match finder. * finder.
*/ */
public boolean isStarted() { public boolean isStarted() {
return readPos != -1; return readPos != -1;
} }
/** /**
* Marks that all the input needs to be made available in * Marks that all the input needs to be made available in the encoded
* the encoded output. * output.
*/ */
public void setFlushing() { public void setFlushing() {
readLimit = writePos - 1; readLimit = writePos - 1;
@@ -251,8 +249,8 @@ public abstract class LZEncoder {
} }
/** /**
* Marks that there is no more input remaining. The read position * Marks that there is no more input remaining. The read position can be
* can be advanced until the end of the data. * advanced until the end of the data.
*/ */
public void setFinishing() { public void setFinishing() {
readLimit = writePos - 1; readLimit = writePos - 1;
@@ -261,8 +259,8 @@ public abstract class LZEncoder {
} }
/** /**
* Tests if there is enough input available to let the caller encode * Tests if there is enough input available to let the caller encode at
* at least one more byte. * least one more byte.
*/ */
public boolean hasEnoughData(int alreadyReadLen) { public boolean hasEnoughData(int alreadyReadLen) {
return readPos - alreadyReadLen < readLimit; return readPos - alreadyReadLen < readLimit;
@@ -277,8 +275,8 @@ public abstract class LZEncoder {
* Get the number of bytes available, including the current byte. * Get the number of bytes available, including the current byte.
* <p> * <p>
* Note that the result is undefined if <code>getMatches</code> or * Note that the result is undefined if <code>getMatches</code> or
* <code>skip</code> hasn't been called yet and no preset dictionary * <code>skip</code> hasn't been called yet and no preset dictionary is
* is being used. * being used.
*/ */
public int getAvail() { public int getAvail() {
assert isStarted(); assert isStarted();
@@ -296,9 +294,8 @@ public abstract class LZEncoder {
/** /**
* Gets the byte from the given backward offset. * Gets the byte from the given backward offset.
* <p> * <p>
* The current byte is at <code>0</code>, the previous byte * The current byte is at <code>0</code>, the previous byte at
* at <code>1</code> etc. To get a byte at zero-based distance, * <code>1</code> etc. To get a byte at zero-based distance, use <code>getByte(dist + 1)<code>.
* use <code>getByte(dist + 1)<code>.
* <p> * <p>
* This function is equivalent to <code>getByte(0, backward)</code>. * This function is equivalent to <code>getByte(0, backward)</code>.
*/ */
@@ -307,9 +304,9 @@ public abstract class LZEncoder {
} }
/** /**
* Gets the byte from the given forward minus backward offset. * Gets the byte from the given forward minus backward offset. The forward
* The forward offset is added to the current position. This lets * offset is added to the current position. This lets one read bytes ahead
* one read bytes ahead of the current byte. * of the current byte.
*/ */
public int getByte(int forward, int backward) { public int getByte(int forward, int backward) {
return buf[readPos + forward - backward] & 0xFF; return buf[readPos + forward - backward] & 0xFF;
@@ -354,10 +351,10 @@ public abstract class LZEncoder {
} }
/** /**
* Verifies that the matches returned by the match finder are valid. * Verifies that the matches returned by the match finder are valid. This is
* This is meant to be used in an assert statement. This is totally * meant to be used in an assert statement. This is totally useless for
* useless for actual encoding since match finder's results should * actual encoding since match finder's results should naturally always be
* naturally always be valid if it isn't broken. * valid if it isn't broken.
* *
* @param matches return value from <code>getMatches</code> * @param matches return value from <code>getMatches</code>
* *
@@ -374,21 +371,17 @@ public abstract class LZEncoder {
} }
/** /**
* Moves to the next byte, checks if there is enough input available, * Moves to the next byte, checks if there is enough input available, and
* and returns the amount of input available. * returns the amount of input available.
* *
* @param requiredForFlushing * @param requiredForFlushing minimum number of available bytes when
* minimum number of available bytes when * flushing; encoding may be continued with new input after flushing
* flushing; encoding may be continued with * @param requiredForFinishing minimum number of available bytes when
* new input after flushing * finishing; encoding must not be continued after finishing or the match
* @param requiredForFinishing * finder state may be corrupt
* minimum number of available bytes when
* finishing; encoding must not be continued
* after finishing or the match finder state
* may be corrupt
* *
* @return the number of bytes available or zero if there * @return the number of bytes available or zero if there is not enough
* is not enough input available * input available
*/ */
int movePos(int requiredForFlushing, int requiredForFinishing) { int movePos(int requiredForFlushing, int requiredForFinishing) {
assert requiredForFlushing >= requiredForFinishing; assert requiredForFlushing >= requiredForFinishing;

View File

@@ -37,10 +37,10 @@ public final class LZMADecoder extends LZMACoder {
} }
/** /**
* Returns true if LZMA end marker was detected. It is encoded as * Returns true if LZMA end marker was detected. It is encoded as the
* the maximum match distance which with signed ints becomes -1. This * maximum match distance which with signed ints becomes -1. This function
* function is needed only for LZMA1. LZMA2 doesn't use the end marker * is needed only for LZMA1. LZMA2 doesn't use the end marker in the LZMA
* in the LZMA layer. * layer.
*/ */
public boolean endMarkerDetected() { public boolean endMarkerDetected() {
return reps[0] == -1; return reps[0] == -1;

View File

@@ -22,10 +22,10 @@ public abstract class LZMAEncoder extends LZMACoder {
* LZMA2 chunk is considered full when its uncompressed size exceeds * LZMA2 chunk is considered full when its uncompressed size exceeds
* <code>LZMA2_UNCOMPRESSED_LIMIT</code>. * <code>LZMA2_UNCOMPRESSED_LIMIT</code>.
* <p> * <p>
* A compressed LZMA2 chunk can hold 2 MiB of uncompressed data. * A compressed LZMA2 chunk can hold 2 MiB of uncompressed data. A single
* A single LZMA symbol may indicate up to MATCH_LEN_MAX bytes * LZMA symbol may indicate up to MATCH_LEN_MAX bytes of data, so the LZMA2
* of data, so the LZMA2 chunk is considered full when there is * chunk is considered full when there is less space than MATCH_LEN_MAX
* less space than MATCH_LEN_MAX bytes. * bytes.
*/ */
private static final int LZMA2_UNCOMPRESSED_LIMIT private static final int LZMA2_UNCOMPRESSED_LIMIT
= (2 << 20) - MATCH_LEN_MAX; = (2 << 20) - MATCH_LEN_MAX;
@@ -34,11 +34,11 @@ public abstract class LZMAEncoder extends LZMACoder {
* LZMA2 chunk is considered full when its compressed size exceeds * LZMA2 chunk is considered full when its compressed size exceeds
* <code>LZMA2_COMPRESSED_LIMIT</code>. * <code>LZMA2_COMPRESSED_LIMIT</code>.
* <p> * <p>
* The maximum compressed size of a LZMA2 chunk is 64 KiB. * The maximum compressed size of a LZMA2 chunk is 64 KiB. A single LZMA
* A single LZMA symbol might use 20 bytes of space even though * symbol might use 20 bytes of space even though it usually takes just one
* it usually takes just one byte or so. Two more bytes are needed * byte or so. Two more bytes are needed for LZMA2 uncompressed chunks (see
* for LZMA2 uncompressed chunks (see LZMA2OutputStream.writeChunk). * LZMA2OutputStream.writeChunk). Leave a little safety margin and use 26
* Leave a little safety margin and use 26 bytes. * bytes.
*/ */
private static final int LZMA2_COMPRESSED_LIMIT = (64 << 10) - 26; private static final int LZMA2_COMPRESSED_LIMIT = (64 << 10) - 26;
@@ -107,9 +107,9 @@ public abstract class LZMAEncoder extends LZMACoder {
} }
/** /**
* Gets an integer [0, 63] matching the highest two bits of an integer. * Gets an integer [0, 63] matching the highest two bits of an integer. This
* This is like bit scan reverse (BSR) on x86 except that this also * is like bit scan reverse (BSR) on x86 except that this also cares about
* cares about the second highest bit. * the second highest bit.
*/ */
public static int getDistSlot(int dist) { public static int getDistSlot(int dist) {
if (dist <= DIST_MODEL_START) if (dist <= DIST_MODEL_START)
@@ -147,19 +147,18 @@ public abstract class LZMAEncoder extends LZMACoder {
/** /**
* Gets the next LZMA symbol. * Gets the next LZMA symbol.
* <p> * <p>
* There are three types of symbols: literal (a single byte), * There are three types of symbols: literal (a single byte), repeated
* repeated match, and normal match. The symbol is indicated * match, and normal match. The symbol is indicated by the return value and
* by the return value and by the variable <code>back</code>. * by the variable <code>back</code>.
* <p> * <p>
* Literal: <code>back == -1</code> and return value is <code>1</code>. * Literal: <code>back == -1</code> and return value is <code>1</code>. The
* The literal itself needs to be read from <code>lz</code> separately. * literal itself needs to be read from <code>lz</code> separately.
* <p> * <p>
* Repeated match: <code>back</code> is in the range [0, 3] and * Repeated match: <code>back</code> is in the range [0, 3] and the return
* the return value is the length of the repeated match. * value is the length of the repeated match.
* <p> * <p>
* Normal match: <code>back - REPS<code> (<code>back - 4</code>) * Normal match: <code>back - REPS<code> (<code>back - 4</code>) is the
* is the distance of the match and the return value is the length * distance of the match and the return value is the length of the match.
* of the match.
*/ */
abstract int getNextSymbol(); abstract int getNextSymbol();
@@ -477,9 +476,9 @@ public abstract class LZMAEncoder extends LZMACoder {
} }
/** /**
* Updates the lookup tables used for calculating match distance * Updates the lookup tables used for calculating match distance and length
* and length prices. The updating is skipped for performance reasons * prices. The updating is skipped for performance reasons if the tables
* if the tables haven't changed much since the previous update. * haven't changed much since the previous update.
*/ */
void updatePrices() { void updatePrices() {
if (distPriceCount <= 0) if (distPriceCount <= 0)
@@ -621,8 +620,8 @@ public abstract class LZMAEncoder extends LZMACoder {
/** /**
* The prices are updated after at least * The prices are updated after at least
* <code>PRICE_UPDATE_INTERVAL</code> many lengths * <code>PRICE_UPDATE_INTERVAL</code> many lengths have been encoded
* have been encoded with the same posState. * with the same posState.
*/ */
private static final int PRICE_UPDATE_INTERVAL = 32; // FIXME? private static final int PRICE_UPDATE_INTERVAL = 32; // FIXME?

View File

@@ -60,9 +60,9 @@ final class LZMAEncoderNormal extends LZMAEncoder {
} }
/** /**
* Converts the opts array from backward indexes to forward indexes. * Converts the opts array from backward indexes to forward indexes. Then it
* Then it will be simple to get the next symbol from the array * will be simple to get the next symbol from the array in later calls to
* in later calls to <code>getNextSymbol()</code>. * <code>getNextSymbol()</code>.
*/ */
private int convertOpts() { private int convertOpts() {
optEnd = optCur; optEnd = optCur;

View File

@@ -58,8 +58,7 @@ final class Optimum {
} }
/** /**
* Sets to indicate three LZMA symbols of which the second one * Sets to indicate three LZMA symbols of which the second one is a literal.
* is a literal.
*/ */
void set3(int newPrice, int optCur, int back2, int len2, int back) { void set3(int newPrice, int optCur, int back2, int len2, int back) {
price = newPrice; price = newPrice;

View File

@@ -3,14 +3,14 @@
* *
* <h4>Introduction</h4> * <h4>Introduction</h4>
* <p> * <p>
* This aims to be a complete implementation of XZ data compression * This aims to be a complete implementation of XZ data compression in pure
* in pure Java. Features: * Java. Features:
* <ul> * <ul>
* <li>Full support for the .xz file format specification version 1.0.4</li> * <li>Full support for the .xz file format specification version 1.0.4</li>
* <li>Single-threaded streamed compression and decompression</li> * <li>Single-threaded streamed compression and decompression</li>
* <li>Single-threaded decompression with limited random access support</li> * <li>Single-threaded decompression with limited random access support</li>
* <li>Raw streams (no .xz headers) for advanced users, including LZMA2 * <li>Raw streams (no .xz headers) for advanced users, including LZMA2 with
* with preset dictionary</li> * preset dictionary</li>
* </ul> * </ul>
* <p> * <p>
* Threading is planned but it is unknown when it will be implemented. * Threading is planned but it is unknown when it will be implemented.
@@ -21,15 +21,14 @@
* <h4>Getting started</h4> * <h4>Getting started</h4>
* <p> * <p>
* Start by reading the documentation of {@link org.tukaani.xz.XZOutputStream} * Start by reading the documentation of {@link org.tukaani.xz.XZOutputStream}
* and {@link org.tukaani.xz.XZInputStream}. * and {@link org.tukaani.xz.XZInputStream}. If you use XZ inside another file
* If you use XZ inside another file format or protocol, * format or protocol, see also {@link org.tukaani.xz.SingleXZInputStream}.
* see also {@link org.tukaani.xz.SingleXZInputStream}.
* *
* <h4>Licensing</h4> * <h4>Licensing</h4>
* <p> * <p>
* XZ for Java has been put into the public domain, thus you can do * XZ for Java has been put into the public domain, thus you can do whatever you
* whatever you want with it. All the files in the package have been * want with it. All the files in the package have been written by Lasse Collin
* written by Lasse Collin and/or Igor Pavlov. * and/or Igor Pavlov.
* <p> * <p>
* This software is provided "as is", without any warranty. * This software is provided "as is", without any warranty.
*/ */

File diff suppressed because it is too large Load Diff

View File

@@ -18,27 +18,35 @@ package rx;
/** /**
* Provides a mechanism for receiving push-based notifications. * Provides a mechanism for receiving push-based notifications.
* <p> * <p>
* After an Observer calls an {@link Observable}'s <code>Observable.subscribe</code> method, the {@link Observable} calls the Observer's <code>onNext</code> method to provide notifications. A * After an Observer calls an {@link Observable}'s
* well-behaved {@link Observable} will * <code>Observable.subscribe</code> method, the {@link Observable} calls the
* call an Observer's <code>onCompleted</code> closure exactly once or the Observer's <code>onError</code> closure exactly once. * Observer's <code>onNext</code> method to provide notifications. A
* well-behaved {@link Observable} will call an Observer's
* <code>onCompleted</code> closure exactly once or the Observer's
* <code>onError</code> closure exactly once.
* <p> * <p>
* For more information see the <a href="https://github.com/Netflix/RxJava/wiki/Observable">RxJava Wiki</a> * For more information see the
* <a href="https://github.com/Netflix/RxJava/wiki/Observable">RxJava Wiki</a>
* *
* @param <T> * @param <T>
*/ */
public interface Observer<T> { public interface Observer<T> {
/** /**
* Notifies the Observer that the {@link Observable} has finished sending push-based notifications. * Notifies the Observer that the {@link Observable} has finished sending
* push-based notifications.
* <p> * <p>
* The {@link Observable} will not call this closure if it calls <code>onError</code>. * The {@link Observable} will not call this closure if it calls
* <code>onError</code>.
*/ */
public void onCompleted(); public void onCompleted();
/** /**
* Notifies the Observer that the {@link Observable} has experienced an error condition. * Notifies the Observer that the {@link Observable} has experienced an
* error condition.
* <p> * <p>
* If the {@link Observable} calls this closure, it will not thereafter call <code>onNext</code> or <code>onCompleted</code>. * If the {@link Observable} calls this closure, it will not thereafter call
* <code>onNext</code> or <code>onCompleted</code>.
* *
* @param e * @param e
*/ */
@@ -47,9 +55,12 @@ public interface Observer<T> {
/** /**
* Provides the Observer with new data. * Provides the Observer with new data.
* <p> * <p>
* The {@link Observable} calls this closure 1 or more times, unless it calls <code>onError</code> in which case this closure may never be called. * The {@link Observable} calls this closure 1 or more times, unless it
* calls <code>onError</code> in which case this closure may never be
* called.
* <p> * <p>
* The {@link Observable} will not call this closure again after it calls either <code>onCompleted</code> or <code>onError</code>. * The {@link Observable} will not call this closure again after it calls
* either <code>onCompleted</code> or <code>onError</code>.
* *
* @param args * @param args
*/ */

View File

@@ -28,8 +28,8 @@ public interface Scheduler {
/** /**
* Schedules a cancelable action to be executed. * Schedules a cancelable action to be executed.
* *
* @param action * @param action action
* action *
* @return a subscription to be able to unsubscribe from action. * @return a subscription to be able to unsubscribe from action.
*/ */
Subscription schedule(Func0<Subscription> action); Subscription schedule(Func0<Subscription> action);
@@ -37,8 +37,8 @@ public interface Scheduler {
/** /**
* Schedules an action to be executed. * Schedules an action to be executed.
* *
* @param action * @param action action
* action *
* @return a subscription to be able to unsubscribe from action. * @return a subscription to be able to unsubscribe from action.
*/ */
Subscription schedule(Action0 action); Subscription schedule(Action0 action);
@@ -46,8 +46,8 @@ public interface Scheduler {
/** /**
* Schedules an action to be executed in dueTime. * Schedules an action to be executed in dueTime.
* *
* @param action * @param action action
* action *
* @return a subscription to be able to unsubscribe from action. * @return a subscription to be able to unsubscribe from action.
*/ */
Subscription schedule(Action0 action, long dueTime, TimeUnit unit); Subscription schedule(Action0 action, long dueTime, TimeUnit unit);
@@ -55,8 +55,8 @@ public interface Scheduler {
/** /**
* Schedules a cancelable action to be executed in dueTime. * Schedules a cancelable action to be executed in dueTime.
* *
* @param action * @param action action
* action *
* @return a subscription to be able to unsubscribe from action. * @return a subscription to be able to unsubscribe from action.
*/ */
Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit); Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit);

View File

@@ -18,16 +18,20 @@ package rx;
import rx.subscriptions.Subscriptions; import rx.subscriptions.Subscriptions;
/** /**
* Subscription returns from {@link Observable#subscribe(Observer)} to allow unsubscribing. * Subscription returns from {@link Observable#subscribe(Observer)} to allow
* unsubscribing.
* <p> * <p>
* See utilities in {@link Subscriptions} and implementations in the {@link rx.subscriptions} package. * See utilities in {@link Subscriptions} and implementations in the
* {@link rx.subscriptions} package.
*/ */
public interface Subscription { public interface Subscription {
/** /**
* Stop receiving notifications on the {@link Observer} that was registered when this Subscription was received. * Stop receiving notifications on the {@link Observer} that was registered
* when this Subscription was received.
* <p> * <p>
* This allows unregistering an {@link Observer} before it has finished receiving all events (ie. before onCompleted is called). * This allows unregistering an {@link Observer} before it has finished
* receiving all events (ie. before onCompleted is called).
*/ */
public void unsubscribe(); public void unsubscribe();

View File

@@ -23,7 +23,9 @@ import rx.subscriptions.Subscriptions;
import rx.util.functions.Action0; import rx.util.functions.Action0;
import rx.util.functions.Func0; import rx.util.functions.Func0;
/* package */ /*
* package
*/
abstract class AbstractScheduler implements Scheduler { abstract class AbstractScheduler implements Scheduler {
@Override @Override

View File

@@ -23,9 +23,11 @@ import rx.Subscription;
import rx.util.functions.Func0; import rx.util.functions.Func0;
/** /**
* Schedules work on the current thread but does not execute immediately. Work is put in a queue and executed after the current unit of work is completed. * Schedules work on the current thread but does not execute immediately. Work
* is put in a queue and executed after the current unit of work is completed.
*/ */
public class CurrentThreadScheduler extends AbstractScheduler { public class CurrentThreadScheduler extends AbstractScheduler {
private static final CurrentThreadScheduler INSTANCE = new CurrentThreadScheduler(); private static final CurrentThreadScheduler INSTANCE = new CurrentThreadScheduler();
public static CurrentThreadScheduler getInstance() { public static CurrentThreadScheduler getInstance() {
@@ -61,9 +63,8 @@ public class CurrentThreadScheduler extends AbstractScheduler {
queue.add(action); queue.add(action);
if (exec) { if (exec) {
while (!queue.isEmpty()) { while (!queue.isEmpty())
queue.poll().call(); queue.poll().call();
}
QUEUE.set(null); QUEUE.set(null);
} }

View File

@@ -22,9 +22,15 @@ import rx.util.AtomicObservableSubscription;
import rx.util.functions.Func0; import rx.util.functions.Func0;
/** /**
* Combines standard {@link Subscription#unsubscribe()} functionality with ability to skip execution if an unsubscribe occurs before the {@link #call()} method is invoked. * Combines standard {@link Subscription#unsubscribe()} functionality with
* ability to skip execution if an unsubscribe occurs before the {@link #call()}
* method is invoked.
*/ */
/* package */class DiscardableAction implements Func0<Subscription>, Subscription { /*
* package
*/
class DiscardableAction implements Func0<Subscription>, Subscription {
private final Func0<Subscription> underlying; private final Func0<Subscription> underlying;
private final AtomicObservableSubscription wrapper = new AtomicObservableSubscription(); private final AtomicObservableSubscription wrapper = new AtomicObservableSubscription();

View File

@@ -27,6 +27,7 @@ import rx.util.functions.Func0;
* @author huangyuhui * @author huangyuhui
*/ */
public class EventQueueScheduler extends AbstractScheduler { public class EventQueueScheduler extends AbstractScheduler {
private static final EventQueueScheduler INSTANCE = new EventQueueScheduler(); private static final EventQueueScheduler INSTANCE = new EventQueueScheduler();
public static EventQueueScheduler getInstance() { public static EventQueueScheduler getInstance() {

View File

@@ -24,6 +24,7 @@ import rx.util.functions.Func0;
* Executes work immediately on the current thread. * Executes work immediately on the current thread.
*/ */
public final class ImmediateScheduler extends AbstractScheduler { public final class ImmediateScheduler extends AbstractScheduler {
private static final ImmediateScheduler INSTANCE = new ImmediateScheduler(); private static final ImmediateScheduler INSTANCE = new ImmediateScheduler();
public static ImmediateScheduler getInstance() { public static ImmediateScheduler getInstance() {

View File

@@ -24,6 +24,7 @@ import rx.util.functions.Func0;
* Schedules work on a new thread. * Schedules work on a new thread.
*/ */
public class NewThreadScheduler extends AbstractScheduler { public class NewThreadScheduler extends AbstractScheduler {
private static final NewThreadScheduler INSTANCE = new NewThreadScheduler(); private static final NewThreadScheduler INSTANCE = new NewThreadScheduler();
public static NewThreadScheduler getInstance() { public static NewThreadScheduler getInstance() {

View File

@@ -28,6 +28,7 @@ import rx.Scheduler;
* Static factory methods for creating Schedulers. * Static factory methods for creating Schedulers.
*/ */
public class Schedulers { public class Schedulers {
private static final ScheduledExecutorService COMPUTATION_EXECUTOR = createComputationExecutor(); private static final ScheduledExecutorService COMPUTATION_EXECUTOR = createComputationExecutor();
private static final Executor IO_EXECUTOR = createIOExecutor(); private static final Executor IO_EXECUTOR = createIOExecutor();
@@ -45,7 +46,8 @@ public class Schedulers {
} }
/** /**
* {@link Scheduler} that queues work on the current thread to be executed after the current work completes. * {@link Scheduler} that queues work on the current thread to be executed
* after the current work completes.
* *
* @return {@link CurrentThreadScheduler} instance * @return {@link CurrentThreadScheduler} instance
*/ */
@@ -54,7 +56,8 @@ public class Schedulers {
} }
/** /**
* {@link Scheduler} that creates a new {@link Thread} for each unit of work. * {@link Scheduler} that creates a new {@link Thread} for each unit of
* work.
* *
* @return {@link NewThreadScheduler} instance * @return {@link NewThreadScheduler} instance
*/ */
@@ -63,7 +66,8 @@ public class Schedulers {
} }
/** /**
* {@link Scheduler} that queues work on the EventQueue thread to be executed on the Swing UI Thread. * {@link Scheduler} that queues work on the EventQueue thread to be
* executed on the Swing UI Thread.
* *
* @return {@link NewThreadScheduler} instance * @return {@link NewThreadScheduler} instance
*/ */
@@ -83,7 +87,8 @@ public class Schedulers {
} }
/** /**
* {@link Scheduler} that queues work on an {@link ScheduledExecutorService}. * {@link Scheduler} that queues work on an
* {@link ScheduledExecutorService}.
* *
* @return {@link ExecutorScheduler} instance * @return {@link ExecutorScheduler} instance
*/ */
@@ -94,11 +99,14 @@ public class Schedulers {
/** /**
* {@link Scheduler} intended for computational work. * {@link Scheduler} intended for computational work.
* <p> * <p>
* The implementation is backed by a {@link ScheduledExecutorService} thread-pool sized to the number of CPU cores. * The implementation is backed by a {@link ScheduledExecutorService}
* thread-pool sized to the number of CPU cores.
* <p> * <p>
* This can be used for event-loops, processing callbacks and other computational work. * This can be used for event-loops, processing callbacks and other
* computational work.
* <p> * <p>
* Do not perform IO-bound work on this scheduler. Use {@link #threadPoolForComputation()} instead. * Do not perform IO-bound work on this scheduler. Use
* {@link #threadPoolForComputation()} instead.
* *
* @return {@link ExecutorScheduler} for computation-bound work. * @return {@link ExecutorScheduler} for computation-bound work.
*/ */
@@ -109,11 +117,13 @@ public class Schedulers {
/** /**
* {@link Scheduler} intended for IO-bound work. * {@link Scheduler} intended for IO-bound work.
* <p> * <p>
* The implementation is backed by an {@link Executor} thread-pool that will grow as needed. * The implementation is backed by an {@link Executor} thread-pool that will
* grow as needed.
* <p> * <p>
* This can be used for asynchronously performing blocking IO. * This can be used for asynchronously performing blocking IO.
* <p> * <p>
* Do not perform computational work on this scheduler. Use {@link #threadPoolForComputation()} instead. * Do not perform computational work on this scheduler. Use
* {@link #threadPoolForComputation()} instead.
* *
* @return {@link ExecutorScheduler} for IO-bound work. * @return {@link ExecutorScheduler} for IO-bound work.
*/ */

View File

@@ -21,7 +21,11 @@ import rx.Scheduler;
import rx.Subscription; import rx.Subscription;
import rx.util.functions.Func0; import rx.util.functions.Func0;
/* package */class SleepingAction implements Func0<Subscription> { /*
* package
*/
class SleepingAction implements Func0<Subscription> {
private final Func0<Subscription> underlying; private final Func0<Subscription> underlying;
private final Scheduler scheduler; private final Scheduler scheduler;
private final long execTime; private final long execTime;
@@ -34,14 +38,13 @@ import rx.util.functions.Func0;
@Override @Override
public Subscription call() { public Subscription call() {
if (execTime < scheduler.now()) { if (execTime < scheduler.now())
try { try {
Thread.sleep(scheduler.now() - execTime); Thread.sleep(scheduler.now() - execTime);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}
return underlying.call(); return underlying.call();

View File

@@ -21,7 +21,9 @@ import rx.Subscription;
import rx.util.functions.Func1; import rx.util.functions.Func1;
/** /**
* An {@link Observable} that has been grouped by a key whose value can be obtained using {@link #getKey()} <p> * An {@link Observable} that has been grouped by a key whose value can be
* obtained using {@link #getKey()}
* <p>
* *
* @see {@link Observable#groupBy(Observable, Func1)} * @see {@link Observable#groupBy(Observable, Func1)}
* *
@@ -29,6 +31,7 @@ import rx.util.functions.Func1;
* @param <T> * @param <T>
*/ */
public class GroupedObservable<K, T> extends Observable<T> { public class GroupedObservable<K, T> extends Observable<T> {
private final K key; private final K key;
public GroupedObservable(K key, Func1<Observer<T>, Subscription> onSubscribe) { public GroupedObservable(K key, Func1<Observer<T>, Subscription> onSubscribe) {

View File

@@ -15,19 +15,18 @@ public class OperationAll {
} }
private static class AllObservable<T> implements Func1<Observer<Boolean>, Subscription> { private static class AllObservable<T> implements Func1<Observer<Boolean>, Subscription> {
private final Observable<T> sequence; private final Observable<T> sequence;
private final Func1<T, Boolean> predicate; private final Func1<T, Boolean> predicate;
private final AtomicBoolean status = new AtomicBoolean(true); private final AtomicBoolean status = new AtomicBoolean(true);
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
private AllObservable(Observable<T> sequence, Func1<T, Boolean> predicate) { private AllObservable(Observable<T> sequence, Func1<T, Boolean> predicate) {
this.sequence = sequence; this.sequence = sequence;
this.predicate = predicate; this.predicate = predicate;
} }
@Override @Override
public Subscription call(final Observer<Boolean> observer) { public Subscription call(final Observer<Boolean> observer) {
return subscription.wrap(sequence.subscribe(new Observer<T>() { return subscription.wrap(sequence.subscribe(new Observer<T>() {

View File

@@ -36,12 +36,18 @@ import rx.util.functions.Functions;
public class OperationCombineLatest { public class OperationCombineLatest {
/** /**
* Combines the two given observables, emitting an event containing an aggregation of the latest values of each of the source observables * Combines the two given observables, emitting an event containing an
* each time an event is received from one of the source observables, where the aggregation is defined by the given function. * aggregation of the latest values of each of the source observables each
* time an event is received from one of the source observables, where the
* aggregation is defined by the given function.
*
* @param w0 The first source observable. * @param w0 The first source observable.
* @param w1 The second source observable. * @param w1 The second source observable.
* @param combineLatestFunction The aggregation function used to combine the source observable values. * @param combineLatestFunction The aggregation function used to combine the
* @return A function from an observer to a subscription. This can be used to create an observable from. * source observable values.
*
* @return A function from an observer to a subscription. This can be used
* to create an observable from.
*/ */
public static <T0, T1, R> Func1<Observer<R>, Subscription> combineLatest(Observable<T0> w0, Observable<T1> w1, Func2<T0, T1, R> combineLatestFunction) { public static <T0, T1, R> Func1<Observer<R>, Subscription> combineLatest(Observable<T0> w0, Observable<T1> w1, Func2<T0, T1, R> combineLatestFunction) {
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction)); Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction));
@@ -51,7 +57,8 @@ public class OperationCombineLatest {
} }
/** /**
* @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) * @see #combineLatest(Observable w0, Observable w1, Func2
* combineLatestFunction)
*/ */
public static <T0, T1, T2, R> Func1<Observer<R>, Subscription> combineLatest(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Func3<T0, T1, T2, R> combineLatestFunction) { public static <T0, T1, T2, R> Func1<Observer<R>, Subscription> combineLatest(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Func3<T0, T1, T2, R> combineLatestFunction) {
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction)); Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction));
@@ -62,7 +69,8 @@ public class OperationCombineLatest {
} }
/** /**
* @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) * @see #combineLatest(Observable w0, Observable w1, Func2
* combineLatestFunction)
*/ */
public static <T0, T1, T2, T3, R> Func1<Observer<R>, Subscription> combineLatest(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Observable<T3> w3, Func4<T0, T1, T2, T3, R> combineLatestFunction) { public static <T0, T1, T2, T3, R> Func1<Observer<R>, Subscription> combineLatest(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Observable<T3> w3, Func4<T0, T1, T2, T3, R> combineLatestFunction) {
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction)); Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction));
@@ -74,6 +82,7 @@ public class OperationCombineLatest {
} }
private static class CombineObserver<R, T> implements Observer<T> { private static class CombineObserver<R, T> implements Observer<T> {
final Observable<T> w; final Observable<T> w;
final Aggregator<R> a; final Aggregator<R> a;
private Subscription subscription; private Subscription subscription;
@@ -84,9 +93,8 @@ public class OperationCombineLatest {
} }
public synchronized void startWatching() { public synchronized void startWatching() {
if (subscription != null) { if (subscription != null)
throw new RuntimeException("This should only be called once."); throw new RuntimeException("This should only be called once.");
}
subscription = w.subscribe(this); subscription = w.subscribe(this);
} }
@@ -107,9 +115,10 @@ public class OperationCombineLatest {
} }
/** /**
* Receive notifications from each of the observables we are reducing and execute the combineLatestFunction * Receive notifications from each of the observables we are reducing and
* whenever we have received an event from one of the observables, as soon as each Observable has received * execute the combineLatestFunction whenever we have received an event from
* at least one event. * one of the observables, as soon as each Observable has received at least
* one event.
*/ */
private static class Aggregator<R> implements Func1<Observer<R>, Subscription> { private static class Aggregator<R> implements Func1<Observer<R>, Subscription> {
@@ -124,27 +133,33 @@ public class OperationCombineLatest {
/** /**
* Store when an observer completes. * Store when an observer completes.
* <p> * <p>
* Note that access to this set MUST BE SYNCHRONIZED via 'lockObject' above. * Note that access to this set MUST BE SYNCHRONIZED via 'lockObject'
* */ * above.
*
*/
private final Set<CombineObserver<R, ?>> completed = new HashSet<>(); private final Set<CombineObserver<R, ?>> completed = new HashSet<>();
/** /**
* The latest value from each observer * The latest value from each observer
* <p> * <p>
* Note that access to this set MUST BE SYNCHRONIZED via 'lockObject' above. * Note that access to this set MUST BE SYNCHRONIZED via 'lockObject'
* */ * above.
*
*/
private final Map<CombineObserver<R, ?>, Object> latestValue = new HashMap<>(); private final Map<CombineObserver<R, ?>, Object> latestValue = new HashMap<>();
/** /**
* Whether each observer has a latest value at all. * Whether each observer has a latest value at all.
* <p> * <p>
* Note that access to this set MUST BE SYNCHRONIZED via 'lockObject' above. * Note that access to this set MUST BE SYNCHRONIZED via 'lockObject'
* */ * above.
*
*/
private final Set<CombineObserver<R, ?>> hasLatestValue = new HashSet<>(); private final Set<CombineObserver<R, ?>> hasLatestValue = new HashSet<>();
/** /**
* Ordered list of observers to combine. * Ordered list of observers to combine. No synchronization is necessary
* No synchronization is necessary as these can not be added or changed asynchronously. * as these can not be added or changed asynchronously.
*/ */
private final List<CombineObserver<R, ?>> observers = new LinkedList<>(); private final List<CombineObserver<R, ?>> observers = new LinkedList<>();
@@ -153,7 +168,8 @@ public class OperationCombineLatest {
} }
/** /**
* Receive notification of a Observer starting (meaning we should require it for aggregation) * Receive notification of a Observer starting (meaning we should
* require it for aggregation)
* *
* @param w The observer to add. * @param w The observer to add.
*/ */
@@ -171,7 +187,7 @@ public class OperationCombineLatest {
// store that this CombineLatestObserver is completed // store that this CombineLatestObserver is completed
completed.add(w); completed.add(w);
// if all CombineObservers are completed, we mark the whole thing as completed // if all CombineObservers are completed, we mark the whole thing as completed
if (completed.size() == observers.size()) { if (completed.size() == observers.size())
if (running.get()) { if (running.get()) {
// mark ourselves as done // mark ourselves as done
observer.onCompleted(); observer.onCompleted();
@@ -180,34 +196,39 @@ public class OperationCombineLatest {
} }
} }
} }
}
/** /**
* Receive error for a Observer. Throw the error up the chain and stop processing. * Receive error for a Observer. Throw the error up the chain and stop
* processing.
*/ */
void error(Exception e) { void error(Exception e) {
observer.onError(e); observer.onError(e);
/* tell all observers to unsubscribe since we had an error */ /*
* tell all observers to unsubscribe since we had an error
*/
stop(); stop();
} }
/** /**
* Receive the next value from an observer. * Receive the next value from an observer.
* <p> * <p>
* If we have received values from all observers, trigger the combineLatest function, otherwise store the value and keep waiting. * If we have received values from all observers, trigger the
* combineLatest function, otherwise store the value and keep waiting.
* *
* @param w * @param w
* @param arg * @param arg
*/ */
<T> void next(CombineObserver<R, T> w, T arg) { <T> void next(CombineObserver<R, T> w, T arg) {
if (observer == null) { if (observer == null)
throw new RuntimeException("This shouldn't be running if an Observer isn't registered"); throw new RuntimeException("This shouldn't be running if an Observer isn't registered");
}
/* if we've been 'unsubscribed' don't process anything further even if the things we're watching keep sending (likely because they are not responding to the unsubscribe call) */ /*
if (!running.get()) { * if we've been 'unsubscribed' don't process anything further even
* if the things we're watching keep sending (likely because they
* are not responding to the unsubscribe call)
*/
if (!running.get())
return; return;
}
// define here so the variable is out of the synchronized scope // define here so the variable is out of the synchronized scope
Object[] argsToCombineLatest = new Object[observers.size()]; Object[] argsToCombineLatest = new Object[observers.size()];
@@ -221,18 +242,15 @@ public class OperationCombineLatest {
hasLatestValue.add(w); hasLatestValue.add(w);
// if all observers in the 'observers' list have a value, invoke the combineLatestFunction // if all observers in the 'observers' list have a value, invoke the combineLatestFunction
for (CombineObserver<R, ?> rw : observers) { for (CombineObserver<R, ?> rw : observers)
if (!hasLatestValue.contains(rw)) { if (!hasLatestValue.contains(rw))
// we don't have a value yet for each observer to combine, so we don't have a combined value yet either // we don't have a value yet for each observer to combine, so we don't have a combined value yet either
return; return;
}
}
// if we get to here this means all the queues have data // if we get to here this means all the queues have data
int i = 0; int i = 0;
for (CombineObserver<R, ?> _w : observers) { for (CombineObserver<R, ?> _w : observers)
argsToCombineLatest[i++] = latestValue.get(_w); argsToCombineLatest[i++] = latestValue.get(_w);
} }
}
// if we did not return above from the synchronized block we can now invoke the combineLatestFunction with all of the args // if we did not return above from the synchronized block we can now invoke the combineLatestFunction with all of the args
// we do this outside the synchronized block as it is now safe to call this concurrently and don't need to block other threads from calling // we do this outside the synchronized block as it is now safe to call this concurrently and don't need to block other threads from calling
// this 'next' method while another thread finishes calling this combineLatestFunction // this 'next' method while another thread finishes calling this combineLatestFunction
@@ -241,15 +259,15 @@ public class OperationCombineLatest {
@Override @Override
public Subscription call(Observer<R> observer) { public Subscription call(Observer<R> observer) {
if (this.observer != null) { if (this.observer != null)
throw new IllegalStateException("Only one Observer can subscribe to this Observable."); throw new IllegalStateException("Only one Observer can subscribe to this Observable.");
}
this.observer = observer; this.observer = observer;
/* start the observers */ /*
for (CombineObserver<R, ?> rw : observers) { * start the observers
*/
for (CombineObserver<R, ?> rw : observers)
rw.startWatching(); rw.startWatching();
}
return new Subscription() { return new Subscription() {
@Override @Override
@@ -260,14 +278,16 @@ public class OperationCombineLatest {
} }
private void stop() { private void stop() {
/* tell ourselves to stop processing onNext events */ /*
* tell ourselves to stop processing onNext events
*/
running.set(false); running.set(false);
/* propogate to all observers to unsubscribe */ /*
for (CombineObserver<R, ?> rw : observers) { * propogate to all observers to unsubscribe
if (rw.subscription != null) { */
for (CombineObserver<R, ?> rw : observers)
if (rw.subscription != null)
rw.subscription.unsubscribe(); rw.subscription.unsubscribe();
} }
} }
} }
}
}

View File

@@ -30,11 +30,13 @@ import rx.util.functions.Func1;
public final class OperationConcat { public final class OperationConcat {
/** /**
* Combine the observable sequences from the list of Observables into one observable sequence without any transformation. * Combine the observable sequences from the list of Observables into one
* observable sequence without any transformation.
* *
* @param sequences * @param sequences An observable sequence of elements to project.
* An observable sequence of elements to project. *
* @return An observable sequence whose elements are the result of combining the output from the list of Observables. * @return An observable sequence whose elements are the result of combining
* the output from the list of Observables.
*/ */
public static <T> Func1<Observer<T>, Subscription> concat(final Observable<T>... sequences) { public static <T> Func1<Observer<T>, Subscription> concat(final Observable<T>... sequences) {
return new Func1<Observer<T>, Subscription>() { return new Func1<Observer<T>, Subscription>() {
@@ -66,6 +68,7 @@ public final class OperationConcat {
} }
private static class Concat<T> implements Func1<Observer<T>, Subscription> { private static class Concat<T> implements Func1<Observer<T>, Subscription> {
private final Observable<T>[] sequences; private final Observable<T>[] sequences;
private int num = 0; private int num = 0;
private int count = 0; private int count = 0;
@@ -94,6 +97,7 @@ public final class OperationConcat {
} }
private class ConcatObserver implements Observer<T> { private class ConcatObserver implements Observer<T> {
private final Observer<T> observer; private final Observer<T> observer;
ConcatObserver(Observer<T> observer) { ConcatObserver(Observer<T> observer) {

View File

@@ -22,17 +22,23 @@ import rx.Subscription;
import rx.util.functions.Func1; import rx.util.functions.Func1;
/** /**
* Dematerializes the explicit notification values of an observable sequence as implicit notifications. * Dematerializes the explicit notification values of an observable sequence as
* See http://msdn.microsoft.com/en-us/library/hh229047(v=vs.103).aspx for the Microsoft Rx equivalent. * implicit notifications. See
* http://msdn.microsoft.com/en-us/library/hh229047(v=vs.103).aspx for the
* Microsoft Rx equivalent.
*/ */
public final class OperationDematerialize { public final class OperationDematerialize {
/** /**
* Dematerializes the explicit notification values of an observable sequence as implicit notifications. * Dematerializes the explicit notification values of an observable sequence
* as implicit notifications.
*
* @param sequence An observable sequence containing explicit notification
* values which have to be turned into implicit notifications.
*
* @return An observable sequence exhibiting the behavior corresponding to
* the source sequence's notification values.
* *
* @param sequence
* An observable sequence containing explicit notification values which have to be turned into implicit notifications.
* @return An observable sequence exhibiting the behavior corresponding to the source sequence's notification values.
* @see http://msdn.microsoft.com/en-us/library/hh229047(v=vs.103).aspx * @see http://msdn.microsoft.com/en-us/library/hh229047(v=vs.103).aspx
*/ */
public static <T> Func1<Observer<T>, Subscription> dematerialize(final Observable<Notification<T>> sequence) { public static <T> Func1<Observer<T>, Subscription> dematerialize(final Observable<Notification<T>> sequence) {

View File

@@ -44,9 +44,8 @@ public final class OperationFilter<T> {
@Override @Override
public void onNext(T value) { public void onNext(T value) {
try { try {
if (predicate.call(value)) { if (predicate.call(value))
observer.onNext(value); observer.onNext(value);
}
} catch (Exception ex) { } catch (Exception ex) {
observer.onError(ex); observer.onError(ex);
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable // this will work if the sequence is asynchronous, it will have no effect on a synchronous observable

View File

@@ -32,18 +32,24 @@ public final class OperationFinally {
* so we use "finallyDo". * so we use "finallyDo".
* *
* @param sequence An observable sequence of elements * @param sequence An observable sequence of elements
* @param action An action to be taken when the sequence is complete or throws an exception * @param action An action to be taken when the sequence is complete or
* @return An observable sequence with the same elements as the input. * throws an exception
* After the last element is consumed (and {@link Observer#onCompleted} has been called), *
* or after an exception is thrown (and {@link Observer#onError} has been called), * @return An observable sequence with the same elements as the input. After
* the given action will be called. * the last element is consumed (and {@link Observer#onCompleted} has been
* @see <a href="http://msdn.microsoft.com/en-us/library/hh212133(v=vs.103).aspx">MSDN Observable.Finally method</a> * called), or after an exception is thrown (and {@link Observer#onError}
* has been called), the given action will be called.
*
* @see
* <a href="http://msdn.microsoft.com/en-us/library/hh212133(v=vs.103).aspx">MSDN
* Observable.Finally method</a>
*/ */
public static <T> Func1<Observer<T>, Subscription> finallyDo(final Observable<T> sequence, final Action0 action) { public static <T> Func1<Observer<T>, Subscription> finallyDo(final Observable<T> sequence, final Action0 action) {
return new Finally<>(sequence, action)::call; return new Finally<>(sequence, action)::call;
} }
private static class Finally<T> implements Func1<Observer<T>, Subscription> { private static class Finally<T> implements Func1<Observer<T>, Subscription> {
private final Observable<T> sequence; private final Observable<T> sequence;
private final Action0 finalAction; private final Action0 finalAction;
@@ -58,6 +64,7 @@ public final class OperationFinally {
} }
private class FinallyObserver implements Observer<T> { private class FinallyObserver implements Observer<T> {
private final Observer<T> observer; private final Observer<T> observer;
FinallyObserver(Observer<T> observer) { FinallyObserver(Observer<T> observer) {

View File

@@ -23,52 +23,51 @@ import rx.util.functions.Func1;
public final class OperationMap { public final class OperationMap {
/** /**
* Accepts a sequence and a transformation function. Returns a sequence that is the result of * Accepts a sequence and a transformation function. Returns a sequence that
* applying the transformation function to each item in the sequence. * is the result of applying the transformation function to each item in the
* sequence.
* *
* @param sequence * @param sequence the input sequence.
* the input sequence. * @param func a function to apply to each item in the sequence.
* @param func * @param <T> the type of the input sequence.
* a function to apply to each item in the sequence. * @param <R> the type of the output sequence.
* @param <T> *
* the type of the input sequence. * @return a sequence that is the result of applying the transformation
* @param <R> * function to each item in the input sequence.
* the type of the output sequence.
* @return a sequence that is the result of applying the transformation function to each item in the input sequence.
*/ */
public static <T, R> Func1<Observer<R>, Subscription> map(Observable<T> sequence, Func1<T, R> func) { public static <T, R> Func1<Observer<R>, Subscription> map(Observable<T> sequence, Func1<T, R> func) {
return new MapObservable<>(sequence, func); return new MapObservable<>(sequence, func);
} }
/** /**
* Accepts a sequence of observable sequences and a transformation function. Returns a flattened sequence that is the result of * Accepts a sequence of observable sequences and a transformation function.
* applying the transformation function to each item in the sequence of each observable sequence. * Returns a flattened sequence that is the result of applying the
* transformation function to each item in the sequence of each observable
* sequence.
* <p> * <p>
* The closure should return an Observable which will then be merged. * The closure should return an Observable which will then be merged.
* *
* @param sequence * @param sequence the input sequence.
* the input sequence. * @param func a function to apply to each item in the sequence.
* @param func * @param <T> the type of the input sequence.
* a function to apply to each item in the sequence. * @param <R> the type of the output sequence.
* @param <T> *
* the type of the input sequence. * @return a sequence that is the result of applying the transformation
* @param <R> * function to each item in the input sequence.
* the type of the output sequence.
* @return a sequence that is the result of applying the transformation function to each item in the input sequence.
*/ */
public static <T, R> Func1<Observer<R>, Subscription> mapMany(Observable<T> sequence, Func1<T, Observable<R>> func) { public static <T, R> Func1<Observer<R>, Subscription> mapMany(Observable<T> sequence, Func1<T, Observable<R>> func) {
return OperationMerge.merge(Observable.create(map(sequence, func))); return OperationMerge.merge(Observable.create(map(sequence, func)));
} }
/** /**
* An observable sequence that is the result of applying a transformation to each item in an input sequence. * An observable sequence that is the result of applying a transformation to
* each item in an input sequence.
* *
* @param <T> * @param <T> the type of the input sequence.
* the type of the input sequence. * @param <R> the type of the output sequence.
* @param <R>
* the type of the output sequence.
*/ */
private static class MapObservable<T, R> implements Func1<Observer<R>, Subscription> { private static class MapObservable<T, R> implements Func1<Observer<R>, Subscription> {
public MapObservable(Observable<T> sequence, Func1<T, R> func) { public MapObservable(Observable<T> sequence, Func1<T, R> func) {
this.sequence = sequence; this.sequence = sequence;
this.func = func; this.func = func;
@@ -85,14 +84,14 @@ public final class OperationMap {
} }
/** /**
* An observer that applies a transformation function to each item and forwards the result to an inner observer. * An observer that applies a transformation function to each item and
* forwards the result to an inner observer.
* *
* @param <T> * @param <T> the type of the observer items.
* the type of the observer items. * @param <R> the type of the inner observer items.
* @param <R>
* the type of the inner observer items.
*/ */
private static class MapObserver<T, R> implements Observer<T> { private static class MapObserver<T, R> implements Observer<T> {
public MapObserver(Observer<R> observer, Func1<T, R> func) { public MapObserver(Observer<R> observer, Func1<T, R> func) {
this.observer = observer; this.observer = observer;
this.func = func; this.func = func;

View File

@@ -22,20 +22,27 @@ import rx.Subscription;
import rx.util.functions.Func1; import rx.util.functions.Func1;
/** /**
* Materializes the implicit notifications of an observable sequence as explicit notification values. * Materializes the implicit notifications of an observable sequence as explicit
* notification values.
* <p> * <p>
* In other words, converts a sequence of OnNext, OnError and OnCompleted events into a sequence of ObservableNotifications containing the OnNext, OnError and OnCompleted values. * In other words, converts a sequence of OnNext, OnError and OnCompleted events
* into a sequence of ObservableNotifications containing the OnNext, OnError and
* OnCompleted values.
* <p> * <p>
* See http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx for the Microsoft Rx equivalent. * See http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx for the
* Microsoft Rx equivalent.
*/ */
public final class OperationMaterialize { public final class OperationMaterialize {
/** /**
* Materializes the implicit notifications of an observable sequence as explicit notification values. * Materializes the implicit notifications of an observable sequence as
* explicit notification values.
*
* @param sequence An observable sequence of elements to project.
*
* @return An observable sequence whose elements are the result of
* materializing the notifications of the given sequence.
* *
* @param sequence
* An observable sequence of elements to project.
* @return An observable sequence whose elements are the result of materializing the notifications of the given sequence.
* @see http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx * @see http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx
*/ */
public static <T> Func1<Observer<Notification<T>>, Subscription> materialize(final Observable<T> sequence) { public static <T> Func1<Observer<Notification<T>>, Subscription> materialize(final Observable<T> sequence) {

View File

@@ -30,11 +30,14 @@ import rx.util.functions.Func1;
public final class OperationMerge { public final class OperationMerge {
/** /**
* Flattens the observable sequences from the list of Observables into one observable sequence without any transformation. * Flattens the observable sequences from the list of Observables into one
* observable sequence without any transformation.
*
* @param source An observable sequence of elements to project.
*
* @return An observable sequence whose elements are the result of
* flattening the output from the list of Observables.
* *
* @param source
* An observable sequence of elements to project.
* @return An observable sequence whose elements are the result of flattening the output from the list of Observables.
* @see http://msdn.microsoft.com/en-us/library/hh229099(v=vs.103).aspx * @see http://msdn.microsoft.com/en-us/library/hh229099(v=vs.103).aspx
*/ */
public static <T> Func1<Observer<T>, Subscription> merge(final Observable<Observable<T>> source) { public static <T> Func1<Observer<T>, Subscription> merge(final Observable<Observable<T>> source) {
@@ -53,17 +56,14 @@ public final class OperationMerge {
@Override @Override
public Subscription call(Observer<Observable<T>> observer) { public Subscription call(Observer<Observable<T>> observer) {
for (Observable<T> o : sequences) { for (Observable<T> o : sequences)
if (!unsubscribed) { if (!unsubscribed)
observer.onNext(o); observer.onNext(o);
} else { else
// break out of the loop if we are unsubscribed // break out of the loop if we are unsubscribed
break; break;
} if (!unsubscribed)
}
if (!unsubscribed) {
observer.onCompleted(); observer.onCompleted();
}
return () -> { return () -> {
unsubscribed = true; unsubscribed = true;
@@ -73,17 +73,24 @@ public final class OperationMerge {
} }
/** /**
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads. * This class is NOT thread-safe if invoked and referenced multiple times.
* In other words, don't subscribe to it multiple times from different
* threads.
* <p> * <p>
* It IS thread-safe from within it while receiving onNext events from multiple threads. * It IS thread-safe from within it while receiving onNext events from
* multiple threads.
* <p> * <p>
* This should all be fine as long as it's kept as a private class and a new instance created from static factory method above. * This should all be fine as long as it's kept as a private class and a new
* instance created from static factory method above.
* <p> * <p>
* Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow. * Note how the take() factory method above protects us from a single
* instance being exposed with the Observable wrapper handling the subscribe
* flow.
* *
* @param <T> * @param <T>
*/ */
private static final class MergeObservable<T> implements Func1<Observer<T>, Subscription> { private static final class MergeObservable<T> implements Func1<Observer<T>, Subscription> {
private final Observable<Observable<T>> sequences; private final Observable<Observable<T>> sequences;
private final MergeSubscription ourSubscription = new MergeSubscription(); private final MergeSubscription ourSubscription = new MergeSubscription();
private final AtomicBoolean stopped = new AtomicBoolean(false); private final AtomicBoolean stopped = new AtomicBoolean(false);
@@ -99,7 +106,8 @@ public final class OperationMerge {
public Subscription call(Observer<T> actualObserver) { public Subscription call(Observer<T> actualObserver) {
/** /**
* We must synchronize a merge because we subscribe to multiple sequences in parallel that will each be emitting. * We must synchronize a merge because we subscribe to multiple
* sequences in parallel that will each be emitting.
* <p> * <p>
* The calls from each sequence must be serialized. * The calls from each sequence must be serialized.
* <p> * <p>
@@ -109,18 +117,23 @@ public final class OperationMerge {
SynchronizedObserver<T> synchronizedObserver = new SynchronizedObserver<>(actualObserver, subscription); SynchronizedObserver<T> synchronizedObserver = new SynchronizedObserver<>(actualObserver, subscription);
/** /**
* Subscribe to the parent Observable to get to the children Observables * Subscribe to the parent Observable to get to the children
* Observables
*/ */
sequences.subscribe(new ParentObserver(synchronizedObserver)); sequences.subscribe(new ParentObserver(synchronizedObserver));
/* return our subscription to allow unsubscribing */ /*
* return our subscription to allow unsubscribing
*/
return subscription; return subscription;
} }
/** /**
* Manage the internal subscription with a thread-safe means of stopping/unsubscribing so we don't unsubscribe twice. * Manage the internal subscription with a thread-safe means of
* stopping/unsubscribing so we don't unsubscribe twice.
* <p> * <p>
* Also has the stop() method returning a boolean so callers know if their thread "won" and should perform further actions. * Also has the stop() method returning a boolean so callers know if
* their thread "won" and should perform further actions.
*/ */
private class MergeSubscription implements Subscription { private class MergeSubscription implements Subscription {
@@ -134,23 +147,23 @@ public final class OperationMerge {
boolean didSet = stopped.compareAndSet(false, true); boolean didSet = stopped.compareAndSet(false, true);
if (didSet) { if (didSet) {
// this thread won the race to stop, so unsubscribe from the actualSubscription // this thread won the race to stop, so unsubscribe from the actualSubscription
for (Subscription _s : childSubscriptions.values()) { for (Subscription _s : childSubscriptions.values())
_s.unsubscribe(); _s.unsubscribe();
}
return true; return true;
} else { } else
// another thread beat us // another thread beat us
return false; return false;
} }
} }
}
/** /**
* Subscribe to the top level Observable to receive the sequence of Observable<T> children. * Subscribe to the top level Observable to receive the sequence of
* Observable<T> children.
* *
* @param <T> * @param <T>
*/ */
private class ParentObserver implements Observer<Observable<T>> { private class ParentObserver implements Observer<Observable<T>> {
private final Observer<T> actualObserver; private final Observer<T> actualObserver;
public ParentObserver(Observer<T> actualObserver) { public ParentObserver(Observer<T> actualObserver) {
@@ -164,14 +177,11 @@ public final class OperationMerge {
// but will let the child worry about it // but will let the child worry about it
// if however this completes and there are no children processing, then we will send onCompleted // if however this completes and there are no children processing, then we will send onCompleted
if (childObservers.isEmpty()) { if (childObservers.isEmpty())
if (!stopped.get()) { if (!stopped.get())
if (ourSubscription.stop()) { if (ourSubscription.stop())
actualObserver.onCompleted(); actualObserver.onCompleted();
} }
}
}
}
@Override @Override
public void onError(Exception e) { public void onError(Exception e) {
@@ -180,20 +190,21 @@ public final class OperationMerge {
@Override @Override
public void onNext(Observable<T> childObservable) { public void onNext(Observable<T> childObservable) {
if (stopped.get()) { if (stopped.get())
// we won't act on any further items // we won't act on any further items
return; return;
}
if (childObservable == null) { if (childObservable == null)
throw new IllegalArgumentException("Observable<T> can not be null."); throw new IllegalArgumentException("Observable<T> can not be null.");
}
/** /**
* For each child Observable we receive we'll subscribe with a separate Observer * For each child Observable we receive we'll subscribe with a
* that will each then forward their sequences to the actualObserver. * separate Observer that will each then forward their sequences
* to the actualObserver.
* <p> * <p>
* We use separate child Observers for each sequence to simplify the onComplete/onError handling so each sequence has its own lifecycle. * We use separate child Observers for each sequence to simplify
* the onComplete/onError handling so each sequence has its own
* lifecycle.
*/ */
ChildObserver _w = new ChildObserver(actualObserver); ChildObserver _w = new ChildObserver(actualObserver);
childObservers.put(_w, _w); childObservers.put(_w, _w);
@@ -204,7 +215,8 @@ public final class OperationMerge {
} }
/** /**
* Subscribe to each child Observable<T> and forward their sequence of data to the actualObserver * Subscribe to each child Observable<T> and forward their sequence of
* data to the actualObserver
* *
*/ */
private class ChildObserver implements Observer<T> { private class ChildObserver implements Observer<T> {
@@ -221,34 +233,28 @@ public final class OperationMerge {
childObservers.remove(this); childObservers.remove(this);
// if there are now 0 Observers left, so if the parent is also completed we send the onComplete to the actualObserver // if there are now 0 Observers left, so if the parent is also completed we send the onComplete to the actualObserver
// if the parent is not complete that means there is another sequence (and child Observer) to come // if the parent is not complete that means there is another sequence (and child Observer) to come
if (!stopped.get()) { if (!stopped.get())
if (childObservers.isEmpty() && parentCompleted) { if (childObservers.isEmpty() && parentCompleted)
if (ourSubscription.stop()) { if (ourSubscription.stop())
// this thread 'won' the race to unsubscribe/stop so let's send onCompleted // this thread 'won' the race to unsubscribe/stop so let's send onCompleted
actualObserver.onCompleted(); actualObserver.onCompleted();
} }
}
}
}
@Override @Override
public void onError(Exception e) { public void onError(Exception e) {
if (!stopped.get()) { if (!stopped.get())
if (ourSubscription.stop()) { if (ourSubscription.stop())
// this thread 'won' the race to unsubscribe/stop so let's send the error // this thread 'won' the race to unsubscribe/stop so let's send the error
actualObserver.onError(e); actualObserver.onError(e);
} }
}
}
@Override @Override
public void onNext(T args) { public void onNext(T args) {
// in case the Observable is poorly behaved and doesn't listen to the unsubscribe request // in case the Observable is poorly behaved and doesn't listen to the unsubscribe request
// we'll ignore anything that comes in after we've unsubscribed // we'll ignore anything that comes in after we've unsubscribed
if (!stopped.get()) { if (!stopped.get())
actualObserver.onNext(args); actualObserver.onNext(args);
} }
}
} }
} }

View File

@@ -27,23 +27,30 @@ import rx.util.CompositeException;
import rx.util.functions.Func1; import rx.util.functions.Func1;
/** /**
* Same functionality as OperationMerge except that onError events will be skipped so that all onNext calls are passed on until all sequences finish with onComplete or onError, and then the first * Same functionality as OperationMerge except that onError events will be
* onError received (if any) will be passed on. * skipped so that all onNext calls are passed on until all sequences finish
* with onComplete or onError, and then the first onError received (if any) will
* be passed on.
* <p> * <p>
* This allows retrieving all successful onNext calls without being blocked by an onError early in a sequence. * This allows retrieving all successful onNext calls without being blocked by
* an onError early in a sequence.
* <p> * <p>
* NOTE: If this is used on an infinite stream it will never call onError and effectively will swallow errors. * NOTE: If this is used on an infinite stream it will never call onError and
* effectively will swallow errors.
*/ */
public final class OperationMergeDelayError { public final class OperationMergeDelayError {
/** /**
* Flattens the observable sequences from the list of Observables into one observable sequence without any transformation and delays any onError calls until after all sequences have called * Flattens the observable sequences from the list of Observables into one
* onError or onComplete so as to allow all successful * observable sequence without any transformation and delays any onError
* onNext calls to be received. * calls until after all sequences have called onError or onComplete so as
* to allow all successful onNext calls to be received.
*
* @param source An observable sequence of elements to project.
*
* @return An observable sequence whose elements are the result of
* flattening the output from the list of Observables.
* *
* @param source
* An observable sequence of elements to project.
* @return An observable sequence whose elements are the result of flattening the output from the list of Observables.
* @see http://msdn.microsoft.com/en-us/library/hh229099(v=vs.103).aspx * @see http://msdn.microsoft.com/en-us/library/hh229099(v=vs.103).aspx
*/ */
public static <T> Func1<Observer<T>, Subscription> mergeDelayError(final Observable<Observable<T>> sequences) { public static <T> Func1<Observer<T>, Subscription> mergeDelayError(final Observable<Observable<T>> sequences) {
@@ -63,17 +70,14 @@ public final class OperationMergeDelayError {
@Override @Override
public Subscription call(Observer<Observable<T>> observer) { public Subscription call(Observer<Observable<T>> observer) {
for (Observable<T> o : sequences) { for (Observable<T> o : sequences)
if (!unsubscribed) { if (!unsubscribed)
observer.onNext(o); observer.onNext(o);
} else { else
// break out of the loop if we are unsubscribed // break out of the loop if we are unsubscribed
break; break;
} if (!unsubscribed)
}
if (!unsubscribed) {
observer.onCompleted(); observer.onCompleted();
}
return new Subscription() { return new Subscription() {
@Override @Override
@@ -93,17 +97,14 @@ public final class OperationMergeDelayError {
@Override @Override
public Subscription call(Observer<Observable<T>> observer) { public Subscription call(Observer<Observable<T>> observer) {
for (Observable<T> o : sequences) { for (Observable<T> o : sequences)
if (!unsubscribed) { if (!unsubscribed)
observer.onNext(o); observer.onNext(o);
} else { else
// break out of the loop if we are unsubscribed // break out of the loop if we are unsubscribed
break; break;
} if (!unsubscribed)
}
if (!unsubscribed) {
observer.onCompleted(); observer.onCompleted();
}
return new Subscription() { return new Subscription() {
@@ -118,17 +119,24 @@ public final class OperationMergeDelayError {
} }
/** /**
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads. * This class is NOT thread-safe if invoked and referenced multiple times.
* In other words, don't subscribe to it multiple times from different
* threads.
* <p> * <p>
* It IS thread-safe from within it while receiving onNext events from multiple threads. * It IS thread-safe from within it while receiving onNext events from
* multiple threads.
* <p> * <p>
* This should all be fine as long as it's kept as a private class and a new instance created from static factory method above. * This should all be fine as long as it's kept as a private class and a new
* instance created from static factory method above.
* <p> * <p>
* Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow. * Note how the take() factory method above protects us from a single
* instance being exposed with the Observable wrapper handling the subscribe
* flow.
* *
* @param <T> * @param <T>
*/ */
private static final class MergeDelayErrorObservable<T> implements Func1<Observer<T>, Subscription> { private static final class MergeDelayErrorObservable<T> implements Func1<Observer<T>, Subscription> {
private final Observable<Observable<T>> sequences; private final Observable<Observable<T>> sequences;
private final MergeSubscription ourSubscription = new MergeSubscription(); private final MergeSubscription ourSubscription = new MergeSubscription();
private AtomicBoolean stopped = new AtomicBoolean(false); private AtomicBoolean stopped = new AtomicBoolean(false);
@@ -144,18 +152,23 @@ public final class OperationMergeDelayError {
public Subscription call(Observer<T> actualObserver) { public Subscription call(Observer<T> actualObserver) {
/** /**
* Subscribe to the parent Observable to get to the children Observables * Subscribe to the parent Observable to get to the children
* Observables
*/ */
sequences.subscribe(new ParentObserver(actualObserver)); sequences.subscribe(new ParentObserver(actualObserver));
/* return our subscription to allow unsubscribing */ /*
* return our subscription to allow unsubscribing
*/
return ourSubscription; return ourSubscription;
} }
/** /**
* Manage the internal subscription with a thread-safe means of stopping/unsubscribing so we don't unsubscribe twice. * Manage the internal subscription with a thread-safe means of
* stopping/unsubscribing so we don't unsubscribe twice.
* <p> * <p>
* Also has the stop() method returning a boolean so callers know if their thread "won" and should perform further actions. * Also has the stop() method returning a boolean so callers know if
* their thread "won" and should perform further actions.
*/ */
private class MergeSubscription implements Subscription { private class MergeSubscription implements Subscription {
@@ -169,23 +182,23 @@ public final class OperationMergeDelayError {
boolean didSet = stopped.compareAndSet(false, true); boolean didSet = stopped.compareAndSet(false, true);
if (didSet) { if (didSet) {
// this thread won the race to stop, so unsubscribe from the actualSubscription // this thread won the race to stop, so unsubscribe from the actualSubscription
for (Subscription _s : childSubscriptions.values()) { for (Subscription _s : childSubscriptions.values())
_s.unsubscribe(); _s.unsubscribe();
}
return true; return true;
} else { } else
// another thread beat us // another thread beat us
return false; return false;
} }
} }
}
/** /**
* Subscribe to the top level Observable to receive the sequence of Observable<T> children. * Subscribe to the top level Observable to receive the sequence of
* Observable<T> children.
* *
* @param <T> * @param <T>
*/ */
private class ParentObserver implements Observer<Observable<T>> { private class ParentObserver implements Observer<Observable<T>> {
private final Observer<T> actualObserver; private final Observer<T> actualObserver;
public ParentObserver(Observer<T> actualObserver) { public ParentObserver(Observer<T> actualObserver) {
@@ -199,23 +212,19 @@ public final class OperationMergeDelayError {
// but will let the child worry about it // but will let the child worry about it
// if however this completes and there are no children processing, then we will send onCompleted // if however this completes and there are no children processing, then we will send onCompleted
if (childObservers.size() == 0) { if (childObservers.size() == 0)
if (!stopped.get()) { if (!stopped.get())
if (ourSubscription.stop()) { if (ourSubscription.stop())
if (onErrorReceived.size() == 1) { if (onErrorReceived.size() == 1)
// an onError was received from 1 ChildObserver so we now send it as a delayed error // an onError was received from 1 ChildObserver so we now send it as a delayed error
actualObserver.onError(onErrorReceived.peek()); actualObserver.onError(onErrorReceived.peek());
} else if (onErrorReceived.size() > 1) { else if (onErrorReceived.size() > 1)
// an onError was received from more than 1 ChildObserver so we now send it as a delayed error // an onError was received from more than 1 ChildObserver so we now send it as a delayed error
actualObserver.onError(new CompositeException(onErrorReceived)); actualObserver.onError(new CompositeException(onErrorReceived));
} else { else
// no delayed error so send onCompleted // no delayed error so send onCompleted
actualObserver.onCompleted(); actualObserver.onCompleted();
} }
}
}
}
}
@Override @Override
public void onError(Exception e) { public void onError(Exception e) {
@@ -224,20 +233,21 @@ public final class OperationMergeDelayError {
@Override @Override
public void onNext(Observable<T> childObservable) { public void onNext(Observable<T> childObservable) {
if (stopped.get()) { if (stopped.get())
// we won't act on any further items // we won't act on any further items
return; return;
}
if (childObservable == null) { if (childObservable == null)
throw new IllegalArgumentException("Observable<T> can not be null."); throw new IllegalArgumentException("Observable<T> can not be null.");
}
/** /**
* For each child Observable we receive we'll subscribe with a separate Observer * For each child Observable we receive we'll subscribe with a
* that will each then forward their sequences to the actualObserver. * separate Observer that will each then forward their sequences
* to the actualObserver.
* <p> * <p>
* We use separate child Observers for each sequence to simplify the onComplete/onError handling so each sequence has its own lifecycle. * We use separate child Observers for each sequence to simplify
* the onComplete/onError handling so each sequence has its own
* lifecycle.
*/ */
ChildObserver _w = new ChildObserver(actualObserver); ChildObserver _w = new ChildObserver(actualObserver);
childObservers.put(_w, _w); childObservers.put(_w, _w);
@@ -248,7 +258,8 @@ public final class OperationMergeDelayError {
} }
/** /**
* Subscribe to each child Observable<T> and forward their sequence of data to the actualObserver * Subscribe to each child Observable<T> and forward their sequence of
* data to the actualObserver
* *
*/ */
private class ChildObserver implements Observer<T> { private class ChildObserver implements Observer<T> {
@@ -266,10 +277,9 @@ public final class OperationMergeDelayError {
childObservers.remove(this); childObservers.remove(this);
// if there are now 0 Observers left, so if the parent is also completed we send the onComplete to the actualObserver // if there are now 0 Observers left, so if the parent is also completed we send the onComplete to the actualObserver
// if the parent is not complete that means there is another sequence (and child Observer) to come // if the parent is not complete that means there is another sequence (and child Observer) to come
if (!stopped.get()) { if (!stopped.get())
finishObserver(); finishObserver();
} }
}
@Override @Override
public void onError(Exception e) { public void onError(Exception e) {
@@ -289,38 +299,38 @@ public final class OperationMergeDelayError {
} }
/** /**
* onComplete and onError when called need to check for the parent being complete and if so send the onCompleted or onError to the actualObserver. * onComplete and onError when called need to check for the parent
* being complete and if so send the onCompleted or onError to the
* actualObserver.
* <p> * <p>
* This does NOT get invoked if synchronous execution occurs, but will when asynchronously executing. * This does NOT get invoked if synchronous execution occurs, but
* will when asynchronously executing.
* <p> * <p>
* TestCase testErrorDelayed4WithThreading specifically tests this use case. * TestCase testErrorDelayed4WithThreading specifically tests this
* use case.
*/ */
private void finishObserver() { private void finishObserver() {
if (childObservers.size() == 0 && parentCompleted) { if (childObservers.size() == 0 && parentCompleted)
if (ourSubscription.stop()) { if (ourSubscription.stop())
// this thread 'won' the race to unsubscribe/stop so let's send onError or onCompleted // this thread 'won' the race to unsubscribe/stop so let's send onError or onCompleted
if (onErrorReceived.size() == 1) { if (onErrorReceived.size() == 1)
// an onError was received from 1 ChildObserver so we now send it as a delayed error // an onError was received from 1 ChildObserver so we now send it as a delayed error
actualObserver.onError(onErrorReceived.peek()); actualObserver.onError(onErrorReceived.peek());
} else if (onErrorReceived.size() > 1) { else if (onErrorReceived.size() > 1)
// an onError was received from more than 1 ChildObserver so we now send it as a delayed error // an onError was received from more than 1 ChildObserver so we now send it as a delayed error
actualObserver.onError(new CompositeException(onErrorReceived)); actualObserver.onError(new CompositeException(onErrorReceived));
} else { else
// no delayed error so send onCompleted // no delayed error so send onCompleted
actualObserver.onCompleted(); actualObserver.onCompleted();
} }
}
}
}
@Override @Override
public void onNext(T args) { public void onNext(T args) {
// in case the Observable is poorly behaved and doesn't listen to the unsubscribe request // in case the Observable is poorly behaved and doesn't listen to the unsubscribe request
// we'll ignore anything that comes in after we've unsubscribed or an onError has been received and delayed // we'll ignore anything that comes in after we've unsubscribed or an onError has been received and delayed
if (!stopped.get() && !finished) { if (!stopped.get() && !finished)
actualObserver.onNext(args); actualObserver.onNext(args);
} }
}
} }
} }

View File

@@ -54,9 +54,8 @@ public final class OperationMostRecent {
@Override @Override
public T next() { public T next() {
if (observer.getException() != null) { if (observer.getException() != null)
throw Exceptions.propagate(observer.getException()); throw Exceptions.propagate(observer.getException());
}
return observer.getRecentValue(); return observer.getRecentValue();
} }
@@ -67,6 +66,7 @@ public final class OperationMostRecent {
} }
private static class MostRecentObserver<T> implements Observer<T> { private static class MostRecentObserver<T> implements Observer<T> {
private final AtomicBoolean completed = new AtomicBoolean(false); private final AtomicBoolean completed = new AtomicBoolean(false);
private final AtomicReference<T> value; private final AtomicReference<T> value;
private final AtomicReference<Exception> exception = new AtomicReference<>(); private final AtomicReference<Exception> exception = new AtomicReference<>();

View File

@@ -26,7 +26,8 @@ import rx.Observer;
import rx.util.Exceptions; import rx.util.Exceptions;
/** /**
* Samples the next value (blocking without buffering) from in an observable sequence. * Samples the next value (blocking without buffering) from in an observable
* sequence.
*/ */
public final class OperationNext { public final class OperationNext {
@@ -56,9 +57,8 @@ public final class OperationNext {
@Override @Override
public T next() { public T next() {
if (observer.isCompleted(true)) { if (observer.isCompleted(true))
throw new IllegalStateException("Observable is completed"); throw new IllegalStateException("Observable is completed");
}
observer.await(); observer.await();
@@ -78,6 +78,7 @@ public final class OperationNext {
} }
private static class NextObserver<T> implements Observer<Notification<T>> { private static class NextObserver<T> implements Observer<Notification<T>> {
private final BlockingQueue<Notification<T>> buf = new ArrayBlockingQueue<>(1); private final BlockingQueue<Notification<T>> buf = new ArrayBlockingQueue<>(1);
private final AtomicBoolean waiting = new AtomicBoolean(false); private final AtomicBoolean waiting = new AtomicBoolean(false);
@@ -100,11 +101,10 @@ public final class OperationNext {
Notification<T> concurrentItem = buf.poll(); Notification<T> concurrentItem = buf.poll();
// in case if we won race condition with onComplete/onError method // in case if we won race condition with onComplete/onError method
if (!concurrentItem.isOnNext()) { if (!concurrentItem.isOnNext())
toOffer = concurrentItem; toOffer = concurrentItem;
} }
} }
}
} }
@@ -114,17 +114,14 @@ public final class OperationNext {
public boolean isCompleted(boolean rethrowExceptionIfExists) { public boolean isCompleted(boolean rethrowExceptionIfExists) {
Notification<T> lastItem = buf.peek(); Notification<T> lastItem = buf.peek();
if (lastItem == null) { if (lastItem == null)
return false; return false;
}
if (lastItem.isOnError()) { if (lastItem.isOnError())
if (rethrowExceptionIfExists) { if (rethrowExceptionIfExists)
throw Exceptions.propagate(lastItem.getException()); throw Exceptions.propagate(lastItem.getException());
} else { else
return true; return true;
}
}
return lastItem.isOnCompleted(); return lastItem.isOnCompleted();
} }
@@ -132,13 +129,11 @@ public final class OperationNext {
public T takeNext() throws InterruptedException { public T takeNext() throws InterruptedException {
Notification<T> next = buf.take(); Notification<T> next = buf.take();
if (next.isOnError()) { if (next.isOnError())
throw Exceptions.propagate(next.getException()); throw Exceptions.propagate(next.getException());
}
if (next.isOnCompleted()) { if (next.isOnCompleted())
throw new IllegalStateException("Observable is completed"); throw new IllegalStateException("Observable is completed");
}
return next.getValue(); return next.getValue();

View File

@@ -28,6 +28,7 @@ public class OperationObserveOn {
} }
private static class ObserveOn<T> implements Func1<Observer<T>, Subscription> { private static class ObserveOn<T> implements Func1<Observer<T>, Subscription> {
private final Observable<T> source; private final Observable<T> source;
private final Scheduler scheduler; private final Scheduler scheduler;

View File

@@ -55,31 +55,40 @@ public final class OperationOnErrorResumeNextViaFunction<T> {
} }
/** /**
* Instead of passing the onError forward, we intercept and "resume" with the resumeSequence. * Instead of passing the onError forward, we intercept and
* "resume" with the resumeSequence.
*/ */
@Override @Override
public void onError(Exception ex) { public void onError(Exception ex) {
/* remember what the current subscription is so we can determine if someone unsubscribes concurrently */ /*
* remember what the current subscription is so we can
* determine if someone unsubscribes concurrently
*/
AtomicObservableSubscription currentSubscription = subscriptionRef.get(); AtomicObservableSubscription currentSubscription = subscriptionRef.get();
// check that we have not been unsubscribed before we can process the error // check that we have not been unsubscribed before we can process the error
if (currentSubscription != null) { if (currentSubscription != null)
try { try {
Observable<T> resumeSequence = resumeFunction.call(ex); Observable<T> resumeSequence = resumeFunction.call(ex);
/* error occurred, so switch subscription to the 'resumeSequence' */ /*
* error occurred, so switch subscription to the
* 'resumeSequence'
*/
AtomicObservableSubscription innerSubscription = new AtomicObservableSubscription(resumeSequence.subscribe(observer)); AtomicObservableSubscription innerSubscription = new AtomicObservableSubscription(resumeSequence.subscribe(observer));
/* we changed the sequence, so also change the subscription to the one of the 'resumeSequence' instead */ /*
if (!subscriptionRef.compareAndSet(currentSubscription, innerSubscription)) { * we changed the sequence, so also change the
* subscription to the one of the 'resumeSequence'
* instead
*/
if (!subscriptionRef.compareAndSet(currentSubscription, innerSubscription))
// we failed to set which means 'subscriptionRef' was set to NULL via the unsubscribe below // we failed to set which means 'subscriptionRef' was set to NULL via the unsubscribe below
// so we want to immediately unsubscribe from the resumeSequence we just subscribed to // so we want to immediately unsubscribe from the resumeSequence we just subscribed to
innerSubscription.unsubscribe(); innerSubscription.unsubscribe();
}
} catch (Exception e) { } catch (Exception e) {
// the resume function failed so we need to call onError // the resume function failed so we need to call onError
// I am using CompositeException so that both exceptions can be seen // I am using CompositeException so that both exceptions can be seen
observer.onError(new CompositeException("OnErrorResume function failed", Arrays.asList(ex, e))); observer.onError(new CompositeException("OnErrorResume function failed", Arrays.asList(ex, e)));
} }
} }
}
@Override @Override
public void onCompleted() { public void onCompleted() {

View File

@@ -54,24 +54,34 @@ public final class OperationOnErrorResumeNextViaObservable<T> {
} }
/** /**
* Instead of passing the onError forward, we intercept and "resume" with the resumeSequence. * Instead of passing the onError forward, we intercept and
* "resume" with the resumeSequence.
*/ */
@Override @Override
public void onError(Exception ex) { public void onError(Exception ex) {
/* remember what the current subscription is so we can determine if someone unsubscribes concurrently */ /*
* remember what the current subscription is so we can
* determine if someone unsubscribes concurrently
*/
AtomicObservableSubscription currentSubscription = subscriptionRef.get(); AtomicObservableSubscription currentSubscription = subscriptionRef.get();
// check that we have not been unsubscribed before we can process the error // check that we have not been unsubscribed before we can process the error
if (currentSubscription != null) { if (currentSubscription != null) {
/* error occurred, so switch subscription to the 'resumeSequence' */ /*
* error occurred, so switch subscription to the
* 'resumeSequence'
*/
AtomicObservableSubscription innerSubscription = new AtomicObservableSubscription(resumeSequence.subscribe(observer)); AtomicObservableSubscription innerSubscription = new AtomicObservableSubscription(resumeSequence.subscribe(observer));
/* we changed the sequence, so also change the subscription to the one of the 'resumeSequence' instead */ /*
if (!subscriptionRef.compareAndSet(currentSubscription, innerSubscription)) { * we changed the sequence, so also change the
* subscription to the one of the 'resumeSequence'
* instead
*/
if (!subscriptionRef.compareAndSet(currentSubscription, innerSubscription))
// we failed to set which means 'subscriptionRef' was set to NULL via the unsubscribe below // we failed to set which means 'subscriptionRef' was set to NULL via the unsubscribe below
// so we want to immediately unsubscribe from the resumeSequence we just subscribed to // so we want to immediately unsubscribe from the resumeSequence we just subscribed to
innerSubscription.unsubscribe(); innerSubscription.unsubscribe();
} }
} }
}
@Override @Override
public void onCompleted() { public void onCompleted() {

View File

@@ -26,7 +26,8 @@ import rx.util.CompositeException;
import rx.util.functions.Func1; import rx.util.functions.Func1;
/** /**
* When an onError occurs the resumeFunction will be executed and it's response passed to onNext instead of calling onError. * When an onError occurs the resumeFunction will be executed and it's response
* passed to onNext instead of calling onError.
*/ */
public final class OperationOnErrorReturn<T> { public final class OperationOnErrorReturn<T> {
@@ -35,6 +36,7 @@ public final class OperationOnErrorReturn<T> {
} }
private static class OnErrorReturn<T> implements Func1<Observer<T>, Subscription> { private static class OnErrorReturn<T> implements Func1<Observer<T>, Subscription> {
private final Func1<Exception, T> resumeFunction; private final Func1<Exception, T> resumeFunction;
private final Observable<T> originalSequence; private final Observable<T> originalSequence;
@@ -59,28 +61,44 @@ public final class OperationOnErrorReturn<T> {
} }
/** /**
* Instead of passing the onError forward, we intercept and "resume" with the resumeSequence. * Instead of passing the onError forward, we intercept and
* "resume" with the resumeSequence.
*/ */
@Override @Override
public void onError(Exception ex) { public void onError(Exception ex) {
/* remember what the current subscription is so we can determine if someone unsubscribes concurrently */ /*
* remember what the current subscription is so we can
* determine if someone unsubscribes concurrently
*/
AtomicObservableSubscription currentSubscription = subscriptionRef.get(); AtomicObservableSubscription currentSubscription = subscriptionRef.get();
// check that we have not been unsubscribed before we can process the error // check that we have not been unsubscribed before we can process the error
if (currentSubscription != null) { if (currentSubscription != null)
try { try {
/* error occurred, so execute the function, give it the exception and call onNext with the response */ /*
* error occurred, so execute the function, give it
* the exception and call onNext with the response
*/
onNext(resumeFunction.call(ex)); onNext(resumeFunction.call(ex));
/* /*
* we are not handling an exception thrown from this function ... should we do something? * we are not handling an exception thrown from this
* error handling within an error handler is a weird one to determine what we should do * function ... should we do something?
* right now I'm going to just let it throw whatever exceptions occur (such as NPE) * error handling within an error handler is a weird
* but I'm considering calling the original Observer.onError to act as if this OnErrorReturn operator didn't happen * one to determine what we should do
* right now I'm going to just let it throw whatever
* exceptions occur (such as NPE)
* but I'm considering calling the original
* Observer.onError to act as if this OnErrorReturn
* operator didn't happen
*/ */
/* we are now completed */ /*
* we are now completed
*/
onCompleted(); onCompleted();
/* unsubscribe since it blew up */ /*
* unsubscribe since it blew up
*/
currentSubscription.unsubscribe(); currentSubscription.unsubscribe();
} catch (Exception e) { } catch (Exception e) {
// the return function failed so we need to call onError // the return function failed so we need to call onError
@@ -88,7 +106,6 @@ public final class OperationOnErrorReturn<T> {
observer.onError(new CompositeException("OnErrorReturn function failed", Arrays.asList(ex, e))); observer.onError(new CompositeException("OnErrorReturn function failed", Arrays.asList(ex, e)));
} }
} }
}
@Override @Override
public void onCompleted() { public void onCompleted() {

View File

@@ -23,17 +23,19 @@ import rx.util.functions.Func1;
import rx.util.functions.Func2; import rx.util.functions.Func2;
public final class OperationScan { public final class OperationScan {
/** /**
* Applies an accumulator function over an observable sequence and returns each intermediate result with the specified source and accumulator. * Applies an accumulator function over an observable sequence and returns
* each intermediate result with the specified source and accumulator.
* *
* @param sequence * @param sequence An observable sequence of elements to project.
* An observable sequence of elements to project. * @param initialValue The initial (seed) accumulator value.
* @param initialValue * @param accumulator An accumulator function to be invoked on each element
* The initial (seed) accumulator value. * from the sequence.
* @param accumulator *
* An accumulator function to be invoked on each element from the sequence. * @return An observable sequence whose elements are the result of
* accumulating the output from the list of Observables.
* *
* @return An observable sequence whose elements are the result of accumulating the output from the list of Observables.
* @see http://msdn.microsoft.com/en-us/library/hh211665(v=vs.103).aspx * @see http://msdn.microsoft.com/en-us/library/hh211665(v=vs.103).aspx
*/ */
public static <T> Func1<Observer<T>, Subscription> scan(Observable<T> sequence, T initialValue, Func2<T, T, T> accumulator) { public static <T> Func1<Observer<T>, Subscription> scan(Observable<T> sequence, T initialValue, Func2<T, T, T> accumulator) {
@@ -41,14 +43,16 @@ public final class OperationScan {
} }
/** /**
* Applies an accumulator function over an observable sequence and returns each intermediate result with the specified source and accumulator. * Applies an accumulator function over an observable sequence and returns
* each intermediate result with the specified source and accumulator.
* *
* @param sequence * @param sequence An observable sequence of elements to project.
* An observable sequence of elements to project. * @param accumulator An accumulator function to be invoked on each element
* @param accumulator * from the sequence.
* An accumulator function to be invoked on each element from the sequence. *
* @return An observable sequence whose elements are the result of
* accumulating the output from the list of Observables.
* *
* @return An observable sequence whose elements are the result of accumulating the output from the list of Observables.
* @see http://msdn.microsoft.com/en-us/library/hh211665(v=vs.103).aspx * @see http://msdn.microsoft.com/en-us/library/hh211665(v=vs.103).aspx
*/ */
public static <T> Func1<Observer<T>, Subscription> scan(Observable<T> sequence, Func2<T, T, T> accumulator) { public static <T> Func1<Observer<T>, Subscription> scan(Observable<T> sequence, Func2<T, T, T> accumulator) {
@@ -56,6 +60,7 @@ public final class OperationScan {
} }
private static class Accumulator<T> implements Func1<Observer<T>, Subscription> { private static class Accumulator<T> implements Func1<Observer<T>, Subscription> {
private final Observable<T> sequence; private final Observable<T> sequence;
private final T initialValue; private final T initialValue;
private final Func2<T, T, T> accumlatorFunction; private final Func2<T, T, T> accumlatorFunction;
@@ -75,11 +80,13 @@ public final class OperationScan {
private boolean hasSentInitialValue = false; private boolean hasSentInitialValue = false;
/** /**
* We must synchronize this because we can't allow * We must synchronize this because we can't allow multiple
* multiple threads to execute the 'accumulatorFunction' at the same time because * threads to execute the 'accumulatorFunction' at the same time
* the accumulator code very often will be doing mutation of the 'acc' object such as a non-threadsafe HashMap * because the accumulator code very often will be doing
* mutation of the 'acc' object such as a non-threadsafe HashMap
* *
* Because it's synchronized it's using non-atomic variables since everything in this method is single-threaded * Because it's synchronized it's using non-atomic variables
* since everything in this method is single-threaded
*/ */
@Override @Override
public synchronized void onNext(T value) { public synchronized void onNext(T value) {
@@ -119,9 +126,8 @@ public final class OperationScan {
@Override @Override
public synchronized void onCompleted() { public synchronized void onCompleted() {
// if only one sequence value existed, we send it without any accumulation // if only one sequence value existed, we send it without any accumulation
if (!hasSentInitialValue) { if (!hasSentInitialValue)
observer.onNext(acc); observer.onNext(acc);
}
observer.onCompleted(); observer.onCompleted();
} }
})); }));

View File

@@ -23,15 +23,18 @@ import rx.Subscription;
import rx.util.functions.Func1; import rx.util.functions.Func1;
/** /**
* Skips a specified number of contiguous values from the start of a Observable sequence and then returns the remaining values. * Skips a specified number of contiguous values from the start of a Observable
* sequence and then returns the remaining values.
*/ */
public final class OperationSkip { public final class OperationSkip {
/** /**
* Skips a specified number of contiguous values from the start of a Observable sequence and then returns the remaining values. * Skips a specified number of contiguous values from the start of a
* Observable sequence and then returns the remaining values.
* *
* @param items * @param items
* @param num * @param num
*
* @return * @return
* *
* @see http://msdn.microsoft.com/en-us/library/hh229847(v=vs.103).aspx * @see http://msdn.microsoft.com/en-us/library/hh229847(v=vs.103).aspx
@@ -42,13 +45,17 @@ public final class OperationSkip {
} }
/** /**
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads. * This class is NOT thread-safe if invoked and referenced multiple times.
* In other words, don't subscribe to it multiple times from different
* threads.
* <p> * <p>
* It IS thread-safe from within it while receiving onNext events from multiple threads. * It IS thread-safe from within it while receiving onNext events from
* multiple threads.
* *
* @param <T> * @param <T>
*/ */
private static class Skip<T> implements Func1<Observer<T>, Subscription> { private static class Skip<T> implements Func1<Observer<T>, Subscription> {
private final int num; private final int num;
private final Observable<T> items; private final Observable<T> items;
@@ -63,7 +70,8 @@ public final class OperationSkip {
} }
/** /**
* Used to subscribe to the 'items' Observable sequence and forward to the actualObserver up to 'num' count. * Used to subscribe to the 'items' Observable sequence and forward to
* the actualObserver up to 'num' count.
*/ */
private class ItemObserver implements Observer<T> { private class ItemObserver implements Observer<T> {
@@ -87,10 +95,9 @@ public final class OperationSkip {
@Override @Override
public void onNext(T args) { public void onNext(T args) {
// skip them until we reach the 'num' value // skip them until we reach the 'num' value
if (counter.incrementAndGet() > num) { if (counter.incrementAndGet() > num)
observer.onNext(args); observer.onNext(args);
} }
}
} }

View File

@@ -23,29 +23,32 @@ import rx.util.SynchronizedObserver;
import rx.util.functions.Func1; import rx.util.functions.Func1;
/** /**
* An observable that wraps an observable of the same type and then enforces the semantics * An observable that wraps an observable of the same type and then enforces the
* expected of a well-behaved observable. * semantics expected of a well-behaved observable.
* <p> * <p>
* An observable that ensures onNext, onCompleted, or onError calls on its subscribers are * An observable that ensures onNext, onCompleted, or onError calls on its
* not interleaved, onCompleted and onError are only called once respectively, and no * subscribers are not interleaved, onCompleted and onError are only called once
* onNext calls follow onCompleted and onError calls. * respectively, and no onNext calls follow onCompleted and onError calls.
* <p> * <p>
* NOTE: {@link Observable#create} already wraps Observables so this is generally redundant. * NOTE: {@link Observable#create} already wraps Observables so this is
* generally redundant.
* *
* @param <T> * @param <T> The type of the observable sequence.
* The type of the observable sequence.
*/ */
public final class OperationSynchronize<T> { public final class OperationSynchronize<T> {
/** /**
* Accepts an observable and wraps it in another observable which ensures that the resulting observable is well-behaved. * Accepts an observable and wraps it in another observable which ensures
* that the resulting observable is well-behaved.
* *
* A well-behaved observable ensures onNext, onCompleted, or onError calls to its subscribers are * A well-behaved observable ensures onNext, onCompleted, or onError calls
* not interleaved, onCompleted and onError are only called once respectively, and no * to its subscribers are not interleaved, onCompleted and onError are only
* onNext calls follow onCompleted and onError calls. * called once respectively, and no onNext calls follow onCompleted and
* onError calls.
* *
* @param observable * @param observable
* @param <T> * @param <T>
*
* @return * @return
*/ */
public static <T> Func1<Observer<T>, Subscription> synchronize(Observable<T> observable) { public static <T> Func1<Observer<T>, Subscription> synchronize(Observable<T> observable) {

View File

@@ -25,15 +25,18 @@ import rx.util.AtomicObservableSubscription;
import rx.util.functions.Func1; import rx.util.functions.Func1;
/** /**
* Returns a specified number of contiguous values from the start of an observable sequence. * Returns a specified number of contiguous values from the start of an
* observable sequence.
*/ */
public final class OperationTake { public final class OperationTake {
/** /**
* Returns a specified number of contiguous values from the start of an observable sequence. * Returns a specified number of contiguous values from the start of an
* observable sequence.
* *
* @param items * @param items
* @param num * @param num
*
* @return * @return
*/ */
public static <T> Func1<Observer<T>, Subscription> take(final Observable<T> items, final int num) { public static <T> Func1<Observer<T>, Subscription> take(final Observable<T> items, final int num) {
@@ -42,17 +45,24 @@ public final class OperationTake {
} }
/** /**
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads. * This class is NOT thread-safe if invoked and referenced multiple times.
* In other words, don't subscribe to it multiple times from different
* threads.
* <p> * <p>
* It IS thread-safe from within it while receiving onNext events from multiple threads. * It IS thread-safe from within it while receiving onNext events from
* multiple threads.
* <p> * <p>
* This should all be fine as long as it's kept as a private class and a new instance created from static factory method above. * This should all be fine as long as it's kept as a private class and a new
* instance created from static factory method above.
* <p> * <p>
* Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow. * Note how the take() factory method above protects us from a single
* instance being exposed with the Observable wrapper handling the subscribe
* flow.
* *
* @param <T> * @param <T>
*/ */
private static class Take<T> implements Func1<Observer<T>, Subscription> { private static class Take<T> implements Func1<Observer<T>, Subscription> {
private final AtomicInteger counter = new AtomicInteger(); private final AtomicInteger counter = new AtomicInteger();
private final Observable<T> items; private final Observable<T> items;
private final int num; private final int num;
@@ -66,21 +76,17 @@ public final class OperationTake {
@Override @Override
public Subscription call(Observer<T> observer) { public Subscription call(Observer<T> observer) {
if (num < 1) { if (num < 1) {
items.subscribe(new Observer<T>() items.subscribe(new Observer<T>() {
{
@Override @Override
public void onCompleted() public void onCompleted() {
{
} }
@Override @Override
public void onError(Exception e) public void onError(Exception e) {
{
} }
@Override @Override
public void onNext(T args) public void onNext(T args) {
{
} }
}).unsubscribe(); }).unsubscribe();
observer.onCompleted(); observer.onCompleted();
@@ -91,6 +97,7 @@ public final class OperationTake {
} }
private class ItemObserver implements Observer<T> { private class ItemObserver implements Observer<T> {
private final Observer<T> observer; private final Observer<T> observer;
public ItemObserver(Observer<T> observer) { public ItemObserver(Observer<T> observer) {
@@ -99,32 +106,28 @@ public final class OperationTake {
@Override @Override
public void onCompleted() { public void onCompleted() {
if (counter.getAndSet(num) < num) { if (counter.getAndSet(num) < num)
observer.onCompleted(); observer.onCompleted();
} }
}
@Override @Override
public void onError(Exception e) { public void onError(Exception e) {
if (counter.getAndSet(num) < num) { if (counter.getAndSet(num) < num)
observer.onError(e); observer.onError(e);
} }
}
@Override @Override
public void onNext(T args) { public void onNext(T args) {
final int count = counter.incrementAndGet(); final int count = counter.incrementAndGet();
if (count <= num) { if (count <= num) {
observer.onNext(args); observer.onNext(args);
if (count == num) { if (count == num)
observer.onCompleted(); observer.onCompleted();
} }
} if (count >= num)
if (count >= num) {
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable // this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
subscription.unsubscribe(); subscription.unsubscribe();
} }
}
} }

View File

@@ -25,7 +25,8 @@ import rx.util.AtomicObservableSubscription;
import rx.util.functions.Func1; import rx.util.functions.Func1;
/** /**
* Returns a specified number of contiguous elements from the end of an observable sequence. * Returns a specified number of contiguous elements from the end of an
* observable sequence.
*/ */
public final class OperationTakeLast { public final class OperationTakeLast {
@@ -34,6 +35,7 @@ public final class OperationTakeLast {
} }
private static class TakeLast<T> implements Func1<Observer<T>, Subscription> { private static class TakeLast<T> implements Func1<Observer<T>, Subscription> {
private final int count; private final int count;
private final Observable<T> items; private final Observable<T> items;
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
@@ -60,9 +62,8 @@ public final class OperationTakeLast {
@Override @Override
public void onCompleted() { public void onCompleted() {
Iterator<T> reverse = deque.descendingIterator(); Iterator<T> reverse = deque.descendingIterator();
while (reverse.hasNext()) { while (reverse.hasNext())
observer.onNext(reverse.next()); observer.onNext(reverse.next());
}
observer.onCompleted(); observer.onCompleted();
} }
@@ -73,10 +74,9 @@ public final class OperationTakeLast {
@Override @Override
public void onNext(T args) { public void onNext(T args) {
while (!deque.offerFirst(args)) { while (!deque.offerFirst(args))
deque.removeLast(); deque.removeLast();
} }
}
} }

View File

@@ -26,16 +26,18 @@ import rx.util.functions.Func1;
import rx.util.functions.Func2; import rx.util.functions.Func2;
/** /**
* Returns values from an observable sequence as long as a specified condition is true, and then skips the remaining values. * Returns values from an observable sequence as long as a specified condition
* is true, and then skips the remaining values.
*/ */
public final class OperationTakeWhile { public final class OperationTakeWhile {
/** /**
* Returns a specified number of contiguous values from the start of an observable sequence. * Returns a specified number of contiguous values from the start of an
* observable sequence.
* *
* @param items * @param items
* @param predicate * @param predicate a function to test each source element for a condition
* a function to test each source element for a condition *
* @return * @return
*/ */
public static <T> Func1<Observer<T>, Subscription> takeWhile(final Observable<T> items, final Func1<T, Boolean> predicate) { public static <T> Func1<Observer<T>, Subscription> takeWhile(final Observable<T> items, final Func1<T, Boolean> predicate) {
@@ -43,11 +45,14 @@ public final class OperationTakeWhile {
} }
/** /**
* Returns values from an observable sequence as long as a specified condition is true, and then skips the remaining values. * Returns values from an observable sequence as long as a specified
* condition is true, and then skips the remaining values.
* *
* @param items * @param items
* @param predicate * @param predicate a function to test each element for a condition; the
* a function to test each element for a condition; the second parameter of the function represents the index of the source element; otherwise, false. * second parameter of the function represents the index of the source
* element; otherwise, false.
*
* @return * @return
*/ */
public static <T> Func1<Observer<T>, Subscription> takeWhileWithIndex(final Observable<T> items, final Func2<T, Integer, Boolean> predicate) { public static <T> Func1<Observer<T>, Subscription> takeWhileWithIndex(final Observable<T> items, final Func2<T, Integer, Boolean> predicate) {
@@ -60,17 +65,24 @@ public final class OperationTakeWhile {
} }
/** /**
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads. * This class is NOT thread-safe if invoked and referenced multiple times.
* In other words, don't subscribe to it multiple times from different
* threads.
* <p> * <p>
* It IS thread-safe from within it while receiving onNext events from multiple threads. * It IS thread-safe from within it while receiving onNext events from
* multiple threads.
* <p> * <p>
* This should all be fine as long as it's kept as a private class and a new instance created from static factory method above. * This should all be fine as long as it's kept as a private class and a new
* instance created from static factory method above.
* <p> * <p>
* Note how the takeWhileWithIndex() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow. * Note how the takeWhileWithIndex() factory method above protects us from a
* single instance being exposed with the Observable wrapper handling the
* subscribe flow.
* *
* @param <T> * @param <T>
*/ */
private static class TakeWhile<T> implements Func1<Observer<T>, Subscription> { private static class TakeWhile<T> implements Func1<Observer<T>, Subscription> {
private final AtomicInteger counter = new AtomicInteger(); private final AtomicInteger counter = new AtomicInteger();
private final Observable<T> items; private final Observable<T> items;
private final Func2<T, Integer, Boolean> predicate; private final Func2<T, Integer, Boolean> predicate;
@@ -87,6 +99,7 @@ public final class OperationTakeWhile {
} }
private class ItemObserver implements Observer<T> { private class ItemObserver implements Observer<T> {
private final Observer<T> observer; private final Observer<T> observer;
public ItemObserver(Observer<T> observer) { public ItemObserver(Observer<T> observer) {
@@ -115,9 +128,9 @@ public final class OperationTakeWhile {
observer.onError(e); observer.onError(e);
return; return;
} }
if (isSelected) { if (isSelected)
observer.onNext(args); observer.onNext(args);
} else { else {
observer.onCompleted(); observer.onCompleted();
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable // this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
subscription.unsubscribe(); subscription.unsubscribe();

View File

@@ -24,7 +24,9 @@ import rx.subscriptions.Subscriptions;
import rx.util.functions.Func1; import rx.util.functions.Func1;
public class OperationToObservableFuture { public class OperationToObservableFuture {
private static class ToObservableFuture<T> implements Func1<Observer<T>, Subscription> { private static class ToObservableFuture<T> implements Func1<Observer<T>, Subscription> {
private final Future<T> that; private final Future<T> that;
private final Long time; private final Long time;
private final TimeUnit unit; private final TimeUnit unit;
@@ -46,9 +48,8 @@ public class OperationToObservableFuture {
try { try {
T value = (time == null) ? that.get() : that.get(time, unit); T value = (time == null) ? that.get() : that.get(time, unit);
if (!that.isCancelled()) { if (!that.isCancelled())
observer.onNext(value); observer.onNext(value);
}
observer.onCompleted(); observer.onCompleted();
} catch (Exception e) { } catch (Exception e) {
observer.onError(e); observer.onError(e);

View File

@@ -23,8 +23,7 @@ import rx.util.functions.Func1;
/** /**
* Accepts an Iterable object and exposes it as an Observable. * Accepts an Iterable object and exposes it as an Observable.
* *
* @param <T> * @param <T> The type of the Iterable sequence.
* The type of the Iterable sequence.
*/ */
public final class OperationToObservableIterable<T> { public final class OperationToObservableIterable<T> {

View File

@@ -43,6 +43,7 @@ public final class OperationToObservableList<T> {
return that.subscribe(new Observer<T>() { return that.subscribe(new Observer<T>() {
final ConcurrentLinkedQueue<T> list = new ConcurrentLinkedQueue<>(); final ConcurrentLinkedQueue<T> list = new ConcurrentLinkedQueue<>();
@Override @Override
public void onNext(T value) { public void onNext(T value) {
// onNext can be concurrently executed so list must be thread-safe // onNext can be concurrently executed so list must be thread-safe

View File

@@ -27,7 +27,8 @@ import rx.util.functions.Func1;
import rx.util.functions.Func2; import rx.util.functions.Func2;
/** /**
* Similar to toList in that it converts a sequence<T> into a List<T> except that it accepts a Function that will provide an implementation of Comparator. * Similar to toList in that it converts a sequence<T> into a List<T> except
* that it accepts a Function that will provide an implementation of Comparator.
* *
* @param <T> * @param <T>
*/ */
@@ -37,8 +38,8 @@ public final class OperationToObservableSortedList<T> {
* Sort T objects by their natural order (object must implement Comparable). * Sort T objects by their natural order (object must implement Comparable).
* *
* @param sequence * @param sequence
* @throws ClassCastException *
* if T objects do not implement Comparable * @throws ClassCastException if T objects do not implement Comparable
* @return * @return
*/ */
public static <T> Func1<Observer<List<T>>, Subscription> toSortedList(Observable<T> sequence) { public static <T> Func1<Observer<List<T>>, Subscription> toSortedList(Observable<T> sequence) {
@@ -50,6 +51,7 @@ public final class OperationToObservableSortedList<T> {
* *
* @param sequence * @param sequence
* @param sortFunction * @param sortFunction
*
* @return * @return
*/ */
public static <T> Func1<Observer<List<T>>, Subscription> toSortedList(Observable<T> sequence, Func2<T, T, Integer> sortFunction) { public static <T> Func1<Observer<List<T>>, Subscription> toSortedList(Observable<T> sequence, Func2<T, T, Integer> sortFunction) {
@@ -92,9 +94,8 @@ public final class OperationToObservableSortedList<T> {
try { try {
// copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface // copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface
ArrayList<T> l = new ArrayList<>(list.size()); ArrayList<T> l = new ArrayList<>(list.size());
for (T t : list) { for (T t : list)
l.add(t); l.add(t);
}
// sort the list before delivery // sort the list before delivery
Collections.sort(l, (T o1, T o2) -> sortFunction.call(o1, o2)); Collections.sort(l, (T o1, T o2) -> sortFunction.call(o1, o2));

View File

@@ -61,6 +61,7 @@ public final class OperationZip {
* ThreadSafe * ThreadSafe
*/ */
private static class ZipObserver<R, T> implements Observer<T> { private static class ZipObserver<R, T> implements Observer<T> {
final Observable<T> w; final Observable<T> w;
final Aggregator<R> a; final Aggregator<R> a;
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
@@ -72,11 +73,10 @@ public final class OperationZip {
} }
public void startWatching() { public void startWatching() {
if (subscribed.compareAndSet(false, true)) { if (subscribed.compareAndSet(false, true))
// only subscribe once even if called more than once // only subscribe once even if called more than once
subscription.wrap(w.subscribe(this)); subscription.wrap(w.subscribe(this));
} }
}
@Override @Override
public void onCompleted() { public void onCompleted() {
@@ -99,7 +99,9 @@ public final class OperationZip {
} }
/** /**
* Receive notifications from each of the Observables we are reducing and execute the zipFunction whenever we have received events from all Observables. * Receive notifications from each of the Observables we are reducing and
* execute the zipFunction whenever we have received events from all
* Observables.
* *
* This class is thread-safe. * This class is thread-safe.
* *
@@ -113,9 +115,17 @@ public final class OperationZip {
private final AtomicBoolean running = new AtomicBoolean(true); private final AtomicBoolean running = new AtomicBoolean(true);
private final ConcurrentHashMap<ZipObserver<T, ?>, Boolean> completed = new ConcurrentHashMap<>(); private final ConcurrentHashMap<ZipObserver<T, ?>, Boolean> completed = new ConcurrentHashMap<>();
/* we use ConcurrentHashMap despite synchronization of methods because stop() does NOT use synchronization and this map is used by it and can be called by other threads */ /*
* we use ConcurrentHashMap despite synchronization of methods because
* stop() does NOT use synchronization and this map is used by it and
* can be called by other threads
*/
private final ConcurrentHashMap<ZipObserver<T, ?>, ConcurrentLinkedQueue<Object>> receivedValuesPerObserver = new ConcurrentHashMap<>(); private final ConcurrentHashMap<ZipObserver<T, ?>, ConcurrentLinkedQueue<Object>> receivedValuesPerObserver = new ConcurrentHashMap<>();
/* we use a ConcurrentLinkedQueue to retain ordering (I'd like to just use a ConcurrentLinkedHashMap for 'receivedValuesPerObserver' but that doesn't exist in standard java */ /*
* we use a ConcurrentLinkedQueue to retain ordering (I'd like to just
* use a ConcurrentLinkedHashMap for 'receivedValuesPerObserver' but
* that doesn't exist in standard java
*/
private final ConcurrentLinkedQueue<ZipObserver<T, ?>> observers = new ConcurrentLinkedQueue<>(); private final ConcurrentLinkedQueue<ZipObserver<T, ?>> observers = new ConcurrentLinkedQueue<>();
public Aggregator(FuncN<T> zipFunction) { public Aggregator(FuncN<T> zipFunction) {
@@ -123,9 +133,12 @@ public final class OperationZip {
} }
/** /**
* Receive notification of a Observer starting (meaning we should require it for aggregation) * Receive notification of a Observer starting (meaning we should
* require it for aggregation)
* *
* Thread Safety => Invoke ONLY from the static factory methods at top of this class which are always an atomic execution by a single thread. * Thread Safety => Invoke ONLY from the static factory methods at top
* of this class which are always an atomic execution by a single
* thread.
* *
* @param w * @param w
*/ */
@@ -144,17 +157,16 @@ public final class OperationZip {
// store that this ZipObserver is completed // store that this ZipObserver is completed
completed.put(w, Boolean.TRUE); completed.put(w, Boolean.TRUE);
// if all ZipObservers are completed, we mark the whole thing as completed // if all ZipObservers are completed, we mark the whole thing as completed
if (completed.size() == observers.size()) { if (completed.size() == observers.size())
if (running.compareAndSet(true, false)) { if (running.compareAndSet(true, false))
// this thread succeeded in setting running=false so let's propagate the completion // this thread succeeded in setting running=false so let's propagate the completion
// mark ourselves as done // mark ourselves as done
observer.onCompleted(); observer.onCompleted();
} }
}
}
/** /**
* Receive error for a Observer. Throw the error up the chain and stop processing. * Receive error for a Observer. Throw the error up the chain and stop
* processing.
* *
* @param w * @param w
*/ */
@@ -162,7 +174,9 @@ public final class OperationZip {
if (running.compareAndSet(true, false)) { if (running.compareAndSet(true, false)) {
// this thread succeeded in setting running=false so let's propagate the error // this thread succeeded in setting running=false so let's propagate the error
observer.onError(e); observer.onError(e);
/* since we receive an error we want to tell everyone to stop */ /*
* since we receive an error we want to tell everyone to stop
*/
stop(); stop();
} }
} }
@@ -170,20 +184,23 @@ public final class OperationZip {
/** /**
* Receive the next value from a Observer. * Receive the next value from a Observer.
* <p> * <p>
* If we have received values from all Observers, trigger the zip function, otherwise store the value and keep waiting. * If we have received values from all Observers, trigger the zip
* function, otherwise store the value and keep waiting.
* *
* @param w * @param w
* @param arg * @param arg
*/ */
void next(ZipObserver<T, ?> w, Object arg) { void next(ZipObserver<T, ?> w, Object arg) {
if (observer == null) { if (observer == null)
throw new RuntimeException("This shouldn't be running if a Observer isn't registered"); throw new RuntimeException("This shouldn't be running if a Observer isn't registered");
}
/* if we've been 'unsubscribed' don't process anything further even if the things we're watching keep sending (likely because they are not responding to the unsubscribe call) */ /*
if (!running.get()) { * if we've been 'unsubscribed' don't process anything further even
* if the things we're watching keep sending (likely because they
* are not responding to the unsubscribe call)
*/
if (!running.get())
return; return;
}
// store the value we received and below we'll decide if we are to send it to the Observer // store the value we received and below we'll decide if we are to send it to the Observer
receivedValuesPerObserver.get(w).add(arg); receivedValuesPerObserver.get(w).add(arg);
@@ -191,21 +208,22 @@ public final class OperationZip {
// define here so the variable is out of the synchronized scope // define here so the variable is out of the synchronized scope
Object[] argsToZip = new Object[observers.size()]; Object[] argsToZip = new Object[observers.size()];
/* we have to synchronize here despite using concurrent data structures because the compound logic here must all be done atomically */ /*
* we have to synchronize here despite using concurrent data
* structures because the compound logic here must all be done
* atomically
*/
synchronized (this) { synchronized (this) {
// if all ZipObservers in 'receivedValues' map have a value, invoke the zipFunction // if all ZipObservers in 'receivedValues' map have a value, invoke the zipFunction
for (ZipObserver<T, ?> rw : receivedValuesPerObserver.keySet()) { for (ZipObserver<T, ?> rw : receivedValuesPerObserver.keySet())
if (receivedValuesPerObserver.get(rw).peek() == null) { if (receivedValuesPerObserver.get(rw).peek() == null)
// we have a null meaning the queues aren't all populated so won't do anything // we have a null meaning the queues aren't all populated so won't do anything
return; return;
}
}
// if we get to here this means all the queues have data // if we get to here this means all the queues have data
int i = 0; int i = 0;
for (ZipObserver<T, ?> rw : observers) { for (ZipObserver<T, ?> rw : observers)
argsToZip[i++] = receivedValuesPerObserver.get(rw).remove(); argsToZip[i++] = receivedValuesPerObserver.get(rw).remove();
} }
}
// if we did not return above from the synchronized block we can now invoke the zipFunction with all of the args // if we did not return above from the synchronized block we can now invoke the zipFunction with all of the args
// we do this outside the synchronized block as it is now safe to call this concurrently and don't need to block other threads from calling // we do this outside the synchronized block as it is now safe to call this concurrently and don't need to block other threads from calling
// this 'next' method while another thread finishes calling this zipFunction // this 'next' method while another thread finishes calling this zipFunction
@@ -217,23 +235,27 @@ public final class OperationZip {
if (started.compareAndSet(false, true)) { if (started.compareAndSet(false, true)) {
AtomicObservableSubscription subscription = new AtomicObservableSubscription(); AtomicObservableSubscription subscription = new AtomicObservableSubscription();
this.observer = new SynchronizedObserver<>(observer, subscription); this.observer = new SynchronizedObserver<>(observer, subscription);
/* start the Observers */ /*
for (ZipObserver<T, ?> rw : observers) { * start the Observers
*/
for (ZipObserver<T, ?> rw : observers)
rw.startWatching(); rw.startWatching();
}
return subscription.wrap(this::stop); return subscription.wrap(this::stop);
} else { } else
/* a Observer already has subscribed so blow up */ /*
* a Observer already has subscribed so blow up
*/
throw new IllegalStateException("Only one Observer can subscribe to this Observable."); throw new IllegalStateException("Only one Observer can subscribe to this Observable.");
} }
}
/* /*
* Do NOT synchronize this because it gets called via unsubscribe which can occur on other threads * Do NOT synchronize this because it gets called via unsubscribe which
* can occur on other threads
* and result in deadlocks. (http://jira/browse/API-4060) * and result in deadlocks. (http://jira/browse/API-4060)
* *
* AtomicObservableSubscription uses compareAndSet instead of locking to avoid deadlocks but ensure single-execution. * AtomicObservableSubscription uses compareAndSet instead of locking to
* avoid deadlocks but ensure single-execution.
* *
* We do the same in the implementation of this method. * We do the same in the implementation of this method.
* *
@@ -243,16 +265,19 @@ public final class OperationZip {
* - ZipObserver.subscription being an AtomicObservableSubscription * - ZipObserver.subscription being an AtomicObservableSubscription
*/ */
private void stop() { private void stop() {
/* tell ourselves to stop processing onNext events by setting running=false */ /*
if (running.compareAndSet(true, false)) { * tell ourselves to stop processing onNext events by setting
/* propogate to all Observers to unsubscribe if this thread succeeded in setting running=false */ * running=false
for (ZipObserver<T, ?> o : observers) { */
if (o.subscription != null) { if (running.compareAndSet(true, false))
/*
* propogate to all Observers to unsubscribe if this thread
* succeeded in setting running=false
*/
for (ZipObserver<T, ?> o : observers)
if (o.subscription != null)
o.subscription.unsubscribe(); o.subscription.unsubscribe();
} }
}
}
}
} }

View File

@@ -46,6 +46,7 @@ public final class OperatorGroupBy {
} }
private static class GroupBy<K, V> implements Func1<Observer<GroupedObservable<K, V>>, Subscription> { private static class GroupBy<K, V> implements Func1<Observer<GroupedObservable<K, V>>, Subscription> {
private final Observable<KeyValue<K, V>> source; private final Observable<KeyValue<K, V>> source;
private final ConcurrentHashMap<K, Boolean> keys = new ConcurrentHashMap<K, Boolean>(); private final ConcurrentHashMap<K, Boolean> keys = new ConcurrentHashMap<K, Boolean>();
@@ -72,10 +73,9 @@ public final class OperatorGroupBy {
public void onNext(final KeyValue<K, V> args) { public void onNext(final KeyValue<K, V> args) {
K key = args.key; K key = args.key;
boolean newGroup = keys.putIfAbsent(key, true) == null; boolean newGroup = keys.putIfAbsent(key, true) == null;
if (newGroup) { if (newGroup)
observer.onNext(buildObservableFor(source, key)); observer.onNext(buildObservableFor(source, key));
} }
}
}); });
} }
@@ -104,6 +104,7 @@ public final class OperatorGroupBy {
} }
private static class KeyValue<K, V> { private static class KeyValue<K, V> {
private final K key; private final K key;
private final V value; private final V value;

View File

@@ -23,17 +23,18 @@ import rx.util.functions.Func1;
public class OperatorTakeUntil { public class OperatorTakeUntil {
/** /**
* Returns the values from the source observable sequence until the other observable sequence produces a value. * Returns the values from the source observable sequence until the other
* observable sequence produces a value.
* *
* @param source * @param source the source sequence to propagate elements for.
* the source sequence to propagate elements for. * @param other the observable sequence that terminates propagation of
* @param other * elements of the source sequence.
* the observable sequence that terminates propagation of elements of the source sequence. * @param <T> the type of source.
* @param <T> * @param <E> the other type.
* the type of source. *
* @param <E> * @return An observable sequence containing the elements of the source
* the other type. * sequence up to the point the other sequence interrupted further
* @return An observable sequence containing the elements of the source sequence up to the point the other sequence interrupted further propagation. * propagation.
*/ */
public static <T, E> Observable<T> takeUntil(final Observable<T> source, final Observable<E> other) { public static <T, E> Observable<T> takeUntil(final Observable<T> source, final Observable<E> other) {
Observable<Notification<T>> s = Observable.create(new SourceObservable<T>(source)); Observable<Notification<T>> s = Observable.create(new SourceObservable<T>(source));
@@ -41,8 +42,8 @@ public class OperatorTakeUntil {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
/** /**
* In JDK 7 we could use 'varargs' instead of 'unchecked'. * In JDK 7 we could use 'varargs' instead of 'unchecked'. See
* See http://stackoverflow.com/questions/1445233/is-it-possible-to-solve-the-a-generic-array-of-t-is-created-for-a-varargs-param * http://stackoverflow.com/questions/1445233/is-it-possible-to-solve-the-a-generic-array-of-t-is-created-for-a-varargs-param
* and http://hg.openjdk.java.net/jdk7/tl/langtools/rev/46cf751559ae * and http://hg.openjdk.java.net/jdk7/tl/langtools/rev/46cf751559ae
*/ */
Observable<Notification<T>> result = Observable.merge(s, o); Observable<Notification<T>> result = Observable.merge(s, o);
@@ -61,6 +62,7 @@ public class OperatorTakeUntil {
} }
private static class Notification<T> { private static class Notification<T> {
private final boolean halt; private final boolean halt;
private final T value; private final T value;
@@ -80,6 +82,7 @@ public class OperatorTakeUntil {
} }
private static class SourceObservable<T> implements Func1<Observer<Notification<T>>, Subscription> { private static class SourceObservable<T> implements Func1<Observer<Notification<T>>, Subscription> {
private final Observable<T> sequence; private final Observable<T> sequence;
private SourceObservable(Observable<T> sequence) { private SourceObservable(Observable<T> sequence) {
@@ -108,6 +111,7 @@ public class OperatorTakeUntil {
} }
private static class OtherObservable<T, E> implements Func1<Observer<Notification<T>>, Subscription> { private static class OtherObservable<T, E> implements Func1<Observer<Notification<T>>, Subscription> {
private final Observable<E> sequence; private final Observable<E> sequence;
private OtherObservable(Observable<E> sequence) { private OtherObservable(Observable<E> sequence) {

View File

@@ -17,11 +17,11 @@ public class OperatorToIterator {
/** /**
* Returns an iterator that iterates all values of the observable. * Returns an iterator that iterates all values of the observable.
* *
* @param that * @param that an observable sequence to get an iterator for.
* an observable sequence to get an iterator for. * @param <T> the type of source.
* @param <T> *
* the type of source. * @return the iterator that could be used to iterate over the elements of
* @return the iterator that could be used to iterate over the elements of the observable. * the observable.
*/ */
public static <T> Iterator<T> toIterator(Observable<T> that) { public static <T> Iterator<T> toIterator(Observable<T> that) {
final BlockingQueue<Notification<T>> notifications = new LinkedBlockingQueue<Notification<T>>(); final BlockingQueue<Notification<T>> notifications = new LinkedBlockingQueue<Notification<T>>();
@@ -48,20 +48,17 @@ public class OperatorToIterator {
@Override @Override
public boolean hasNext() { public boolean hasNext() {
if (buf == null) { if (buf == null)
buf = take(); buf = take();
}
return !buf.isOnCompleted(); return !buf.isOnCompleted();
} }
@Override @Override
public T next() { public T next() {
if (buf == null) { if (buf == null)
buf = take(); buf = take();
} if (buf.isOnError())
if (buf.isOnError()) {
throw Exceptions.propagate(buf.getException()); throw Exceptions.propagate(buf.getException());
}
T result = buf.getValue(); T result = buf.getValue();
buf = null; buf = null;

Some files were not shown because too many files have changed in this diff Show More