To make death I removed RxJava
This commit is contained in:
@@ -47,7 +47,6 @@ import org.jackhuang.hellominecraft.lookandfeel.HelloMinecraftLookAndFeel;
|
||||
import org.jackhuang.hellominecraft.utils.MathUtils;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.utils.VersionNumber;
|
||||
import rx.concurrency.Schedulers;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -146,8 +145,7 @@ public final class Main implements Runnable {
|
||||
}
|
||||
|
||||
Settings.UPDATE_CHECKER.outdated.register(IUpgrader.NOW_UPGRADER);
|
||||
Settings.UPDATE_CHECKER.process(false).subscribeOn(Schedulers.newThread()).subscribe(t
|
||||
-> Main.invokeUpdate());
|
||||
Settings.UPDATE_CHECKER.process(false).reg(t -> Main.invokeUpdate()).execute();
|
||||
|
||||
if (StrUtils.isNotBlank(Settings.getInstance().getProxyHost()) && StrUtils.isNotBlank(Settings.getInstance().getProxyPort()) && MathUtils.canParseInt(Settings.getInstance().getProxyPort())) {
|
||||
HMCLog.log("Initializing customized proxy");
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.jackhuang.hellominecraft.launcher.core.assets;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
@@ -30,9 +29,8 @@ import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.launcher.core.download.IDownloadProvider;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.utils.OverridableSwingWorker;
|
||||
import org.jackhuang.hellominecraft.utils.VersionNumber;
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -45,61 +43,42 @@ public class AssetsMojangLoader extends IAssetsHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<String[]> getList(MinecraftVersion mv, IMinecraftAssetService mp) {
|
||||
return Observable.<String[]>createWithEmptySubscription((Observer<String[]> t1) -> {
|
||||
if (mv == null) {
|
||||
t1.onError(null);
|
||||
return;
|
||||
}
|
||||
String assetsId = mv.assets == null ? "legacy" : mv.assets;
|
||||
File assets = mp.getAssets();
|
||||
HMCLog.log("Get index: " + assetsId);
|
||||
File f = IOUtils.tryGetCanonicalFile(new File(assets, "indexes/" + assetsId + ".json"));
|
||||
if (!f.exists() && !mp.downloadMinecraftAssetsIndex(assetsId)) {
|
||||
t1.onError(null);
|
||||
return;
|
||||
}
|
||||
public OverridableSwingWorker<String[]> getList(MinecraftVersion mv, IMinecraftAssetService mp) {
|
||||
return new OverridableSwingWorker<String[]>() {
|
||||
@Override
|
||||
protected void work() throws Exception {
|
||||
if (mv == null)
|
||||
throw new IllegalArgumentException("AssetsMojangLoader: null argument: MinecraftVersion");
|
||||
String assetsId = mv.assets == null ? "legacy" : mv.assets;
|
||||
File assets = mp.getAssets();
|
||||
HMCLog.log("Gathering asset index: " + assetsId);
|
||||
File f = IOUtils.tryGetCanonicalFile(new File(assets, "indexes/" + assetsId + ".json"));
|
||||
if (!f.exists() && !mp.downloadMinecraftAssetsIndex(assetsId))
|
||||
throw new IllegalStateException("Failed to get index json");
|
||||
|
||||
String result;
|
||||
try {
|
||||
result = FileUtils.readFileToString(f);
|
||||
} catch (IOException ex) {
|
||||
HMCLog.warn("Failed to read index json: " + f, ex);
|
||||
t1.onError(null);
|
||||
return;
|
||||
}
|
||||
if (StrUtils.isBlank(result)) {
|
||||
HMCLog.err("Index json is empty, please redownload it!");
|
||||
t1.onError(null);
|
||||
return;
|
||||
}
|
||||
AssetsIndex o;
|
||||
try {
|
||||
o = C.gson.fromJson(result, AssetsIndex.class);
|
||||
} catch (Exception e) {
|
||||
HMCLog.err("Failed to parse index json, please redownload it!", e);
|
||||
t1.onError(null);
|
||||
return;
|
||||
}
|
||||
assetsDownloadURLs = new ArrayList<>();
|
||||
assetsLocalNames = new ArrayList<>();
|
||||
ArrayList<String> al = new ArrayList<>();
|
||||
contents = new ArrayList<>();
|
||||
if (o != null && o.getFileMap() != null)
|
||||
for (Map.Entry<String, AssetsObject> e : o.getFileMap().entrySet()) {
|
||||
Contents c = new Contents();
|
||||
c.eTag = e.getValue().getHash();
|
||||
c.key = c.eTag.substring(0, 2) + "/" + e.getValue().getHash();
|
||||
c.size = e.getValue().getSize();
|
||||
contents.add(c);
|
||||
assetsDownloadURLs.add(c.key);
|
||||
assetsLocalNames.add(new File(assets, "objects" + File.separator + c.key.replace("/", File.separator)));
|
||||
al.add(e.getKey());
|
||||
}
|
||||
String result = FileUtils.readFileToString(f);
|
||||
if (StrUtils.isBlank(result))
|
||||
throw new IllegalStateException("Index json is empty, please redownload it!");
|
||||
AssetsIndex o = C.gson.fromJson(result, AssetsIndex.class);
|
||||
assetsDownloadURLs = new ArrayList<>();
|
||||
assetsLocalNames = new ArrayList<>();
|
||||
ArrayList<String> al = new ArrayList<>();
|
||||
contents = new ArrayList<>();
|
||||
if (o != null && o.getFileMap() != null)
|
||||
for (Map.Entry<String, AssetsObject> e : o.getFileMap().entrySet()) {
|
||||
Contents c = new Contents();
|
||||
c.eTag = e.getValue().getHash();
|
||||
c.key = c.eTag.substring(0, 2) + "/" + e.getValue().getHash();
|
||||
c.size = e.getValue().getSize();
|
||||
contents.add(c);
|
||||
assetsDownloadURLs.add(c.key);
|
||||
assetsLocalNames.add(new File(assets, "objects" + File.separator + c.key.replace("/", File.separator)));
|
||||
al.add(e.getKey());
|
||||
}
|
||||
|
||||
t1.onNext(al.toArray(new String[1]));
|
||||
t1.onCompleted();
|
||||
});
|
||||
publish(al.toArray(new String[1]));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.jackhuang.hellominecraft.utils.tasks.download.FileDownloadTask;
|
||||
import org.jackhuang.hellominecraft.utils.code.DigestUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.NetUtils;
|
||||
import rx.Observable;
|
||||
import org.jackhuang.hellominecraft.utils.OverridableSwingWorker;
|
||||
|
||||
/**
|
||||
* Assets
|
||||
@@ -73,7 +73,7 @@ public abstract class IAssetsHandler {
|
||||
* @param mp Asset Service
|
||||
* @param x finished event
|
||||
*/
|
||||
public abstract Observable<String[]> getList(MinecraftVersion mv, IMinecraftAssetService mp);
|
||||
public abstract OverridableSwingWorker<String[]> getList(MinecraftVersion mv, IMinecraftAssetService mp);
|
||||
|
||||
/**
|
||||
* Will be invoked when the user invoked "Download all assets".
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.jackhuang.hellominecraft.utils.tasks.TaskWindow;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.download.FileDownloadTask;
|
||||
import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import rx.concurrency.Schedulers;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -49,9 +48,7 @@ public class MinecraftAssetService extends IMinecraftAssetService {
|
||||
public void executeTask() throws Throwable {
|
||||
IAssetsHandler type = IAssetsHandler.ASSETS_HANDLER;
|
||||
type.getList(service.version().getVersionById(mcVersion), service.asset())
|
||||
.subscribeOn(Schedulers.newThread())
|
||||
.observeOn(Schedulers.eventQueue())
|
||||
.subscribe((t) -> TaskWindow.getInstance().addTask(type.getDownloadTask(service.getDownloadType().getProvider())).start());
|
||||
.reg((t) -> TaskWindow.getInstance().addTask(type.getDownloadTask(service.getDownloadType().getProvider())).start()).execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -31,12 +31,12 @@ import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.TaskWindow;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.download.FileDownloadTask;
|
||||
import org.jackhuang.hellominecraft.utils.NetUtils;
|
||||
import org.jackhuang.hellominecraft.utils.OverridableSwingWorker;
|
||||
import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.Task;
|
||||
import org.jackhuang.hellominecraft.utils.version.MinecraftRemoteVersion;
|
||||
import org.jackhuang.hellominecraft.utils.version.MinecraftRemoteVersions;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -151,10 +151,14 @@ public class MinecraftDownloadService extends IMinecraftDownloadService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<MinecraftRemoteVersion> getRemoteVersions() {
|
||||
return NetUtils.getRx(service.getDownloadType().getProvider().getVersionsListDownloadURL())
|
||||
.map(r -> C.gson.fromJson(r, MinecraftRemoteVersions.class))
|
||||
.filter(r -> r != null && r.versions != null)
|
||||
.flatMap(r -> Observable.from(r.versions));
|
||||
public OverridableSwingWorker<MinecraftRemoteVersion> getRemoteVersions() {
|
||||
return new OverridableSwingWorker<MinecraftRemoteVersion>() {
|
||||
@Override
|
||||
protected void work() throws Exception {
|
||||
MinecraftRemoteVersions r = C.gson.fromJson(NetUtils.get(service.getDownloadType().getProvider().getVersionsListDownloadURL()), MinecraftRemoteVersions.class);
|
||||
if (r != null && r.versions != null)
|
||||
publish(r.versions.toArray(new MinecraftRemoteVersion[0]));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,7 @@ public class MinecraftLoader extends AbstractMinecraftLoader {
|
||||
|
||||
if (!checkAssetsExist())
|
||||
if (MessageBox.Show(C.i18n("assets.no_assets"), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION) {
|
||||
IAssetsHandler.ASSETS_HANDLER.getList(version, service.asset()).subscribe(a -> {
|
||||
});
|
||||
IAssetsHandler.ASSETS_HANDLER.getList(version, service.asset()).execute();
|
||||
TaskWindow.getInstance().addTask(IAssetsHandler.ASSETS_HANDLER.getDownloadTask(service.getDownloadType().getProvider())).start();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ import java.util.List;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.download.DownloadLibraryJob;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.utils.OverridableSwingWorker;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.Task;
|
||||
import org.jackhuang.hellominecraft.utils.version.MinecraftRemoteVersion;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -51,6 +51,6 @@ public abstract class IMinecraftDownloadService extends IMinecraftBasicService {
|
||||
*/
|
||||
public abstract List<DownloadLibraryJob> getDownloadLibraries(MinecraftVersion mv) throws GameException;
|
||||
|
||||
public abstract Observable<MinecraftRemoteVersion> getRemoteVersions();
|
||||
public abstract OverridableSwingWorker<MinecraftRemoteVersion> getRemoteVersions();
|
||||
|
||||
}
|
||||
|
||||
@@ -100,4 +100,8 @@ public class DefaultMinecraftService extends IMinecraftService {
|
||||
return new MinecraftLoader(options, this, p);
|
||||
}
|
||||
|
||||
public Profile getProfile() {
|
||||
return p;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ import org.jackhuang.hellominecraft.utils.VersionNumber;
|
||||
import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.OS;
|
||||
import rx.concurrency.Schedulers;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -91,7 +90,7 @@ public class AppDataUpgrader extends IUpgrader {
|
||||
|
||||
@Override
|
||||
public boolean call(Object sender, VersionNumber number) {
|
||||
((UpdateChecker) sender).requestDownloadLink().subscribeOn(Schedulers.newThread()).observeOn(Schedulers.eventQueue()).subscribe(map -> {
|
||||
((UpdateChecker) sender).requestDownloadLink().reg(map -> {
|
||||
if (map != null && map.containsKey("pack"))
|
||||
try {
|
||||
if (TaskWindow.getInstance().addTask(new AppDataUpgraderTask(map.get("pack"), number.version)).start()) {
|
||||
@@ -120,7 +119,7 @@ public class AppDataUpgrader extends IUpgrader {
|
||||
MessageBox.Show(C.i18n("update.no_browser"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}).execute();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
import org.jackhuang.hellominecraft.utils.views.SwingUtils;
|
||||
import rx.concurrency.Schedulers;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -108,13 +107,13 @@ public class GameDownloadPanel extends AnimatedPanel implements Selectable {
|
||||
public void refreshDownloads() {
|
||||
DefaultTableModel model = SwingUtils.clearDefaultTable(lstDownloads);
|
||||
gsp.getProfile().service().download().getRemoteVersions()
|
||||
.observeOn(Schedulers.eventQueue()).subscribeOn(Schedulers.newThread())
|
||||
.subscribe((ver) -> model.addRow(new Object[] { ver.id, ver.time,
|
||||
StrUtils.equalsOne(ver.type, "old_beta", "old_alpha", "release", "snapshot") ? C.i18n("versions." + ver.type) : ver.type }),
|
||||
(e) -> {
|
||||
.reg((ver) -> model.addRow(new Object[] { ver.id, ver.time,
|
||||
StrUtils.equalsOne(ver.type, "old_beta", "old_alpha", "release", "snapshot") ? C.i18n("versions." + ver.type) : ver.type }))
|
||||
.regDone(lstDownloads::updateUI).execute();
|
||||
/*(e) -> {
|
||||
MessageBox.Show("Failed to refresh download: " + e.getLocalizedMessage());
|
||||
HMCLog.err("Failed to refresh download.", e);
|
||||
}, lstDownloads::updateUI);
|
||||
}, );*/
|
||||
}
|
||||
|
||||
void downloadMinecraft() {
|
||||
|
||||
@@ -32,9 +32,7 @@ import java.awt.event.ItemEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
@@ -46,7 +44,6 @@ import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.launcher.core.GameException;
|
||||
import org.jackhuang.hellominecraft.launcher.core.LauncherVisibility;
|
||||
import org.jackhuang.hellominecraft.launcher.settings.Profile;
|
||||
import org.jackhuang.hellominecraft.launcher.settings.Settings;
|
||||
@@ -56,13 +53,12 @@ import org.jackhuang.hellominecraft.launcher.core.installers.InstallerType;
|
||||
import org.jackhuang.hellominecraft.launcher.core.mod.ModpackManager;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.GameDirType;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.launcher.views.modpack.ModpackInitializationPanel;
|
||||
import org.jackhuang.hellominecraft.launcher.views.modpack.ModpackWizard;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.TaskRunnable;
|
||||
import org.jackhuang.hellominecraft.utils.tasks.TaskWindow;
|
||||
import org.jackhuang.hellominecraft.utils.Event;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.OverridableSwingWorker;
|
||||
import org.jackhuang.hellominecraft.utils.version.MinecraftVersionRequest;
|
||||
import org.jackhuang.hellominecraft.utils.system.OS;
|
||||
import org.jackhuang.hellominecraft.utils.StrUtils;
|
||||
@@ -70,8 +66,6 @@ import org.jackhuang.hellominecraft.utils.system.FileUtils;
|
||||
import org.jackhuang.hellominecraft.utils.views.SwingUtils;
|
||||
import org.jackhuang.hellominecraft.utils.system.Java;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.api.WizardDisplayer;
|
||||
import rx.Observable;
|
||||
import rx.concurrency.Schedulers;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -1300,14 +1294,16 @@ public final class GameSettingsPanel extends AnimatedPanel implements DropTarget
|
||||
return;
|
||||
reloadingMods = true;
|
||||
DefaultTableModel model = SwingUtils.clearDefaultTable(lstExternalMods);
|
||||
Observable.<List<ModInfo>>createWithEmptySubscription(
|
||||
t -> t.onNext(getProfile().service().mod().recacheMods(getProfile().getSelectedVersion())))
|
||||
.subscribeOn(Schedulers.newThread()).observeOn(Schedulers.eventQueue())
|
||||
.subscribe(t -> {
|
||||
for (ModInfo x : t)
|
||||
model.addRow(new Object[] { x.isActive(), x, x.version });
|
||||
reloadingMods = false;
|
||||
});
|
||||
new OverridableSwingWorker<List<ModInfo>>() {
|
||||
@Override
|
||||
protected void work() throws Exception {
|
||||
publish(getProfile().service().mod().recacheMods(getProfile().getSelectedVersion()));
|
||||
}
|
||||
}.reg(t -> {
|
||||
for (ModInfo x : t)
|
||||
model.addRow(new Object[] { x.isActive(), x, x.version });
|
||||
reloadingMods = false;
|
||||
}).execute();
|
||||
}
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.jackhuang.hellominecraft.launcher.core.download.DownloadType;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.views.SwingUtils;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -43,8 +42,10 @@ public class LauncherSettingsPanel extends AnimatedPanel {
|
||||
public LauncherSettingsPanel() {
|
||||
initComponents();
|
||||
|
||||
Observable.from(DownloadType.values()).map(t -> t.getName()).toList()
|
||||
.subscribe(t -> cboDownloadSource.setModel(new DefaultComboBoxModel(t.toArray(new String[0]))));
|
||||
DefaultComboBoxModel d = new DefaultComboBoxModel();
|
||||
for (DownloadType type : DownloadType.values())
|
||||
d.addElement(type.getName());
|
||||
cboDownloadSource.setModel(d);
|
||||
|
||||
txtBackgroundPath.setText(Settings.getInstance().getBgpath());
|
||||
txtProxyHost.setText(Settings.getInstance().getProxyHost());
|
||||
|
||||
@@ -20,7 +20,6 @@ package org.jackhuang.hellominecraft.launcher.views.modpack;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -31,7 +30,6 @@ import org.jackhuang.hellominecraft.launcher.core.mod.ModpackManager;
|
||||
import org.jackhuang.hellominecraft.launcher.core.service.IMinecraftService;
|
||||
import org.jackhuang.hellominecraft.launcher.core.version.MinecraftVersion;
|
||||
import org.jackhuang.hellominecraft.utils.C;
|
||||
import org.jackhuang.hellominecraft.utils.MessageBox;
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import org.jackhuang.hellominecraft.utils.views.checktree.CheckBoxTreeNode;
|
||||
import org.jackhuang.hellominecraft.utils.views.wizard.spi.DeferredWizardResult;
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.jackhuang.hellominecraft.utils.functions.Predicate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -31,7 +30,9 @@ public final class CollectionUtils {
|
||||
|
||||
public static <T> ArrayList<T> map(Collection<T> coll, Predicate<T> p) {
|
||||
ArrayList<T> newColl = new ArrayList<>();
|
||||
Observable.from(coll).filter(t -> p.apply(t)).subscribe(t -> newColl.add(t));
|
||||
for (T t : coll)
|
||||
if (p.apply(t))
|
||||
newColl.add(t);
|
||||
return newColl;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.jackhuang.hellominecraft.utils;
|
||||
|
||||
import java.util.Map;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -50,13 +49,13 @@ public interface IUpdateChecker {
|
||||
*
|
||||
* @return the process observable.
|
||||
*/
|
||||
Observable process(boolean showMessage);
|
||||
OverridableSwingWorker<VersionNumber> process(boolean showMessage);
|
||||
|
||||
/**
|
||||
* Get the download links.
|
||||
*
|
||||
* @return a JSON, which contains the server response.
|
||||
*/
|
||||
Observable<Map<String, String>> requestDownloadLink();
|
||||
OverridableSwingWorker<Map<String, String>> requestDownloadLink();
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import org.jackhuang.hellominecraft.utils.system.IOUtils;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -158,14 +157,4 @@ public final class NetUtils {
|
||||
throw new IllegalArgumentException("Could not concatenate given URL with GET arguments!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static Observable<String> getRx(String url) {
|
||||
return Observable.createWithEmptySubscription(t1 -> {
|
||||
try {
|
||||
t1.onNext(get(url));
|
||||
} catch (Exception e) {
|
||||
t1.onError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.jackhuang.hellominecraft.utils.functions.Consumer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public abstract class OverridableSwingWorker<T> extends SwingWorker<Void, T> {
|
||||
|
||||
List<Consumer<T>> processListeners = new ArrayList<>();
|
||||
List<Runnable> doneListeners = new ArrayList<>();
|
||||
|
||||
protected abstract void work() throws Exception;
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
for (Runnable c : doneListeners)
|
||||
c.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
work();
|
||||
return null;
|
||||
}
|
||||
|
||||
public OverridableSwingWorker reg(Consumer<T> c) {
|
||||
processListeners.add(c);
|
||||
return this;
|
||||
}
|
||||
|
||||
public OverridableSwingWorker regDone(Runnable c) {
|
||||
doneListeners.add(c);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void process(List<T> chunks) {
|
||||
for (T t : chunks)
|
||||
for (Consumer<T> c : processListeners)
|
||||
c.accept(t);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package org.jackhuang.hellominecraft.utils;
|
||||
|
||||
import org.jackhuang.hellominecraft.utils.logging.HMCLog;
|
||||
import java.util.Map;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -41,27 +40,26 @@ public final class UpdateChecker implements IUpdateChecker {
|
||||
VersionNumber value;
|
||||
|
||||
@Override
|
||||
public Observable<VersionNumber> process(boolean showMessage) {
|
||||
return Observable.createWithEmptySubscription(t -> {
|
||||
if (value == null) {
|
||||
try {
|
||||
versionString = NetUtils.get("http://huangyuhui.duapp.com/info.php?type=" + type);
|
||||
} catch (Exception e) {
|
||||
HMCLog.warn("Failed to get update url.", e);
|
||||
return;
|
||||
}
|
||||
value = VersionNumber.check(versionString);
|
||||
}
|
||||
public OverridableSwingWorker<VersionNumber> process(boolean showMessage) {
|
||||
return new OverridableSwingWorker() {
|
||||
@Override
|
||||
protected void work() throws Exception {
|
||||
|
||||
if (value == null) {
|
||||
HMCLog.warn("Failed to check update...");
|
||||
if (showMessage)
|
||||
MessageBox.Show(C.i18n("update.failed"));
|
||||
} else if (VersionNumber.isOlder(base, value))
|
||||
OUT_DATED = true;
|
||||
if (OUT_DATED)
|
||||
t.onNext(value);
|
||||
});
|
||||
if (value == null) {
|
||||
versionString = NetUtils.get("http://huangyuhui.duapp.com/info.php?type=" + type);
|
||||
value = VersionNumber.check(versionString);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
HMCLog.warn("Failed to check update...");
|
||||
if (showMessage)
|
||||
MessageBox.Show(C.i18n("update.failed"));
|
||||
} else if (VersionNumber.isOlder(base, value))
|
||||
OUT_DATED = true;
|
||||
if (OUT_DATED)
|
||||
publish(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,16 +68,19 @@ public final class UpdateChecker implements IUpdateChecker {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Observable<Map<String, String>> requestDownloadLink() {
|
||||
return Observable.createWithEmptySubscription(t -> {
|
||||
if (download_link == null)
|
||||
try {
|
||||
download_link = C.gson.fromJson(NetUtils.get("http://huangyuhui.duapp.com/update_link.php?type=" + type), Map.class);
|
||||
} catch (Exception e) {
|
||||
HMCLog.warn("Failed to get update link.", e);
|
||||
}
|
||||
t.onNext(download_link);
|
||||
});
|
||||
public synchronized OverridableSwingWorker<Map<String, String>> requestDownloadLink() {
|
||||
return new OverridableSwingWorker() {
|
||||
@Override
|
||||
protected void work() throws Exception {
|
||||
if (download_link == null)
|
||||
try {
|
||||
download_link = C.gson.fromJson(NetUtils.get("http://huangyuhui.duapp.com/update_link.php?type=" + type), Map.class);
|
||||
} catch (Exception e) {
|
||||
HMCLog.warn("Failed to get update link.", e);
|
||||
}
|
||||
publish(download_link);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public final EventHandler<VersionNumber> outdated = new EventHandler<>(this);
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hellominecraft.utils.tasks;
|
||||
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class TaskObservable<T> extends TaskInfo {
|
||||
|
||||
private final Observable<T> r;
|
||||
|
||||
public TaskObservable(String info, Observable<T> r) {
|
||||
super(info);
|
||||
this.r = r;
|
||||
}
|
||||
|
||||
public TaskObservable(Observable<T> r) {
|
||||
this("TaskObservable", r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeTask() {
|
||||
r.subscribe(t -> {
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx;
|
||||
|
||||
/**
|
||||
* An object representing a notification sent to an {@link Observable}.
|
||||
*
|
||||
* For the Microsoft Rx equivalent see:
|
||||
* http://msdn.microsoft.com/en-us/library/hh229462(v=vs.103).aspx
|
||||
*/
|
||||
public class Notification<T> {
|
||||
|
||||
private final Kind kind;
|
||||
private final Exception exception;
|
||||
private final T value;
|
||||
|
||||
/**
|
||||
* A constructor used to represent an onNext notification.
|
||||
*
|
||||
* @param value The data passed to the onNext method.
|
||||
*/
|
||||
public Notification(T value) {
|
||||
this.value = value;
|
||||
this.exception = null;
|
||||
this.kind = Kind.OnNext;
|
||||
}
|
||||
|
||||
/**
|
||||
* A constructor used to represent an onError notification.
|
||||
*
|
||||
* @param exception The exception passed to the onError notification.
|
||||
*/
|
||||
public Notification(Exception exception) {
|
||||
this.exception = exception;
|
||||
this.value = null;
|
||||
this.kind = Kind.OnError;
|
||||
}
|
||||
|
||||
/**
|
||||
* A constructor used to represent an onCompleted notification.
|
||||
*/
|
||||
public Notification() {
|
||||
this.exception = null;
|
||||
this.value = null;
|
||||
this.kind = Kind.OnCompleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the exception associated with an onError notification.
|
||||
*
|
||||
* @return The exception associated with an onError notification.
|
||||
*/
|
||||
public Exception getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the data associated with an onNext notification.
|
||||
*
|
||||
* @return The data associated with an onNext notification.
|
||||
*/
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a value indicating whether this notification has a value.
|
||||
*
|
||||
* @return a value indicating whether this notification has a value.
|
||||
*/
|
||||
public boolean hasValue() {
|
||||
return isOnNext() && value != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a value indicating whether this notification has an exception.
|
||||
*
|
||||
* @return a value indicating whether this notification has an exception.
|
||||
*/
|
||||
public boolean hasException() {
|
||||
return isOnError() && exception != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The kind of notification: OnNext, OnError, OnCompleted
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Kind getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public boolean isOnError() {
|
||||
return getKind() == Kind.OnError;
|
||||
}
|
||||
|
||||
public boolean isOnCompleted() {
|
||||
return getKind() == Kind.OnCompleted;
|
||||
}
|
||||
|
||||
public boolean isOnNext() {
|
||||
return getKind() == Kind.OnNext;
|
||||
}
|
||||
|
||||
public static enum Kind {
|
||||
OnNext, OnError, OnCompleted
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder("[").append(super.toString()).append(" ").append(getKind());
|
||||
if (hasValue())
|
||||
str.append(" ").append(getValue());
|
||||
if (hasException())
|
||||
str.append(" ").append(getException().getMessage());
|
||||
str.append("]");
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = getKind().hashCode();
|
||||
if (hasValue())
|
||||
hash = hash * 31 + getValue().hashCode();
|
||||
if (hasException())
|
||||
hash = hash * 31 + getException().hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj.getClass() != getClass())
|
||||
return false;
|
||||
Notification<?> notification = (Notification<?>) obj;
|
||||
if (notification.getKind() != getKind() || hasValue() && !getValue().equals(notification.getValue()))
|
||||
return false;
|
||||
return !(hasException() && !getException().equals(notification.getException()));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,68 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx;
|
||||
|
||||
/**
|
||||
* Provides a mechanism for receiving push-based notifications.
|
||||
* <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
|
||||
* 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>
|
||||
* For more information see the
|
||||
* <a href="https://github.com/Netflix/RxJava/wiki/Observable">RxJava Wiki</a>
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public interface Observer<T> {
|
||||
|
||||
/**
|
||||
* Notifies the Observer that the {@link Observable} has finished sending
|
||||
* push-based notifications.
|
||||
* <p>
|
||||
* The {@link Observable} will not call this closure if it calls
|
||||
* <code>onError</code>.
|
||||
*/
|
||||
public void onCompleted();
|
||||
|
||||
/**
|
||||
* Notifies the Observer that the {@link Observable} has experienced an
|
||||
* error condition.
|
||||
* <p>
|
||||
* If the {@link Observable} calls this closure, it will not thereafter call
|
||||
* <code>onNext</code> or <code>onCompleted</code>.
|
||||
*
|
||||
* @param e
|
||||
*/
|
||||
public void onError(Exception e);
|
||||
|
||||
/**
|
||||
* Provides the Observer with new data.
|
||||
* <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.
|
||||
* <p>
|
||||
* The {@link Observable} will not call this closure again after it calls
|
||||
* either <code>onCompleted</code> or <code>onError</code>.
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public void onNext(T args);
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.util.functions.Action0;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/**
|
||||
* Represents an object that schedules units of work.
|
||||
*/
|
||||
public interface Scheduler {
|
||||
|
||||
/**
|
||||
* Schedules a cancelable action to be executed.
|
||||
*
|
||||
* @param action action
|
||||
*
|
||||
* @return a subscription to be able to unsubscribe from action.
|
||||
*/
|
||||
Subscription schedule(Func0<Subscription> action);
|
||||
|
||||
/**
|
||||
* Schedules an action to be executed.
|
||||
*
|
||||
* @param action action
|
||||
*
|
||||
* @return a subscription to be able to unsubscribe from action.
|
||||
*/
|
||||
Subscription schedule(Action0 action);
|
||||
|
||||
/**
|
||||
* Schedules an action to be executed in dueTime.
|
||||
*
|
||||
* @param action action
|
||||
*
|
||||
* @return a subscription to be able to unsubscribe from action.
|
||||
*/
|
||||
Subscription schedule(Action0 action, long dueTime, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Schedules a cancelable action to be executed in dueTime.
|
||||
*
|
||||
* @param action action
|
||||
*
|
||||
* @return a subscription to be able to unsubscribe from action.
|
||||
*/
|
||||
Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Returns the scheduler's notion of current time.
|
||||
*/
|
||||
long now();
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx;
|
||||
|
||||
import rx.subscriptions.Subscriptions;
|
||||
|
||||
/**
|
||||
* Subscription returns from {@link Observable#subscribe(Observer)} to allow
|
||||
* unsubscribing.
|
||||
* <p>
|
||||
* See utilities in {@link Subscriptions} and implementations in the
|
||||
* {@link rx.subscriptions} package.
|
||||
*/
|
||||
public interface Subscription {
|
||||
|
||||
/**
|
||||
* Stop receiving notifications on the {@link Observer} that was registered
|
||||
* when this Subscription was received.
|
||||
* <p>
|
||||
* This allows unregistering an {@link Observer} before it has finished
|
||||
* receiving all events (ie. before onCompleted is called).
|
||||
*/
|
||||
public void unsubscribe();
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.concurrency;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.Scheduler;
|
||||
import rx.Subscription;
|
||||
import rx.subscriptions.Subscriptions;
|
||||
import rx.util.functions.Action0;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/*
|
||||
* package
|
||||
*/
|
||||
abstract class AbstractScheduler implements Scheduler {
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Action0 action) {
|
||||
return schedule(asFunc0(action));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Action0 action, long dueTime, TimeUnit unit) {
|
||||
return schedule(asFunc0(action), dueTime, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long now() {
|
||||
return System.nanoTime();
|
||||
}
|
||||
|
||||
private static Func0<Subscription> asFunc0(final Action0 action) {
|
||||
return () -> {
|
||||
action.call();
|
||||
return Subscriptions.empty();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.concurrency;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.Subscription;
|
||||
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.
|
||||
*/
|
||||
public class CurrentThreadScheduler extends AbstractScheduler {
|
||||
|
||||
private static final CurrentThreadScheduler INSTANCE = new CurrentThreadScheduler();
|
||||
|
||||
public static CurrentThreadScheduler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static final ThreadLocal<Queue<DiscardableAction>> QUEUE = new ThreadLocal<>();
|
||||
|
||||
private CurrentThreadScheduler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action) {
|
||||
DiscardableAction discardableAction = new DiscardableAction(action);
|
||||
enqueue(discardableAction);
|
||||
return discardableAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
|
||||
return schedule(new SleepingAction(action, this, dueTime, unit));
|
||||
}
|
||||
|
||||
private void enqueue(DiscardableAction action) {
|
||||
Queue<DiscardableAction> queue = QUEUE.get();
|
||||
boolean exec = queue == null;
|
||||
|
||||
if (exec) {
|
||||
queue = new LinkedList<>();
|
||||
QUEUE.set(queue);
|
||||
}
|
||||
|
||||
queue.add(action);
|
||||
|
||||
if (exec) {
|
||||
while (!queue.isEmpty())
|
||||
queue.poll().call();
|
||||
|
||||
QUEUE.set(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.concurrency;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
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.
|
||||
*/
|
||||
/*
|
||||
* package
|
||||
*/
|
||||
class DiscardableAction implements Func0<Subscription>, Subscription {
|
||||
|
||||
private final Func0<Subscription> underlying;
|
||||
|
||||
private final AtomicObservableSubscription wrapper = new AtomicObservableSubscription();
|
||||
private final AtomicBoolean ready = new AtomicBoolean(true);
|
||||
|
||||
public DiscardableAction(Func0<Subscription> underlying) {
|
||||
this.underlying = underlying;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call() {
|
||||
if (ready.compareAndSet(true, false)) {
|
||||
Subscription subscription = underlying.call();
|
||||
wrapper.wrap(subscription);
|
||||
return subscription;
|
||||
}
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
ready.set(false);
|
||||
wrapper.unsubscribe();
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package rx.concurrency;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class EventQueueScheduler extends AbstractScheduler {
|
||||
|
||||
private static final EventQueueScheduler INSTANCE = new EventQueueScheduler();
|
||||
|
||||
public static EventQueueScheduler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action) {
|
||||
final DiscardableAction discardableAction = new DiscardableAction(action);
|
||||
|
||||
EventQueue.invokeLater(discardableAction::call);
|
||||
|
||||
return discardableAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
|
||||
return schedule(new SleepingAction(action, this, dueTime, unit));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.concurrency;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import rx.Scheduler;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/**
|
||||
* A {@link Scheduler} implementation that uses an {@link Executor} or
|
||||
* {@link ScheduledExecutorService} implementation.
|
||||
* <p>
|
||||
* Note that if an {@link Executor} implementation is used instead of
|
||||
* {@link ScheduledExecutorService} then a system-wide Timer will be used to
|
||||
* handle delayed events.
|
||||
*/
|
||||
public class ExecutorScheduler extends AbstractScheduler {
|
||||
|
||||
private final Executor executor;
|
||||
|
||||
/**
|
||||
* Setup a ScheduledExecutorService that we can use if someone provides an
|
||||
* Executor instead of ScheduledExecutorService.
|
||||
*/
|
||||
private final static ScheduledExecutorService SYSTEM_SCHEDULED_EXECUTOR;
|
||||
|
||||
static {
|
||||
int count = Runtime.getRuntime().availableProcessors();
|
||||
if (count > 8)
|
||||
count = count / 2;
|
||||
// we don't need more than 8 to handle just scheduling and doing no work
|
||||
if (count > 8)
|
||||
count = 8;
|
||||
SYSTEM_SCHEDULED_EXECUTOR = Executors.newScheduledThreadPool(count, new ThreadFactory() {
|
||||
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r, "RxScheduledExecutorPool-" + counter.incrementAndGet());
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public ExecutorScheduler(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public ExecutorScheduler(ScheduledExecutorService executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
|
||||
final DiscardableAction discardableAction = new DiscardableAction(action);
|
||||
|
||||
if (executor instanceof ScheduledExecutorService)
|
||||
((ScheduledExecutorService) executor).schedule(discardableAction::call, dueTime, unit);
|
||||
else if (dueTime == 0)
|
||||
// no delay so put on the thread-pool right now
|
||||
return (schedule(action));
|
||||
else
|
||||
// there is a delay and this isn't a ScheduledExecutorService so we'll use a system-wide ScheduledExecutorService
|
||||
// to handle the scheduling and once it's ready then execute on this Executor
|
||||
SYSTEM_SCHEDULED_EXECUTOR.schedule(() -> {
|
||||
// now execute on the real Executor
|
||||
executor.execute(discardableAction::call);
|
||||
}, dueTime, unit);
|
||||
return discardableAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action) {
|
||||
final DiscardableAction discardableAction = new DiscardableAction(action);
|
||||
|
||||
executor.execute(discardableAction::call);
|
||||
|
||||
return discardableAction;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.concurrency;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/**
|
||||
* Executes work immediately on the current thread.
|
||||
*/
|
||||
public final class ImmediateScheduler extends AbstractScheduler {
|
||||
|
||||
private static final ImmediateScheduler INSTANCE = new ImmediateScheduler();
|
||||
|
||||
public static ImmediateScheduler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private ImmediateScheduler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action) {
|
||||
return action.call();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
|
||||
return schedule(new SleepingAction(action, this, dueTime, unit));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.concurrency;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/**
|
||||
* Schedules work on a new thread.
|
||||
*/
|
||||
public class NewThreadScheduler extends AbstractScheduler {
|
||||
|
||||
private static final NewThreadScheduler INSTANCE = new NewThreadScheduler();
|
||||
|
||||
public static NewThreadScheduler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action) {
|
||||
final DiscardableAction discardableAction = new DiscardableAction(action);
|
||||
|
||||
Thread t = new Thread(discardableAction::call, "RxNewThreadScheduler");
|
||||
|
||||
t.start();
|
||||
|
||||
return discardableAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
|
||||
return schedule(new SleepingAction(action, this, dueTime, unit));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.concurrency;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import rx.Scheduler;
|
||||
|
||||
/**
|
||||
* Static factory methods for creating Schedulers.
|
||||
*/
|
||||
public class Schedulers {
|
||||
|
||||
private static final ScheduledExecutorService COMPUTATION_EXECUTOR = createComputationExecutor();
|
||||
private static final Executor IO_EXECUTOR = createIOExecutor();
|
||||
|
||||
private Schedulers() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} that executes work immediately on the current thread.
|
||||
*
|
||||
* @return {@link ImmediateScheduler} instance
|
||||
*/
|
||||
public static Scheduler immediate() {
|
||||
return ImmediateScheduler.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} that queues work on the current thread to be executed
|
||||
* after the current work completes.
|
||||
*
|
||||
* @return {@link CurrentThreadScheduler} instance
|
||||
*/
|
||||
public static Scheduler currentThread() {
|
||||
return CurrentThreadScheduler.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} that creates a new {@link Thread} for each unit of
|
||||
* work.
|
||||
*
|
||||
* @return {@link NewThreadScheduler} instance
|
||||
*/
|
||||
public static Scheduler newThread() {
|
||||
return NewThreadScheduler.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} that queues work on the EventQueue thread to be
|
||||
* executed on the Swing UI Thread.
|
||||
*
|
||||
* @return {@link NewThreadScheduler} instance
|
||||
*/
|
||||
public static Scheduler eventQueue() {
|
||||
return EventQueueScheduler.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} that queues work on an {@link Executor}.
|
||||
* <p>
|
||||
* Note that this does not support scheduled actions with a delay.
|
||||
*
|
||||
* @return {@link ExecutorScheduler} instance
|
||||
*/
|
||||
public static Scheduler executor(Executor executor) {
|
||||
return new ExecutorScheduler(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} that queues work on an
|
||||
* {@link ScheduledExecutorService}.
|
||||
*
|
||||
* @return {@link ExecutorScheduler} instance
|
||||
*/
|
||||
public static Scheduler executor(ScheduledExecutorService executor) {
|
||||
return new ExecutorScheduler(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} intended for computational work.
|
||||
* <p>
|
||||
* The implementation is backed by a {@link ScheduledExecutorService}
|
||||
* thread-pool sized to the number of CPU cores.
|
||||
* <p>
|
||||
* This can be used for event-loops, processing callbacks and other
|
||||
* computational work.
|
||||
* <p>
|
||||
* Do not perform IO-bound work on this scheduler. Use
|
||||
* {@link #threadPoolForComputation()} instead.
|
||||
*
|
||||
* @return {@link ExecutorScheduler} for computation-bound work.
|
||||
*/
|
||||
public static Scheduler threadPoolForComputation() {
|
||||
return executor(COMPUTATION_EXECUTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} intended for IO-bound work.
|
||||
* <p>
|
||||
* The implementation is backed by an {@link Executor} thread-pool that will
|
||||
* grow as needed.
|
||||
* <p>
|
||||
* This can be used for asynchronously performing blocking IO.
|
||||
* <p>
|
||||
* Do not perform computational work on this scheduler. Use
|
||||
* {@link #threadPoolForComputation()} instead.
|
||||
*
|
||||
* @return {@link ExecutorScheduler} for IO-bound work.
|
||||
*/
|
||||
public static Scheduler threadPoolForIO() {
|
||||
return executor(IO_EXECUTOR);
|
||||
}
|
||||
|
||||
private static ScheduledExecutorService createComputationExecutor() {
|
||||
int cores = Runtime.getRuntime().availableProcessors();
|
||||
return Executors.newScheduledThreadPool(cores, new ThreadFactory() {
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r, "RxComputationThreadPool-" + counter.incrementAndGet());
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static Executor createIOExecutor() {
|
||||
Executor result = Executors.newCachedThreadPool(new ThreadFactory() {
|
||||
final AtomicLong counter = new AtomicLong();
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r, "RxIOThreadPool-" + counter.incrementAndGet());
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.concurrency;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.Scheduler;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/*
|
||||
* package
|
||||
*/
|
||||
class SleepingAction implements Func0<Subscription> {
|
||||
|
||||
private final Func0<Subscription> underlying;
|
||||
private final Scheduler scheduler;
|
||||
private final long execTime;
|
||||
|
||||
public SleepingAction(Func0<Subscription> underlying, Scheduler scheduler, long timespan, TimeUnit timeUnit) {
|
||||
this.underlying = underlying;
|
||||
this.scheduler = scheduler;
|
||||
this.execTime = scheduler.now() + timeUnit.toMillis(timespan);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call() {
|
||||
if (execTime < scheduler.now())
|
||||
try {
|
||||
Thread.sleep(scheduler.now() - execTime);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return underlying.call();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.observables;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* 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)}
|
||||
*
|
||||
* @param <K>
|
||||
* @param <T>
|
||||
*/
|
||||
public class GroupedObservable<K, T> extends Observable<T> {
|
||||
|
||||
private final K key;
|
||||
|
||||
public GroupedObservable(K key, Func1<Observer<T>, Subscription> onSubscribe) {
|
||||
super(onSubscribe);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class OperationAll {
|
||||
|
||||
public static <T> Func1<Observer<Boolean>, Subscription> all(Observable<T> sequence, Func1<T, Boolean> predicate) {
|
||||
return new AllObservable<>(sequence, predicate);
|
||||
}
|
||||
|
||||
private static class AllObservable<T> implements Func1<Observer<Boolean>, Subscription> {
|
||||
|
||||
private final Observable<T> sequence;
|
||||
private final Func1<T, Boolean> predicate;
|
||||
|
||||
private final AtomicBoolean status = new AtomicBoolean(true);
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
private AllObservable(Observable<T> sequence, Func1<T, Boolean> predicate) {
|
||||
this.sequence = sequence;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<Boolean> observer) {
|
||||
return subscription.wrap(sequence.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
if (status.get()) {
|
||||
observer.onNext(true);
|
||||
observer.onCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
boolean result = predicate.call(args);
|
||||
boolean changed = status.compareAndSet(true, result);
|
||||
|
||||
if (changed && !result) {
|
||||
observer.onNext(false);
|
||||
observer.onCompleted();
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,293 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
import rx.util.functions.Func2;
|
||||
import rx.util.functions.Func3;
|
||||
import rx.util.functions.Func4;
|
||||
import rx.util.functions.FuncN;
|
||||
import rx.util.functions.Functions;
|
||||
|
||||
public class OperationCombineLatest {
|
||||
|
||||
/**
|
||||
* Combines the two given observables, emitting an event containing an
|
||||
* 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 w1 The second source observable.
|
||||
* @param combineLatestFunction The aggregation function used to combine the
|
||||
* 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) {
|
||||
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction));
|
||||
a.addObserver(new CombineObserver<R, T0>(a, w0));
|
||||
a.addObserver(new CombineObserver<R, T1>(a, w1));
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction));
|
||||
a.addObserver(new CombineObserver<R, T0>(a, w0));
|
||||
a.addObserver(new CombineObserver<R, T1>(a, w1));
|
||||
a.addObserver(new CombineObserver<R, T2>(a, w2));
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction));
|
||||
a.addObserver(new CombineObserver<R, T0>(a, w0));
|
||||
a.addObserver(new CombineObserver<R, T1>(a, w1));
|
||||
a.addObserver(new CombineObserver<R, T2>(a, w2));
|
||||
a.addObserver(new CombineObserver<R, T3>(a, w3));
|
||||
return a;
|
||||
}
|
||||
|
||||
private static class CombineObserver<R, T> implements Observer<T> {
|
||||
|
||||
final Observable<T> w;
|
||||
final Aggregator<R> a;
|
||||
private Subscription subscription;
|
||||
|
||||
public CombineObserver(Aggregator<R> a, Observable<T> w) {
|
||||
this.a = a;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
public synchronized void startWatching() {
|
||||
if (subscription != null)
|
||||
throw new RuntimeException("This should only be called once.");
|
||||
subscription = w.subscribe(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
a.complete(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
a.error(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
a.next(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive notifications from each of the observables we are reducing and
|
||||
* execute the combineLatestFunction whenever we have received an event from
|
||||
* 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 Observer<R> observer;
|
||||
|
||||
private final FuncN<R> combineLatestFunction;
|
||||
private final AtomicBoolean running = new AtomicBoolean(true);
|
||||
|
||||
// used as an internal lock for handling the latest values and the completed state of each observer
|
||||
private final Object lockObject = new Object();
|
||||
|
||||
/**
|
||||
* Store when an observer completes.
|
||||
* <p>
|
||||
* Note that access to this set MUST BE SYNCHRONIZED via 'lockObject'
|
||||
* above.
|
||||
*
|
||||
*/
|
||||
private final Set<CombineObserver<R, ?>> completed = new HashSet<>();
|
||||
|
||||
/**
|
||||
* The latest value from each observer
|
||||
* <p>
|
||||
* Note that access to this set MUST BE SYNCHRONIZED via 'lockObject'
|
||||
* above.
|
||||
*
|
||||
*/
|
||||
private final Map<CombineObserver<R, ?>, Object> latestValue = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Whether each observer has a latest value at all.
|
||||
* <p>
|
||||
* Note that access to this set MUST BE SYNCHRONIZED via 'lockObject'
|
||||
* above.
|
||||
*
|
||||
*/
|
||||
private final Set<CombineObserver<R, ?>> hasLatestValue = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Ordered list of observers to combine. No synchronization is necessary
|
||||
* as these can not be added or changed asynchronously.
|
||||
*/
|
||||
private final List<CombineObserver<R, ?>> observers = new LinkedList<>();
|
||||
|
||||
public Aggregator(FuncN<R> combineLatestFunction) {
|
||||
this.combineLatestFunction = combineLatestFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive notification of a Observer starting (meaning we should
|
||||
* require it for aggregation)
|
||||
*
|
||||
* @param w The observer to add.
|
||||
*/
|
||||
<T> void addObserver(CombineObserver<R, T> w) {
|
||||
observers.add(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive notification of a Observer completing its iterations.
|
||||
*
|
||||
* @param w The observer that has completed.
|
||||
*/
|
||||
<T> void complete(CombineObserver<R, T> w) {
|
||||
synchronized (lockObject) {
|
||||
// store that this CombineLatestObserver is completed
|
||||
completed.add(w);
|
||||
// if all CombineObservers are completed, we mark the whole thing as completed
|
||||
if (completed.size() == observers.size())
|
||||
if (running.get()) {
|
||||
// mark ourselves as done
|
||||
observer.onCompleted();
|
||||
// just to ensure we stop processing in case we receive more onNext/complete/error calls after this
|
||||
running.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive error for a Observer. Throw the error up the chain and stop
|
||||
* processing.
|
||||
*/
|
||||
void error(Exception e) {
|
||||
observer.onError(e);
|
||||
/*
|
||||
* tell all observers to unsubscribe since we had an error
|
||||
*/
|
||||
stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the next value from an observer.
|
||||
* <p>
|
||||
* If we have received values from all observers, trigger the
|
||||
* combineLatest function, otherwise store the value and keep waiting.
|
||||
*
|
||||
* @param w
|
||||
* @param arg
|
||||
*/
|
||||
<T> void next(CombineObserver<R, T> w, T arg) {
|
||||
if (observer == null)
|
||||
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())
|
||||
return;
|
||||
|
||||
// define here so the variable is out of the synchronized scope
|
||||
Object[] argsToCombineLatest = new Object[observers.size()];
|
||||
|
||||
// we synchronize everything that touches latest values
|
||||
synchronized (lockObject) {
|
||||
// remember this as the latest value for this observer
|
||||
latestValue.put(w, arg);
|
||||
|
||||
// remember that this observer now has a latest value set
|
||||
hasLatestValue.add(w);
|
||||
|
||||
// if all observers in the 'observers' list have a value, invoke the combineLatestFunction
|
||||
for (CombineObserver<R, ?> rw : observers)
|
||||
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
|
||||
return;
|
||||
// if we get to here this means all the queues have data
|
||||
int i = 0;
|
||||
for (CombineObserver<R, ?> _w : observers)
|
||||
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
|
||||
// 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
|
||||
observer.onNext(combineLatestFunction.call(argsToCombineLatest));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<R> observer) {
|
||||
if (this.observer != null)
|
||||
throw new IllegalStateException("Only one Observer can subscribe to this Observable.");
|
||||
this.observer = observer;
|
||||
|
||||
/*
|
||||
* start the observers
|
||||
*/
|
||||
for (CombineObserver<R, ?> rw : observers)
|
||||
rw.startWatching();
|
||||
|
||||
return new Subscription() {
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
stop();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
/*
|
||||
* tell ourselves to stop processing onNext events
|
||||
*/
|
||||
running.set(false);
|
||||
/*
|
||||
* propogate to all observers to unsubscribe
|
||||
*/
|
||||
for (CombineObserver<R, ?> rw : observers)
|
||||
if (rw.subscription != null)
|
||||
rw.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Action1;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationConcat {
|
||||
|
||||
/**
|
||||
* Combine the observable sequences from the list of Observables into one
|
||||
* observable sequence without any transformation.
|
||||
*
|
||||
* @param sequences 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.
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> concat(final Observable<T>... sequences) {
|
||||
return new Func1<Observer<T>, Subscription>() {
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
return new Concat<T>(sequences).call(observer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> concat(final List<Observable<T>> sequences) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Observable<T>[] o = sequences.toArray((Observable<T>[]) Array.newInstance(Observable.class, sequences.size()));
|
||||
return concat(o);
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> concat(final Observable<Observable<T>> sequences) {
|
||||
final List<Observable<T>> list = new ArrayList<Observable<T>>();
|
||||
sequences.toList().subscribe(new Action1<List<Observable<T>>>() {
|
||||
@Override
|
||||
public void call(List<Observable<T>> t1) {
|
||||
list.addAll(t1);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return concat(list);
|
||||
}
|
||||
|
||||
private static class Concat<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<T>[] sequences;
|
||||
private int num = 0;
|
||||
private int count = 0;
|
||||
private Subscription s;
|
||||
|
||||
Concat(final Observable<T>... sequences) {
|
||||
this.sequences = sequences;
|
||||
this.num = sequences.length - 1;
|
||||
}
|
||||
|
||||
private final AtomicObservableSubscription Subscription = new AtomicObservableSubscription();
|
||||
|
||||
private final Subscription actualSubscription = new Subscription() {
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
if (null != s)
|
||||
s.unsubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
public Subscription call(Observer<T> observer) {
|
||||
s = sequences[count].subscribe(new ConcatObserver(observer));
|
||||
|
||||
return Subscription.wrap(actualSubscription);
|
||||
}
|
||||
|
||||
private class ConcatObserver implements Observer<T> {
|
||||
|
||||
private final Observer<T> observer;
|
||||
|
||||
ConcatObserver(Observer<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
if (num == count)
|
||||
observer.onCompleted();
|
||||
else {
|
||||
count++;
|
||||
s = sequences[count].subscribe(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onError(e);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
observer.onNext(args);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func0;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationDefer {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> defer(final Func0<Observable<T>> observableFactory) {
|
||||
return observableFactory.call()::subscribe;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Notification;
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Dematerializes the explicit notification values of an observable sequence as
|
||||
* implicit notifications. See
|
||||
* http://msdn.microsoft.com/en-us/library/hh229047(v=vs.103).aspx for the
|
||||
* Microsoft Rx equivalent.
|
||||
*/
|
||||
public final class OperationDematerialize {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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) {
|
||||
return new DematerializeObservable<>(sequence);
|
||||
}
|
||||
|
||||
private static class DematerializeObservable<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<Notification<T>> sequence;
|
||||
|
||||
public DematerializeObservable(Observable<Notification<T>> sequence) {
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
return sequence.subscribe(new Observer<Notification<T>>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Notification<T> value) {
|
||||
switch (value.getKind()) {
|
||||
case OnNext:
|
||||
observer.onNext(value.getValue());
|
||||
break;
|
||||
case OnError:
|
||||
observer.onError(value.getException());
|
||||
break;
|
||||
case OnCompleted:
|
||||
observer.onCompleted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationFilter<T> {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> filter(Observable<T> that, Func1<T, Boolean> predicate) {
|
||||
return new Filter<>(that, predicate);
|
||||
}
|
||||
|
||||
private static class Filter<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<T> that;
|
||||
private final Func1<T, Boolean> predicate;
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
public Filter(Observable<T> that, Func1<T, Boolean> predicate) {
|
||||
this.that = that;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
return subscription.wrap(that.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
try {
|
||||
if (predicate.call(value))
|
||||
observer.onNext(value);
|
||||
} catch (Exception ex) {
|
||||
observer.onError(ex);
|
||||
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
observer.onError(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onCompleted();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Action0;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationFinally {
|
||||
|
||||
/**
|
||||
* Call a given action when a sequence completes (with or without an
|
||||
* exception). The returned observable is exactly as threadsafe as the
|
||||
* source observable.
|
||||
* <p/>
|
||||
* Note that "finally" is a Java reserved word and cannot be an identifier,
|
||||
* so we use "finallyDo".
|
||||
*
|
||||
* @param sequence An observable sequence of elements
|
||||
* @param action An action to be taken when the sequence is complete or
|
||||
* throws an exception
|
||||
*
|
||||
* @return An observable sequence with the same elements as the input. 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), 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) {
|
||||
return new Finally<>(sequence, action)::call;
|
||||
}
|
||||
|
||||
private static class Finally<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<T> sequence;
|
||||
private final Action0 finalAction;
|
||||
|
||||
Finally(final Observable<T> sequence, Action0 finalAction) {
|
||||
this.sequence = sequence;
|
||||
this.finalAction = finalAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
return sequence.subscribe(new FinallyObserver(observer));
|
||||
}
|
||||
|
||||
private class FinallyObserver implements Observer<T> {
|
||||
|
||||
private final Observer<T> observer;
|
||||
|
||||
FinallyObserver(Observer<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
try {
|
||||
observer.onCompleted();
|
||||
} finally {
|
||||
finalAction.call();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
try {
|
||||
observer.onError(e);
|
||||
} finally {
|
||||
finalAction.call();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
observer.onNext(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationMap {
|
||||
|
||||
/**
|
||||
* Accepts a sequence and a transformation function. Returns a sequence that
|
||||
* is the result of applying the transformation function to each item in the
|
||||
* sequence.
|
||||
*
|
||||
* @param sequence the input sequence.
|
||||
* @param func a function to apply to each item in the sequence.
|
||||
* @param <T> the type of the input sequence.
|
||||
* @param <R> 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) {
|
||||
return new MapObservable<>(sequence, func);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a sequence of observable sequences and a transformation function.
|
||||
* Returns a flattened sequence that is the result of applying the
|
||||
* transformation function to each item in the sequence of each observable
|
||||
* sequence.
|
||||
* <p>
|
||||
* The closure should return an Observable which will then be merged.
|
||||
*
|
||||
* @param sequence the input sequence.
|
||||
* @param func a function to apply to each item in the sequence.
|
||||
* @param <T> the type of the input sequence.
|
||||
* @param <R> 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) {
|
||||
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.
|
||||
*
|
||||
* @param <T> the type of the input sequence.
|
||||
* @param <R> the type of the output sequence.
|
||||
*/
|
||||
private static class MapObservable<T, R> implements Func1<Observer<R>, Subscription> {
|
||||
|
||||
public MapObservable(Observable<T> sequence, Func1<T, R> func) {
|
||||
this.sequence = sequence;
|
||||
this.func = func;
|
||||
}
|
||||
|
||||
private final Observable<T> sequence;
|
||||
|
||||
private final Func1<T, R> func;
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<R> observer) {
|
||||
return sequence.subscribe(new MapObserver<>(observer, func));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An observer that applies a transformation function to each item and
|
||||
* forwards the result to an inner observer.
|
||||
*
|
||||
* @param <T> the type of the observer items.
|
||||
* @param <R> the type of the inner observer items.
|
||||
*/
|
||||
private static class MapObserver<T, R> implements Observer<T> {
|
||||
|
||||
public MapObserver(Observer<R> observer, Func1<T, R> func) {
|
||||
this.observer = observer;
|
||||
this.func = func;
|
||||
}
|
||||
|
||||
Observer<R> observer;
|
||||
|
||||
Func1<T, R> func;
|
||||
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
try {
|
||||
observer.onNext(func.call(value));
|
||||
} catch (Exception ex) {
|
||||
observer.onError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
observer.onError(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Notification;
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Materializes the implicit notifications of an observable sequence as explicit
|
||||
* notification values.
|
||||
* <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.
|
||||
* <p>
|
||||
* See http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx for the
|
||||
* Microsoft Rx equivalent.
|
||||
*/
|
||||
public final class OperationMaterialize {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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) {
|
||||
return new MaterializeObservable<>(sequence);
|
||||
}
|
||||
|
||||
private static class MaterializeObservable<T> implements Func1<Observer<Notification<T>>, Subscription> {
|
||||
|
||||
private final Observable<T> sequence;
|
||||
|
||||
public MaterializeObservable(Observable<T> sequence) {
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<Notification<T>> observer) {
|
||||
return sequence.subscribe(new Observer<T>() {
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onNext(new Notification<>());
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onNext(new Notification<>(e));
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
observer.onNext(new Notification<>(value));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.SynchronizedObserver;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationMerge {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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) {
|
||||
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
|
||||
return new MergeObservable<T>(source)::call;
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> merge(final Observable<T>... sequences) {
|
||||
return merge(Arrays.asList(sequences));
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> merge(final List<Observable<T>> sequences) {
|
||||
return merge(Observable.create(new Func1<Observer<Observable<T>>, Subscription>() {
|
||||
|
||||
private volatile boolean unsubscribed = false;
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<Observable<T>> observer) {
|
||||
for (Observable<T> o : sequences)
|
||||
if (!unsubscribed)
|
||||
observer.onNext(o);
|
||||
else
|
||||
// break out of the loop if we are unsubscribed
|
||||
break;
|
||||
if (!unsubscribed)
|
||||
observer.onCompleted();
|
||||
|
||||
return () -> {
|
||||
unsubscribed = true;
|
||||
};
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* It IS thread-safe from within it while receiving onNext events from
|
||||
* multiple threads.
|
||||
* <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.
|
||||
* <p>
|
||||
* 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>
|
||||
*/
|
||||
private static final class MergeObservable<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<Observable<T>> sequences;
|
||||
private final MergeSubscription ourSubscription = new MergeSubscription();
|
||||
private final AtomicBoolean stopped = new AtomicBoolean(false);
|
||||
private volatile boolean parentCompleted = false;
|
||||
private final ConcurrentHashMap<ChildObserver, ChildObserver> childObservers = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<ChildObserver, Subscription> childSubscriptions = new ConcurrentHashMap<>();
|
||||
|
||||
private MergeObservable(Observable<Observable<T>> sequences) {
|
||||
this.sequences = sequences;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> actualObserver) {
|
||||
|
||||
/**
|
||||
* We must synchronize a merge because we subscribe to multiple
|
||||
* sequences in parallel that will each be emitting.
|
||||
* <p>
|
||||
* The calls from each sequence must be serialized.
|
||||
* <p>
|
||||
* Bug report: https://github.com/Netflix/RxJava/issues/200
|
||||
*/
|
||||
AtomicObservableSubscription subscription = new AtomicObservableSubscription(ourSubscription);
|
||||
SynchronizedObserver<T> synchronizedObserver = new SynchronizedObserver<>(actualObserver, subscription);
|
||||
|
||||
/**
|
||||
* Subscribe to the parent Observable to get to the children
|
||||
* Observables
|
||||
*/
|
||||
sequences.subscribe(new ParentObserver(synchronizedObserver));
|
||||
|
||||
/*
|
||||
* return our subscription to allow unsubscribing
|
||||
*/
|
||||
return subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage the internal subscription with a thread-safe means of
|
||||
* stopping/unsubscribing so we don't unsubscribe twice.
|
||||
* <p>
|
||||
* 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 {
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
stop();
|
||||
}
|
||||
|
||||
public boolean stop() {
|
||||
// try setting to false unless another thread beat us
|
||||
boolean didSet = stopped.compareAndSet(false, true);
|
||||
if (didSet) {
|
||||
// this thread won the race to stop, so unsubscribe from the actualSubscription
|
||||
for (Subscription _s : childSubscriptions.values())
|
||||
_s.unsubscribe();
|
||||
return true;
|
||||
} else
|
||||
// another thread beat us
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to the top level Observable to receive the sequence of
|
||||
* Observable<T> children.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private class ParentObserver implements Observer<Observable<T>> {
|
||||
|
||||
private final Observer<T> actualObserver;
|
||||
|
||||
public ParentObserver(Observer<T> actualObserver) {
|
||||
this.actualObserver = actualObserver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
parentCompleted = true;
|
||||
// this *can* occur before the children are done, so if it does we won't send onCompleted
|
||||
// but will let the child worry about it
|
||||
// if however this completes and there are no children processing, then we will send onCompleted
|
||||
|
||||
if (childObservers.isEmpty())
|
||||
if (!stopped.get())
|
||||
if (ourSubscription.stop())
|
||||
actualObserver.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
actualObserver.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Observable<T> childObservable) {
|
||||
if (stopped.get())
|
||||
// we won't act on any further items
|
||||
return;
|
||||
|
||||
if (childObservable == null)
|
||||
throw new IllegalArgumentException("Observable<T> can not be null.");
|
||||
|
||||
/**
|
||||
* For each child Observable we receive we'll subscribe with a
|
||||
* separate Observer that will each then forward their sequences
|
||||
* to the actualObserver.
|
||||
* <p>
|
||||
* 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);
|
||||
childObservers.put(_w, _w);
|
||||
Subscription _subscription = childObservable.subscribe(_w);
|
||||
// remember this Observer and the subscription from it
|
||||
childSubscriptions.put(_w, _subscription);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to each child Observable<T> and forward their sequence of
|
||||
* data to the actualObserver
|
||||
*
|
||||
*/
|
||||
private class ChildObserver implements Observer<T> {
|
||||
|
||||
private final Observer<T> actualObserver;
|
||||
|
||||
public ChildObserver(Observer<T> actualObserver) {
|
||||
this.actualObserver = actualObserver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// remove self from map of Observers
|
||||
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 the parent is not complete that means there is another sequence (and child Observer) to come
|
||||
if (!stopped.get())
|
||||
if (childObservers.isEmpty() && parentCompleted)
|
||||
if (ourSubscription.stop())
|
||||
// this thread 'won' the race to unsubscribe/stop so let's send onCompleted
|
||||
actualObserver.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
if (!stopped.get())
|
||||
if (ourSubscription.stop())
|
||||
// this thread 'won' the race to unsubscribe/stop so let's send the error
|
||||
actualObserver.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
// 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
|
||||
if (!stopped.get())
|
||||
actualObserver.onNext(args);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.CompositeException;
|
||||
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 onError received (if any) will
|
||||
* be passed on.
|
||||
* <p>
|
||||
* This allows retrieving all successful onNext calls without being blocked by
|
||||
* an onError early in a sequence.
|
||||
* <p>
|
||||
* NOTE: If this is used on an infinite stream it will never call onError and
|
||||
* effectively will swallow errors.
|
||||
*/
|
||||
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 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.
|
||||
*
|
||||
* @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) {
|
||||
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
|
||||
return new Func1<Observer<T>, Subscription>() {
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
return new MergeDelayErrorObservable<T>(sequences).call(observer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> mergeDelayError(final Observable<T>... sequences) {
|
||||
return mergeDelayError(Observable.create(new Func1<Observer<Observable<T>>, Subscription>() {
|
||||
private volatile boolean unsubscribed = false;
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<Observable<T>> observer) {
|
||||
for (Observable<T> o : sequences)
|
||||
if (!unsubscribed)
|
||||
observer.onNext(o);
|
||||
else
|
||||
// break out of the loop if we are unsubscribed
|
||||
break;
|
||||
if (!unsubscribed)
|
||||
observer.onCompleted();
|
||||
return new Subscription() {
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
unsubscribed = true;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> mergeDelayError(final List<Observable<T>> sequences) {
|
||||
return mergeDelayError(Observable.create(new Func1<Observer<Observable<T>>, Subscription>() {
|
||||
|
||||
private volatile boolean unsubscribed = false;
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<Observable<T>> observer) {
|
||||
for (Observable<T> o : sequences)
|
||||
if (!unsubscribed)
|
||||
observer.onNext(o);
|
||||
else
|
||||
// break out of the loop if we are unsubscribed
|
||||
break;
|
||||
if (!unsubscribed)
|
||||
observer.onCompleted();
|
||||
|
||||
return new Subscription() {
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
unsubscribed = true;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* It IS thread-safe from within it while receiving onNext events from
|
||||
* multiple threads.
|
||||
* <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.
|
||||
* <p>
|
||||
* 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>
|
||||
*/
|
||||
private static final class MergeDelayErrorObservable<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<Observable<T>> sequences;
|
||||
private final MergeSubscription ourSubscription = new MergeSubscription();
|
||||
private AtomicBoolean stopped = new AtomicBoolean(false);
|
||||
private volatile boolean parentCompleted = false;
|
||||
private final ConcurrentHashMap<ChildObserver, ChildObserver> childObservers = new ConcurrentHashMap<ChildObserver, ChildObserver>();
|
||||
private final ConcurrentHashMap<ChildObserver, Subscription> childSubscriptions = new ConcurrentHashMap<ChildObserver, Subscription>();
|
||||
// onErrors we received that will be delayed until everything is completed and then sent
|
||||
private ConcurrentLinkedQueue<Exception> onErrorReceived = new ConcurrentLinkedQueue<Exception>();
|
||||
|
||||
private MergeDelayErrorObservable(Observable<Observable<T>> sequences) {
|
||||
this.sequences = sequences;
|
||||
}
|
||||
|
||||
public Subscription call(Observer<T> actualObserver) {
|
||||
/**
|
||||
* Subscribe to the parent Observable to get to the children
|
||||
* Observables
|
||||
*/
|
||||
sequences.subscribe(new ParentObserver(actualObserver));
|
||||
|
||||
/*
|
||||
* return our subscription to allow unsubscribing
|
||||
*/
|
||||
return ourSubscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage the internal subscription with a thread-safe means of
|
||||
* stopping/unsubscribing so we don't unsubscribe twice.
|
||||
* <p>
|
||||
* 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 {
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
stop();
|
||||
}
|
||||
|
||||
public boolean stop() {
|
||||
// try setting to false unless another thread beat us
|
||||
boolean didSet = stopped.compareAndSet(false, true);
|
||||
if (didSet) {
|
||||
// this thread won the race to stop, so unsubscribe from the actualSubscription
|
||||
for (Subscription _s : childSubscriptions.values())
|
||||
_s.unsubscribe();
|
||||
return true;
|
||||
} else
|
||||
// another thread beat us
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to the top level Observable to receive the sequence of
|
||||
* Observable<T> children.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private class ParentObserver implements Observer<Observable<T>> {
|
||||
|
||||
private final Observer<T> actualObserver;
|
||||
|
||||
public ParentObserver(Observer<T> actualObserver) {
|
||||
this.actualObserver = actualObserver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
parentCompleted = true;
|
||||
// this *can* occur before the children are done, so if it does we won't send onCompleted
|
||||
// but will let the child worry about it
|
||||
// if however this completes and there are no children processing, then we will send onCompleted
|
||||
|
||||
if (childObservers.size() == 0)
|
||||
if (!stopped.get())
|
||||
if (ourSubscription.stop())
|
||||
if (onErrorReceived.size() == 1)
|
||||
// an onError was received from 1 ChildObserver so we now send it as a delayed error
|
||||
actualObserver.onError(onErrorReceived.peek());
|
||||
else if (onErrorReceived.size() > 1)
|
||||
// an onError was received from more than 1 ChildObserver so we now send it as a delayed error
|
||||
actualObserver.onError(new CompositeException(onErrorReceived));
|
||||
else
|
||||
// no delayed error so send onCompleted
|
||||
actualObserver.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
actualObserver.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Observable<T> childObservable) {
|
||||
if (stopped.get())
|
||||
// we won't act on any further items
|
||||
return;
|
||||
|
||||
if (childObservable == null)
|
||||
throw new IllegalArgumentException("Observable<T> can not be null.");
|
||||
|
||||
/**
|
||||
* For each child Observable we receive we'll subscribe with a
|
||||
* separate Observer that will each then forward their sequences
|
||||
* to the actualObserver.
|
||||
* <p>
|
||||
* 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);
|
||||
childObservers.put(_w, _w);
|
||||
Subscription _subscription = childObservable.subscribe(_w);
|
||||
// remember this Observer and the subscription from it
|
||||
childSubscriptions.put(_w, _subscription);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to each child Observable<T> and forward their sequence of
|
||||
* data to the actualObserver
|
||||
*
|
||||
*/
|
||||
private class ChildObserver implements Observer<T> {
|
||||
|
||||
private final Observer<T> actualObserver;
|
||||
private volatile boolean finished = false;
|
||||
|
||||
public ChildObserver(Observer<T> actualObserver) {
|
||||
this.actualObserver = actualObserver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// remove self from map of Observers
|
||||
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 the parent is not complete that means there is another sequence (and child Observer) to come
|
||||
if (!stopped.get())
|
||||
finishObserver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
if (!stopped.get()) {
|
||||
onErrorReceived.add(e);
|
||||
// mark this ChildObserver as done
|
||||
childObservers.remove(this);
|
||||
// but do NOT forward to actualObserver as we want other ChildObservers to continue until completion
|
||||
// and we'll delay the sending of onError until all others are done
|
||||
|
||||
// we mark finished==true as a safety to ensure that if further calls to onNext occur we ignore them
|
||||
finished = true;
|
||||
|
||||
// check for whether the parent is completed and if so then perform the 'finishing' actions
|
||||
finishObserver();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* This does NOT get invoked if synchronous execution occurs, but
|
||||
* will when asynchronously executing.
|
||||
* <p>
|
||||
* TestCase testErrorDelayed4WithThreading specifically tests this
|
||||
* use case.
|
||||
*/
|
||||
private void finishObserver() {
|
||||
if (childObservers.size() == 0 && parentCompleted)
|
||||
if (ourSubscription.stop())
|
||||
// this thread 'won' the race to unsubscribe/stop so let's send onError or onCompleted
|
||||
if (onErrorReceived.size() == 1)
|
||||
// an onError was received from 1 ChildObserver so we now send it as a delayed error
|
||||
actualObserver.onError(onErrorReceived.peek());
|
||||
else if (onErrorReceived.size() > 1)
|
||||
// an onError was received from more than 1 ChildObserver so we now send it as a delayed error
|
||||
actualObserver.onError(new CompositeException(onErrorReceived));
|
||||
else
|
||||
// no delayed error so send onCompleted
|
||||
actualObserver.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
// 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
|
||||
if (!stopped.get() && !finished)
|
||||
actualObserver.onNext(args);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.util.Exceptions;
|
||||
|
||||
/**
|
||||
* Samples the most recent value in an observable sequence.
|
||||
*/
|
||||
public final class OperationMostRecent {
|
||||
|
||||
public static <T> Iterable<T> mostRecent(final Observable<T> source, T initialValue) {
|
||||
|
||||
MostRecentObserver<T> mostRecentObserver = new MostRecentObserver<>(initialValue);
|
||||
MostRecentIterator<T> nextIterator = new MostRecentIterator<>(mostRecentObserver);
|
||||
|
||||
source.subscribe(mostRecentObserver);
|
||||
|
||||
return () -> nextIterator;
|
||||
|
||||
}
|
||||
|
||||
private static class MostRecentIterator<T> implements Iterator<T> {
|
||||
|
||||
private final MostRecentObserver<T> observer;
|
||||
|
||||
private MostRecentIterator(MostRecentObserver<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !observer.isCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (observer.getException() != null)
|
||||
throw Exceptions.propagate(observer.getException());
|
||||
return observer.getRecentValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Read only iterator");
|
||||
}
|
||||
}
|
||||
|
||||
private static class MostRecentObserver<T> implements Observer<T> {
|
||||
|
||||
private final AtomicBoolean completed = new AtomicBoolean(false);
|
||||
private final AtomicReference<T> value;
|
||||
private final AtomicReference<Exception> exception = new AtomicReference<>();
|
||||
|
||||
private MostRecentObserver(T value) {
|
||||
this.value = new AtomicReference<>(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
completed.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
exception.set(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
value.set(args);
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
return completed.get();
|
||||
}
|
||||
|
||||
public Exception getException() {
|
||||
return exception.get();
|
||||
}
|
||||
|
||||
public T getRecentValue() {
|
||||
return value.get();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Notification;
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.util.Exceptions;
|
||||
|
||||
/**
|
||||
* Samples the next value (blocking without buffering) from in an observable
|
||||
* sequence.
|
||||
*/
|
||||
public final class OperationNext {
|
||||
|
||||
public static <T> Iterable<T> next(final Observable<T> items) {
|
||||
|
||||
NextObserver<T> nextObserver = new NextObserver<>();
|
||||
final NextIterator<T> nextIterator = new NextIterator<>(nextObserver);
|
||||
|
||||
items.materialize().subscribe(nextObserver);
|
||||
|
||||
return () -> nextIterator;
|
||||
|
||||
}
|
||||
|
||||
private static class NextIterator<T> implements Iterator<T> {
|
||||
|
||||
private final NextObserver<T> observer;
|
||||
|
||||
private NextIterator(NextObserver<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !observer.isCompleted(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (observer.isCompleted(true))
|
||||
throw new IllegalStateException("Observable is completed");
|
||||
|
||||
observer.await();
|
||||
|
||||
try {
|
||||
return observer.takeNext();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw Exceptions.propagate(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Read only iterator");
|
||||
}
|
||||
}
|
||||
|
||||
private static class NextObserver<T> implements Observer<Notification<T>> {
|
||||
|
||||
private final BlockingQueue<Notification<T>> buf = new ArrayBlockingQueue<>(1);
|
||||
private final AtomicBoolean waiting = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Notification<T> args) {
|
||||
|
||||
if (waiting.getAndSet(false) || !args.isOnNext()) {
|
||||
Notification<T> toOffer = args;
|
||||
while (!buf.offer(toOffer)) {
|
||||
Notification<T> concurrentItem = buf.poll();
|
||||
|
||||
// in case if we won race condition with onComplete/onError method
|
||||
if (!concurrentItem.isOnNext())
|
||||
toOffer = concurrentItem;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void await() {
|
||||
waiting.set(true);
|
||||
}
|
||||
|
||||
public boolean isCompleted(boolean rethrowExceptionIfExists) {
|
||||
Notification<T> lastItem = buf.peek();
|
||||
if (lastItem == null)
|
||||
return false;
|
||||
|
||||
if (lastItem.isOnError())
|
||||
if (rethrowExceptionIfExists)
|
||||
throw Exceptions.propagate(lastItem.getException());
|
||||
else
|
||||
return true;
|
||||
|
||||
return lastItem.isOnCompleted();
|
||||
}
|
||||
|
||||
public T takeNext() throws InterruptedException {
|
||||
Notification<T> next = buf.take();
|
||||
|
||||
if (next.isOnError())
|
||||
throw Exceptions.propagate(next.getException());
|
||||
|
||||
if (next.isOnCompleted())
|
||||
throw new IllegalStateException("Observable is completed");
|
||||
|
||||
return next.getValue();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Scheduler;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public class OperationObserveOn {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> observeOn(Observable<T> source, Scheduler scheduler) {
|
||||
return new ObserveOn<>(source, scheduler);
|
||||
}
|
||||
|
||||
private static class ObserveOn<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<T> source;
|
||||
private final Scheduler scheduler;
|
||||
|
||||
public ObserveOn(Observable<T> source, Scheduler scheduler) {
|
||||
this.source = source;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
return source.subscribe(new ScheduledObserver<>(observer, scheduler));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.CompositeException;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationOnErrorResumeNextViaFunction<T> {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> onErrorResumeNextViaFunction(Observable<T> originalSequence, Func1<Exception, Observable<T>> resumeFunction) {
|
||||
return new OnErrorResumeNextViaFunction<>(originalSequence, resumeFunction);
|
||||
}
|
||||
|
||||
private static class OnErrorResumeNextViaFunction<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Func1<Exception, Observable<T>> resumeFunction;
|
||||
private final Observable<T> originalSequence;
|
||||
|
||||
public OnErrorResumeNextViaFunction(Observable<T> originalSequence, Func1<Exception, Observable<T>> resumeFunction) {
|
||||
this.resumeFunction = resumeFunction;
|
||||
this.originalSequence = originalSequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
// AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed
|
||||
final AtomicReference<AtomicObservableSubscription> subscriptionRef = new AtomicReference<>(new AtomicObservableSubscription());
|
||||
|
||||
// subscribe to the original Observable and remember the subscription
|
||||
subscriptionRef.get().wrap(new AtomicObservableSubscription(originalSequence.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
// forward the successful calls
|
||||
observer.onNext(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of passing the onError forward, we intercept and
|
||||
* "resume" with the resumeSequence.
|
||||
*/
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
/*
|
||||
* remember what the current subscription is so we can
|
||||
* determine if someone unsubscribes concurrently
|
||||
*/
|
||||
AtomicObservableSubscription currentSubscription = subscriptionRef.get();
|
||||
// check that we have not been unsubscribed before we can process the error
|
||||
if (currentSubscription != null)
|
||||
try {
|
||||
Observable<T> resumeSequence = resumeFunction.call(ex);
|
||||
/*
|
||||
* error occurred, so switch subscription to the
|
||||
* 'resumeSequence'
|
||||
*/
|
||||
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 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
|
||||
innerSubscription.unsubscribe();
|
||||
} catch (Exception e) {
|
||||
// the resume function failed so we need to call onError
|
||||
// I am using CompositeException so that both exceptions can be seen
|
||||
observer.onError(new CompositeException("OnErrorResume function failed", Arrays.asList(ex, e)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// forward the successful calls
|
||||
observer.onCompleted();
|
||||
}
|
||||
})));
|
||||
|
||||
return () -> {
|
||||
// this will get either the original, or the resumeSequence one and unsubscribe on it
|
||||
Subscription s = subscriptionRef.getAndSet(null);
|
||||
if (s != null)
|
||||
s.unsubscribe();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationOnErrorResumeNextViaObservable<T> {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> onErrorResumeNextViaObservable(Observable<T> originalSequence, Observable<T> resumeSequence) {
|
||||
return new OnErrorResumeNextViaObservable<>(originalSequence, resumeSequence);
|
||||
}
|
||||
|
||||
private static class OnErrorResumeNextViaObservable<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<T> resumeSequence;
|
||||
private final Observable<T> originalSequence;
|
||||
|
||||
public OnErrorResumeNextViaObservable(Observable<T> originalSequence, Observable<T> resumeSequence) {
|
||||
this.resumeSequence = resumeSequence;
|
||||
this.originalSequence = originalSequence;
|
||||
}
|
||||
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
// AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed
|
||||
final AtomicReference<AtomicObservableSubscription> subscriptionRef = new AtomicReference<>(subscription);
|
||||
|
||||
// subscribe to the original Observable and remember the subscription
|
||||
subscription.wrap(originalSequence.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
// forward the successful calls
|
||||
observer.onNext(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of passing the onError forward, we intercept and
|
||||
* "resume" with the resumeSequence.
|
||||
*/
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
/*
|
||||
* remember what the current subscription is so we can
|
||||
* determine if someone unsubscribes concurrently
|
||||
*/
|
||||
AtomicObservableSubscription currentSubscription = subscriptionRef.get();
|
||||
// check that we have not been unsubscribed before we can process the error
|
||||
if (currentSubscription != null) {
|
||||
/*
|
||||
* error occurred, so switch subscription to the
|
||||
* 'resumeSequence'
|
||||
*/
|
||||
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 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
|
||||
innerSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// forward the successful calls
|
||||
observer.onCompleted();
|
||||
}
|
||||
}));
|
||||
|
||||
return () -> {
|
||||
// this will get either the original, or the resumeSequence one and unsubscribe on it
|
||||
Subscription s = subscriptionRef.getAndSet(null);
|
||||
if (s != null)
|
||||
s.unsubscribe();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.CompositeException;
|
||||
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.
|
||||
*/
|
||||
public final class OperationOnErrorReturn<T> {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> onErrorReturn(Observable<T> originalSequence, Func1<Exception, T> resumeFunction) {
|
||||
return new OnErrorReturn<>(originalSequence, resumeFunction);
|
||||
}
|
||||
|
||||
private static class OnErrorReturn<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Func1<Exception, T> resumeFunction;
|
||||
private final Observable<T> originalSequence;
|
||||
|
||||
public OnErrorReturn(Observable<T> originalSequence, Func1<Exception, T> resumeFunction) {
|
||||
this.resumeFunction = resumeFunction;
|
||||
this.originalSequence = originalSequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
// AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed
|
||||
final AtomicReference<AtomicObservableSubscription> subscriptionRef = new AtomicReference<>(subscription);
|
||||
|
||||
// subscribe to the original Observable and remember the subscription
|
||||
subscription.wrap(originalSequence.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
// forward the successful calls
|
||||
observer.onNext(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of passing the onError forward, we intercept and
|
||||
* "resume" with the resumeSequence.
|
||||
*/
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
/*
|
||||
* remember what the current subscription is so we can
|
||||
* determine if someone unsubscribes concurrently
|
||||
*/
|
||||
AtomicObservableSubscription currentSubscription = subscriptionRef.get();
|
||||
// check that we have not been unsubscribed before we can process the error
|
||||
if (currentSubscription != null)
|
||||
try {
|
||||
/*
|
||||
* error occurred, so execute the function, give it
|
||||
* the exception and call onNext with the response
|
||||
*/
|
||||
onNext(resumeFunction.call(ex));
|
||||
/*
|
||||
* we are not handling an exception thrown from this
|
||||
* function ... should we do something?
|
||||
* error handling within an error handler is a weird
|
||||
* 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
|
||||
*/
|
||||
onCompleted();
|
||||
|
||||
/*
|
||||
* unsubscribe since it blew up
|
||||
*/
|
||||
currentSubscription.unsubscribe();
|
||||
} catch (Exception e) {
|
||||
// the return function failed so we need to call onError
|
||||
// I am using CompositeException so that both exceptions can be seen
|
||||
observer.onError(new CompositeException("OnErrorReturn function failed", Arrays.asList(ex, e)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// forward the successful calls
|
||||
observer.onCompleted();
|
||||
}
|
||||
}));
|
||||
|
||||
return () -> {
|
||||
// this will get either the original, or the resumeSequence one and unsubscribe on it
|
||||
Subscription s = subscriptionRef.getAndSet(null);
|
||||
if (s != null)
|
||||
s.unsubscribe();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func1;
|
||||
import rx.util.functions.Func2;
|
||||
|
||||
public final class OperationScan {
|
||||
|
||||
/**
|
||||
* Applies an accumulator function over an observable sequence and returns
|
||||
* each intermediate result with the specified source and accumulator.
|
||||
*
|
||||
* @param sequence An observable sequence of elements to project.
|
||||
* @param initialValue The initial (seed) accumulator value.
|
||||
* @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.
|
||||
*
|
||||
* @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) {
|
||||
return new Accumulator<>(sequence, initialValue, accumulator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies an accumulator function over an observable sequence and returns
|
||||
* each intermediate result with the specified source and accumulator.
|
||||
*
|
||||
* @param sequence An observable sequence of elements to project.
|
||||
* @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.
|
||||
*
|
||||
* @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) {
|
||||
return new Accumulator<>(sequence, null, accumulator);
|
||||
}
|
||||
|
||||
private static class Accumulator<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<T> sequence;
|
||||
private final T initialValue;
|
||||
private final Func2<T, T, T> accumlatorFunction;
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
private Accumulator(Observable<T> sequence, T initialValue, Func2<T, T, T> accumulator) {
|
||||
this.sequence = sequence;
|
||||
this.initialValue = initialValue;
|
||||
this.accumlatorFunction = accumulator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
|
||||
return subscription.wrap(sequence.subscribe(new Observer<T>() {
|
||||
private T acc = initialValue;
|
||||
private boolean hasSentInitialValue = false;
|
||||
|
||||
/**
|
||||
* We must synchronize this because we can't allow multiple
|
||||
* threads to execute the 'accumulatorFunction' at the same time
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
public synchronized void onNext(T value) {
|
||||
if (acc == null) {
|
||||
// we assume that acc is not allowed to be returned from accumulatorValue
|
||||
// so it's okay to check null as being the state we initialize on
|
||||
acc = value;
|
||||
// this is all we do for this first value if we didn't have an initialValue
|
||||
return;
|
||||
}
|
||||
if (!hasSentInitialValue) {
|
||||
hasSentInitialValue = true;
|
||||
observer.onNext(acc);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
acc = accumlatorFunction.call(acc, value);
|
||||
if (acc == null) {
|
||||
onError(new IllegalArgumentException("Null is an unsupported return value for an accumulator."));
|
||||
return;
|
||||
}
|
||||
observer.onNext(acc);
|
||||
} catch (Exception ex) {
|
||||
observer.onError(ex);
|
||||
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
observer.onError(ex);
|
||||
}
|
||||
|
||||
// synchronized because we access 'hasSentInitialValue'
|
||||
@Override
|
||||
public synchronized void onCompleted() {
|
||||
// if only one sequence value existed, we send it without any accumulation
|
||||
if (!hasSentInitialValue)
|
||||
observer.onNext(acc);
|
||||
observer.onCompleted();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
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.
|
||||
*/
|
||||
public final class OperationSkip {
|
||||
|
||||
/**
|
||||
* Skips a specified number of contiguous values from the start of a
|
||||
* Observable sequence and then returns the remaining values.
|
||||
*
|
||||
* @param items
|
||||
* @param num
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @see http://msdn.microsoft.com/en-us/library/hh229847(v=vs.103).aspx
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> skip(final Observable<T> items, final int num) {
|
||||
// wrap in a Observable so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
|
||||
return new Skip<>(items, num)::call;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* It IS thread-safe from within it while receiving onNext events from
|
||||
* multiple threads.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private static class Skip<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final int num;
|
||||
private final Observable<T> items;
|
||||
|
||||
Skip(final Observable<T> items, final int num) {
|
||||
this.num = num;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
return items.subscribe(new ItemObserver(observer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to subscribe to the 'items' Observable sequence and forward to
|
||||
* the actualObserver up to 'num' count.
|
||||
*/
|
||||
private class ItemObserver implements Observer<T> {
|
||||
|
||||
private AtomicInteger counter = new AtomicInteger();
|
||||
private final Observer<T> observer;
|
||||
|
||||
public ItemObserver(Observer<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
// skip them until we reach the 'num' value
|
||||
if (counter.incrementAndGet() > num)
|
||||
observer.onNext(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Scheduler;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public class OperationSubscribeOn {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> subscribeOn(Observable<T> source, Scheduler scheduler) {
|
||||
return new SubscribeOn<>(source, scheduler);
|
||||
}
|
||||
|
||||
private static class SubscribeOn<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<T> source;
|
||||
private final Scheduler scheduler;
|
||||
|
||||
public SubscribeOn(Observable<T> source, Scheduler scheduler) {
|
||||
this.source = source;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
return scheduler.schedule(() -> new ScheduledSubscription(source.subscribe(observer), scheduler));
|
||||
}
|
||||
}
|
||||
|
||||
private static class ScheduledSubscription implements Subscription {
|
||||
|
||||
private final Subscription underlying;
|
||||
private final Scheduler scheduler;
|
||||
|
||||
private ScheduledSubscription(Subscription underlying, Scheduler scheduler) {
|
||||
this.underlying = underlying;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
scheduler.schedule(underlying::unsubscribe);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.SynchronizedObserver;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* An observable that wraps an observable of the same type and then enforces the
|
||||
* semantics expected of a well-behaved observable.
|
||||
* <p>
|
||||
* An observable that ensures onNext, onCompleted, or onError calls on its
|
||||
* subscribers are not interleaved, onCompleted and onError are only called once
|
||||
* respectively, and no onNext calls follow onCompleted and onError calls.
|
||||
* <p>
|
||||
* NOTE: {@link Observable#create} already wraps Observables so this is
|
||||
* generally redundant.
|
||||
*
|
||||
* @param <T> The type of the observable sequence.
|
||||
*/
|
||||
public final class OperationSynchronize<T> {
|
||||
|
||||
/**
|
||||
* 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 not interleaved, onCompleted and onError are only
|
||||
* called once respectively, and no onNext calls follow onCompleted and
|
||||
* onError calls.
|
||||
*
|
||||
* @param observable
|
||||
* @param <T>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> synchronize(Observable<T> observable) {
|
||||
return new Synchronize<>(observable);
|
||||
}
|
||||
|
||||
private static class Synchronize<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
public Synchronize(Observable<T> innerObservable) {
|
||||
this.innerObservable = innerObservable;
|
||||
}
|
||||
|
||||
private final Observable<T> innerObservable;
|
||||
private SynchronizedObserver<T> atomicObserver;
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
atomicObserver = new SynchronizedObserver<>(observer, subscription);
|
||||
return subscription.wrap(innerObservable.subscribe(atomicObserver));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.subscriptions.Subscriptions;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Returns a specified number of contiguous values from the start of an
|
||||
* observable sequence.
|
||||
*/
|
||||
public final class OperationTake {
|
||||
|
||||
/**
|
||||
* Returns a specified number of contiguous values from the start of an
|
||||
* observable sequence.
|
||||
*
|
||||
* @param items
|
||||
* @param num
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> take(final Observable<T> items, final int num) {
|
||||
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
|
||||
return new Take<>(items, num)::call;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* It IS thread-safe from within it while receiving onNext events from
|
||||
* multiple threads.
|
||||
* <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.
|
||||
* <p>
|
||||
* 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>
|
||||
*/
|
||||
private static class Take<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final AtomicInteger counter = new AtomicInteger();
|
||||
private final Observable<T> items;
|
||||
private final int num;
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
private Take(Observable<T> items, int num) {
|
||||
this.items = items;
|
||||
this.num = num;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
if (num < 1) {
|
||||
items.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
}
|
||||
}).unsubscribe();
|
||||
observer.onCompleted();
|
||||
return Subscriptions.empty();
|
||||
}
|
||||
|
||||
return subscription.wrap(items.subscribe(new ItemObserver(observer)));
|
||||
}
|
||||
|
||||
private class ItemObserver implements Observer<T> {
|
||||
|
||||
private final Observer<T> observer;
|
||||
|
||||
public ItemObserver(Observer<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
if (counter.getAndSet(num) < num)
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
if (counter.getAndSet(num) < num)
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
final int count = counter.incrementAndGet();
|
||||
if (count <= num) {
|
||||
observer.onNext(args);
|
||||
if (count == num)
|
||||
observer.onCompleted();
|
||||
}
|
||||
if (count >= num)
|
||||
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Returns a specified number of contiguous elements from the end of an
|
||||
* observable sequence.
|
||||
*/
|
||||
public final class OperationTakeLast {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> takeLast(final Observable<T> items, final int count) {
|
||||
return new TakeLast<>(items, count)::call;
|
||||
}
|
||||
|
||||
private static class TakeLast<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final int count;
|
||||
private final Observable<T> items;
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
TakeLast(final Observable<T> items, final int count) {
|
||||
this.count = count;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
return subscription.wrap(items.subscribe(new ItemObserver(observer)));
|
||||
}
|
||||
|
||||
private class ItemObserver implements Observer<T> {
|
||||
|
||||
private LinkedBlockingDeque<T> deque = new LinkedBlockingDeque<>(count);
|
||||
private final Observer<T> observer;
|
||||
|
||||
public ItemObserver(Observer<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
Iterator<T> reverse = deque.descendingIterator();
|
||||
while (reverse.hasNext())
|
||||
observer.onNext(reverse.next());
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
while (!deque.offerFirst(args))
|
||||
deque.removeLast();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.AtomicObserver;
|
||||
import rx.util.functions.Func1;
|
||||
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.
|
||||
*/
|
||||
public final class OperationTakeWhile {
|
||||
|
||||
/**
|
||||
* Returns a specified number of contiguous values from the start of an
|
||||
* observable sequence.
|
||||
*
|
||||
* @param items
|
||||
* @param predicate a function to test each source element for a condition
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> takeWhile(final Observable<T> items, final Func1<T, Boolean> predicate) {
|
||||
return takeWhileWithIndex(items, OperationTakeWhile.<T>skipIndex(predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns values from an observable sequence as long as a specified
|
||||
* condition is true, and then skips the remaining values.
|
||||
*
|
||||
* @param items
|
||||
* @param predicate a function to test each element for a condition; the
|
||||
* second parameter of the function represents the index of the source
|
||||
* element; otherwise, false.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> takeWhileWithIndex(final Observable<T> items, final Func2<T, Integer, Boolean> predicate) {
|
||||
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
|
||||
return new TakeWhile<T>(items, predicate)::call;
|
||||
}
|
||||
|
||||
private static <T> Func2<T, Integer, Boolean> skipIndex(final Func1<T, Boolean> underlying) {
|
||||
return (T input, Integer index) -> underlying.call(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* It IS thread-safe from within it while receiving onNext events from
|
||||
* multiple threads.
|
||||
* <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.
|
||||
* <p>
|
||||
* 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>
|
||||
*/
|
||||
private static class TakeWhile<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final AtomicInteger counter = new AtomicInteger();
|
||||
private final Observable<T> items;
|
||||
private final Func2<T, Integer, Boolean> predicate;
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
private TakeWhile(Observable<T> items, Func2<T, Integer, Boolean> predicate) {
|
||||
this.items = items;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
return subscription.wrap(items.subscribe(new ItemObserver(observer)));
|
||||
}
|
||||
|
||||
private class ItemObserver implements Observer<T> {
|
||||
|
||||
private final Observer<T> observer;
|
||||
|
||||
public ItemObserver(Observer<T> observer) {
|
||||
// Using AtomicObserver because the unsubscribe, onCompleted, onError and error handling behavior
|
||||
// needs "isFinished" logic to not send duplicated events
|
||||
// The 'testTakeWhile1' and 'testTakeWhile2' tests fail without this.
|
||||
this.observer = new AtomicObserver<>(subscription, observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
Boolean isSelected;
|
||||
try {
|
||||
isSelected = predicate.call(args, counter.getAndIncrement());
|
||||
} catch (Exception e) {
|
||||
observer.onError(e);
|
||||
return;
|
||||
}
|
||||
if (isSelected)
|
||||
observer.onNext(args);
|
||||
else {
|
||||
observer.onCompleted();
|
||||
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.subscriptions.Subscriptions;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public class OperationToObservableFuture {
|
||||
|
||||
private static class ToObservableFuture<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Future<T> that;
|
||||
private final Long time;
|
||||
private final TimeUnit unit;
|
||||
|
||||
public ToObservableFuture(Future<T> that) {
|
||||
this.that = that;
|
||||
this.time = null;
|
||||
this.unit = null;
|
||||
}
|
||||
|
||||
public ToObservableFuture(Future<T> that, long time, TimeUnit unit) {
|
||||
this.that = that;
|
||||
this.time = time;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
try {
|
||||
T value = (time == null) ? that.get() : that.get(time, unit);
|
||||
|
||||
if (!that.isCancelled())
|
||||
observer.onNext(value);
|
||||
observer.onCompleted();
|
||||
} catch (Exception e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
// the get() has already completed so there is no point in
|
||||
// giving the user a way to cancel.
|
||||
return Subscriptions.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> toObservableFuture(final Future<T> that) {
|
||||
return new ToObservableFuture<>(that);
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> toObservableFuture(final Future<T> that, long time, TimeUnit unit) {
|
||||
return new ToObservableFuture<>(that, time, unit);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.subscriptions.Subscriptions;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Accepts an Iterable object and exposes it as an Observable.
|
||||
*
|
||||
* @param <T> The type of the Iterable sequence.
|
||||
*/
|
||||
public final class OperationToObservableIterable<T> {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> toObservableIterable(Iterable<T> iterable) {
|
||||
return (Observer<T> observer) -> {
|
||||
for (T item : iterable)
|
||||
observer.onNext(item);
|
||||
observer.onCompleted();
|
||||
|
||||
return Subscriptions.empty();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationToObservableList<T> {
|
||||
|
||||
public static <T> Func1<Observer<List<T>>, Subscription> toObservableList(Observable<T> that) {
|
||||
return new ToObservableList<>(that);
|
||||
}
|
||||
|
||||
private static class ToObservableList<T> implements Func1<Observer<List<T>>, Subscription> {
|
||||
|
||||
private final Observable<T> that;
|
||||
|
||||
public ToObservableList(Observable<T> that) {
|
||||
this.that = that;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<List<T>> observer) {
|
||||
|
||||
return that.subscribe(new Observer<T>() {
|
||||
final ConcurrentLinkedQueue<T> list = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
// onNext can be concurrently executed so list must be thread-safe
|
||||
list.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
observer.onError(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
try {
|
||||
// copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface
|
||||
ArrayList<T> l = new ArrayList<>(list.size());
|
||||
for (T t : list)
|
||||
l.add(t);
|
||||
|
||||
// benjchristensen => I want to make this list immutable but some clients are sorting this
|
||||
// instead of using toSortedList() and this change breaks them until we migrate their code.
|
||||
// observer.onNext(Collections.unmodifiableList(l));
|
||||
observer.onNext(l);
|
||||
observer.onCompleted();
|
||||
} catch (Exception e) {
|
||||
onError(e);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
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.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public final class OperationToObservableSortedList<T> {
|
||||
|
||||
/**
|
||||
* Sort T objects by their natural order (object must implement Comparable).
|
||||
*
|
||||
* @param sequence
|
||||
*
|
||||
* @throws ClassCastException if T objects do not implement Comparable
|
||||
* @return
|
||||
*/
|
||||
public static <T> Func1<Observer<List<T>>, Subscription> toSortedList(Observable<T> sequence) {
|
||||
return new ToObservableSortedList<>(sequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort T objects using the defined sort function.
|
||||
*
|
||||
* @param sequence
|
||||
* @param sortFunction
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static <T> Func1<Observer<List<T>>, Subscription> toSortedList(Observable<T> sequence, Func2<T, T, Integer> sortFunction) {
|
||||
return new ToObservableSortedList<>(sequence, sortFunction);
|
||||
}
|
||||
|
||||
private static class ToObservableSortedList<T> implements Func1<Observer<List<T>>, Subscription> {
|
||||
|
||||
private final Observable<T> that;
|
||||
private final ConcurrentLinkedQueue<T> list = new ConcurrentLinkedQueue<>();
|
||||
private final Func2<T, T, Integer> sortFunction;
|
||||
|
||||
// unchecked as we're support Object for the default
|
||||
@SuppressWarnings("unchecked")
|
||||
private ToObservableSortedList(Observable<T> that) {
|
||||
this(that, defaultSortFunction);
|
||||
}
|
||||
|
||||
private ToObservableSortedList(Observable<T> that, Func2<T, T, Integer> sortFunction) {
|
||||
this.that = that;
|
||||
this.sortFunction = sortFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<List<T>> observer) {
|
||||
return that.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
// onNext can be concurrently executed so list must be thread-safe
|
||||
list.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
observer.onError(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
try {
|
||||
// copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface
|
||||
ArrayList<T> l = new ArrayList<>(list.size());
|
||||
for (T t : list)
|
||||
l.add(t);
|
||||
|
||||
// sort the list before delivery
|
||||
Collections.sort(l, (T o1, T o2) -> sortFunction.call(o1, o2));
|
||||
|
||||
observer.onNext(Collections.unmodifiableList(l));
|
||||
observer.onCompleted();
|
||||
} catch (Exception e) {
|
||||
onError(e);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// raw because we want to support Object for this default
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static Func2 defaultSortFunction = new DefaultComparableFunction();
|
||||
|
||||
private static class DefaultComparableFunction implements Func2<Object, Object, Integer> {
|
||||
|
||||
// unchecked because we want to support Object for this default
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Integer call(Object t1, Object t2) {
|
||||
Comparable<Object> c1 = (Comparable<Object>) t1;
|
||||
Comparable<Object> c2 = (Comparable<Object>) t2;
|
||||
return c1.compareTo(c2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationWhere {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> where(Observable<T> that, Func1<T, Boolean> predicate) {
|
||||
return OperationFilter.filter(that, predicate);
|
||||
}
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.SynchronizedObserver;
|
||||
import rx.util.functions.Func1;
|
||||
import rx.util.functions.Func2;
|
||||
import rx.util.functions.Func3;
|
||||
import rx.util.functions.Func4;
|
||||
import rx.util.functions.FuncN;
|
||||
import rx.util.functions.Functions;
|
||||
|
||||
public final class OperationZip {
|
||||
|
||||
public static <T0, T1, R> Func1<Observer<R>, Subscription> zip(Observable<T0> w0, Observable<T1> w1, Func2<T0, T1, R> zipFunction) {
|
||||
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(zipFunction));
|
||||
a.addObserver(new ZipObserver<>(a, w0));
|
||||
a.addObserver(new ZipObserver<>(a, w1));
|
||||
return a;
|
||||
}
|
||||
|
||||
public static <T0, T1, T2, R> Func1<Observer<R>, Subscription> zip(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Func3<T0, T1, T2, R> zipFunction) {
|
||||
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(zipFunction));
|
||||
a.addObserver(new ZipObserver<>(a, w0));
|
||||
a.addObserver(new ZipObserver<>(a, w1));
|
||||
a.addObserver(new ZipObserver<>(a, w2));
|
||||
return a;
|
||||
}
|
||||
|
||||
public static <T0, T1, T2, T3, R> Func1<Observer<R>, Subscription> zip(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Observable<T3> w3, Func4<T0, T1, T2, T3, R> zipFunction) {
|
||||
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(zipFunction));
|
||||
a.addObserver(new ZipObserver<>(a, w0));
|
||||
a.addObserver(new ZipObserver<>(a, w1));
|
||||
a.addObserver(new ZipObserver<>(a, w2));
|
||||
a.addObserver(new ZipObserver<>(a, w3));
|
||||
return a;
|
||||
}
|
||||
|
||||
/*
|
||||
* ThreadSafe
|
||||
*/
|
||||
private static class ZipObserver<R, T> implements Observer<T> {
|
||||
|
||||
final Observable<T> w;
|
||||
final Aggregator<R> a;
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
private final AtomicBoolean subscribed = new AtomicBoolean(false);
|
||||
|
||||
public ZipObserver(Aggregator<R> a, Observable<T> w) {
|
||||
this.a = a;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
public void startWatching() {
|
||||
if (subscribed.compareAndSet(false, true))
|
||||
// only subscribe once even if called more than once
|
||||
subscription.wrap(w.subscribe(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
a.complete(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
a.error(this, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
try {
|
||||
a.next(this, args);
|
||||
} catch (Exception e) {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private static class Aggregator<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private volatile SynchronizedObserver<T> observer;
|
||||
private final FuncN<T> zipFunction;
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
private final AtomicBoolean running = new AtomicBoolean(true);
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
private final ConcurrentLinkedQueue<ZipObserver<T, ?>> observers = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public Aggregator(FuncN<T> zipFunction) {
|
||||
this.zipFunction = zipFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param w
|
||||
*/
|
||||
private void addObserver(ZipObserver<T, ?> w) {
|
||||
// initialize this ZipObserver
|
||||
observers.add(w);
|
||||
receivedValuesPerObserver.put(w, new ConcurrentLinkedQueue<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive notification of a Observer completing its iterations.
|
||||
*
|
||||
* @param w
|
||||
*/
|
||||
void complete(ZipObserver<T, ?> w) {
|
||||
// store that this ZipObserver is completed
|
||||
completed.put(w, Boolean.TRUE);
|
||||
// if all ZipObservers are completed, we mark the whole thing as completed
|
||||
if (completed.size() == observers.size())
|
||||
if (running.compareAndSet(true, false))
|
||||
// this thread succeeded in setting running=false so let's propagate the completion
|
||||
// mark ourselves as done
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive error for a Observer. Throw the error up the chain and stop
|
||||
* processing.
|
||||
*
|
||||
* @param w
|
||||
*/
|
||||
void error(ZipObserver<T, ?> w, Exception e) {
|
||||
if (running.compareAndSet(true, false)) {
|
||||
// this thread succeeded in setting running=false so let's propagate the error
|
||||
observer.onError(e);
|
||||
/*
|
||||
* since we receive an error we want to tell everyone to stop
|
||||
*/
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the next value from a Observer.
|
||||
* <p>
|
||||
* If we have received values from all Observers, trigger the zip
|
||||
* function, otherwise store the value and keep waiting.
|
||||
*
|
||||
* @param w
|
||||
* @param arg
|
||||
*/
|
||||
void next(ZipObserver<T, ?> w, Object arg) {
|
||||
if (observer == null)
|
||||
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())
|
||||
return;
|
||||
|
||||
// store the value we received and below we'll decide if we are to send it to the Observer
|
||||
receivedValuesPerObserver.get(w).add(arg);
|
||||
|
||||
// define here so the variable is out of the synchronized scope
|
||||
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
|
||||
*/
|
||||
synchronized (this) {
|
||||
// if all ZipObservers in 'receivedValues' map have a value, invoke the zipFunction
|
||||
for (ZipObserver<T, ?> rw : receivedValuesPerObserver.keySet())
|
||||
if (receivedValuesPerObserver.get(rw).peek() == null)
|
||||
// we have a null meaning the queues aren't all populated so won't do anything
|
||||
return;
|
||||
// if we get to here this means all the queues have data
|
||||
int i = 0;
|
||||
for (ZipObserver<T, ?> rw : observers)
|
||||
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
|
||||
// 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
|
||||
observer.onNext(zipFunction.call(argsToZip));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
if (started.compareAndSet(false, true)) {
|
||||
AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
this.observer = new SynchronizedObserver<>(observer, subscription);
|
||||
/*
|
||||
* start the Observers
|
||||
*/
|
||||
for (ZipObserver<T, ?> rw : observers)
|
||||
rw.startWatching();
|
||||
|
||||
return subscription.wrap(this::stop);
|
||||
} else
|
||||
/*
|
||||
* a Observer already has subscribed so blow up
|
||||
*/
|
||||
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
|
||||
* and result in deadlocks. (http://jira/browse/API-4060)
|
||||
*
|
||||
* AtomicObservableSubscription uses compareAndSet instead of locking to
|
||||
* avoid deadlocks but ensure single-execution.
|
||||
*
|
||||
* We do the same in the implementation of this method.
|
||||
*
|
||||
* ThreadSafety of this method is provided by:
|
||||
* - AtomicBoolean[running].compareAndSet
|
||||
* - ConcurrentLinkedQueue[Observers]
|
||||
* - ZipObserver.subscription being an AtomicObservableSubscription
|
||||
*/
|
||||
private void stop() {
|
||||
/*
|
||||
* tell ourselves to stop processing onNext events by setting
|
||||
* running=false
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.observables.GroupedObservable;
|
||||
import rx.util.functions.Func1;
|
||||
import rx.util.functions.Functions;
|
||||
|
||||
public final class OperatorGroupBy {
|
||||
|
||||
public static <K, T, R> Func1<Observer<GroupedObservable<K, R>>, Subscription> groupBy(Observable<T> source, final Func1<T, K> keySelector, final Func1<T, R> elementSelector) {
|
||||
|
||||
final Observable<KeyValue<K, R>> keyval = source.map(new Func1<T, KeyValue<K, R>>() {
|
||||
@Override
|
||||
public KeyValue<K, R> call(T t) {
|
||||
K key = keySelector.call(t);
|
||||
R value = elementSelector.call(t);
|
||||
|
||||
return new KeyValue<K, R>(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
return new GroupBy<K, R>(keyval);
|
||||
}
|
||||
|
||||
public static <K, T> Func1<Observer<GroupedObservable<K, T>>, Subscription> groupBy(Observable<T> source, final Func1<T, K> keySelector) {
|
||||
return groupBy(source, keySelector, Functions.<T>identity());
|
||||
}
|
||||
|
||||
private static class GroupBy<K, V> implements Func1<Observer<GroupedObservable<K, V>>, Subscription> {
|
||||
|
||||
private final Observable<KeyValue<K, V>> source;
|
||||
private final ConcurrentHashMap<K, Boolean> keys = new ConcurrentHashMap<K, Boolean>();
|
||||
|
||||
private GroupBy(Observable<KeyValue<K, V>> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<GroupedObservable<K, V>> observer) {
|
||||
|
||||
return source.subscribe(new Observer<KeyValue<K, V>>() {
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(final KeyValue<K, V> args) {
|
||||
K key = args.key;
|
||||
boolean newGroup = keys.putIfAbsent(key, true) == null;
|
||||
if (newGroup)
|
||||
observer.onNext(buildObservableFor(source, key));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static <K, R> GroupedObservable<K, R> buildObservableFor(Observable<KeyValue<K, R>> source, final K key) {
|
||||
final Observable<R> observable = source.filter(new Func1<KeyValue<K, R>, Boolean>() {
|
||||
@Override
|
||||
public Boolean call(KeyValue<K, R> pair) {
|
||||
return key.equals(pair.key);
|
||||
}
|
||||
}).map(new Func1<KeyValue<K, R>, R>() {
|
||||
@Override
|
||||
public R call(KeyValue<K, R> pair) {
|
||||
return pair.value;
|
||||
}
|
||||
});
|
||||
return new GroupedObservable<K, R>(key, new Func1<Observer<R>, Subscription>() {
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<R> observer) {
|
||||
return observable.subscribe(observer);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private static class KeyValue<K, V> {
|
||||
|
||||
private final K key;
|
||||
private final V value;
|
||||
|
||||
private KeyValue(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public class OperatorTakeUntil {
|
||||
|
||||
/**
|
||||
* Returns the values from the source observable sequence until the other
|
||||
* observable sequence produces a value.
|
||||
*
|
||||
* @param source the source sequence to propagate elements for.
|
||||
* @param other the observable sequence that terminates propagation of
|
||||
* elements of the source sequence.
|
||||
* @param <T> the type of source.
|
||||
* @param <E> the other type.
|
||||
*
|
||||
* @return An observable sequence containing the elements of the source
|
||||
* sequence up to the point the other sequence interrupted further
|
||||
* propagation.
|
||||
*/
|
||||
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>> o = Observable.create(new OtherObservable<T, E>(other));
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
/**
|
||||
* In JDK 7 we could use 'varargs' instead of 'unchecked'. See
|
||||
* 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
|
||||
*/
|
||||
Observable<Notification<T>> result = Observable.merge(s, o);
|
||||
|
||||
return result.takeWhile(new Func1<Notification<T>, Boolean>() {
|
||||
@Override
|
||||
public Boolean call(Notification<T> notification) {
|
||||
return !notification.halt;
|
||||
}
|
||||
}).map(new Func1<Notification<T>, T>() {
|
||||
@Override
|
||||
public T call(Notification<T> notification) {
|
||||
return notification.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class Notification<T> {
|
||||
|
||||
private final boolean halt;
|
||||
private final T value;
|
||||
|
||||
public static <T> Notification<T> value(T value) {
|
||||
return new Notification<T>(false, value);
|
||||
}
|
||||
|
||||
public static <T> Notification<T> halt() {
|
||||
return new Notification<T>(true, null);
|
||||
}
|
||||
|
||||
private Notification(boolean halt, T value) {
|
||||
this.halt = halt;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SourceObservable<T> implements Func1<Observer<Notification<T>>, Subscription> {
|
||||
|
||||
private final Observable<T> sequence;
|
||||
|
||||
private SourceObservable(Observable<T> sequence) {
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<Notification<T>> notificationObserver) {
|
||||
return sequence.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
notificationObserver.onNext(Notification.<T>halt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
notificationObserver.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
notificationObserver.onNext(Notification.value(args));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static class OtherObservable<T, E> implements Func1<Observer<Notification<T>>, Subscription> {
|
||||
|
||||
private final Observable<E> sequence;
|
||||
|
||||
private OtherObservable(Observable<E> sequence) {
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<Notification<T>> notificationObserver) {
|
||||
return sequence.subscribe(new Observer<E>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
notificationObserver.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(E args) {
|
||||
notificationObserver.onNext(Notification.<T>halt());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package rx.operators;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import rx.Notification;
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.util.Exceptions;
|
||||
|
||||
/**
|
||||
* @see https://github.com/Netflix/RxJava/issues/50
|
||||
*/
|
||||
public class OperatorToIterator {
|
||||
|
||||
/**
|
||||
* Returns an iterator that iterates all values of the observable.
|
||||
*
|
||||
* @param that an observable sequence to get an iterator for.
|
||||
* @param <T> the type of source.
|
||||
*
|
||||
* @return the iterator that could be used to iterate over the elements of
|
||||
* the observable.
|
||||
*/
|
||||
public static <T> Iterator<T> toIterator(Observable<T> that) {
|
||||
final BlockingQueue<Notification<T>> notifications = new LinkedBlockingQueue<Notification<T>>();
|
||||
|
||||
Observable.materialize(that).subscribe(new Observer<Notification<T>>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Notification<T> args) {
|
||||
notifications.offer(args);
|
||||
}
|
||||
});
|
||||
|
||||
return new Iterator<T>() {
|
||||
private Notification<T> buf;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (buf == null)
|
||||
buf = take();
|
||||
return !buf.isOnCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (buf == null)
|
||||
buf = take();
|
||||
if (buf.isOnError())
|
||||
throw Exceptions.propagate(buf.getException());
|
||||
|
||||
T result = buf.getValue();
|
||||
buf = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
private Notification<T> take() {
|
||||
try {
|
||||
return notifications.take();
|
||||
} catch (InterruptedException e) {
|
||||
throw Exceptions.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Read-only iterator");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observer;
|
||||
import rx.Scheduler;
|
||||
import rx.util.functions.Action0;
|
||||
|
||||
/*
|
||||
* package
|
||||
*/
|
||||
class ScheduledObserver<T> implements Observer<T> {
|
||||
|
||||
private final Observer<T> underlying;
|
||||
private final Scheduler scheduler;
|
||||
|
||||
public ScheduledObserver(Observer<T> underlying, Scheduler scheduler) {
|
||||
this.underlying = underlying;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
scheduler.schedule(() -> {
|
||||
underlying.onCompleted();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
scheduler.schedule(() -> underlying.onError(e));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(final T args) {
|
||||
scheduler.schedule(() -> {
|
||||
underlying.onNext(args);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.plugins;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
|
||||
/**
|
||||
* Abstract class for defining error handling logic in addition to the normal
|
||||
* {@link Observer#onError(Exception)} behavior.
|
||||
* <p>
|
||||
* For example, all Exceptions can be logged using this handler even if
|
||||
* {@link Observer#onError(Exception)} is ignored or not provided when an
|
||||
* {@link Observable} is subscribed to.
|
||||
* <p>
|
||||
* See {@link RxJavaPlugins} or the RxJava GitHub Wiki for information on
|
||||
* configuring plugins: <a
|
||||
* href="https://github.com/Netflix/RxJava/wiki/Plugins">https://github.com/Netflix/RxJava/wiki/Plugins</a>.
|
||||
*/
|
||||
public abstract class RxJavaErrorHandler {
|
||||
|
||||
/**
|
||||
* Receives all Exceptions from an {@link Observable} passed to
|
||||
* {@link Observer#onError(Exception)}.
|
||||
*
|
||||
* @param e Exception
|
||||
*/
|
||||
public void handleError(Exception e) {
|
||||
// do nothing by default
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.plugins;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link RxJavaErrorHandler} that does nothing.
|
||||
*
|
||||
* @ExcludeFromJavadoc
|
||||
*/
|
||||
public class RxJavaErrorHandlerDefault extends RxJavaErrorHandler {
|
||||
|
||||
private static RxJavaErrorHandlerDefault INSTANCE = new RxJavaErrorHandlerDefault();
|
||||
|
||||
public static RxJavaErrorHandler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.plugins;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Abstract ExecutionHook with invocations at different lifecycle points of
|
||||
* {@link Observable} execution with a default no-op implementation.
|
||||
* <p>
|
||||
* See {@link RxJavaPlugins} or the RxJava GitHub Wiki for information on
|
||||
* configuring plugins: <a
|
||||
* href="https://github.com/Netflix/RxJava/wiki/Plugins">https://github.com/Netflix/RxJava/wiki/Plugins</a>.
|
||||
* <p>
|
||||
* <b>Note on thread-safety and performance</b>
|
||||
* <p>
|
||||
* A single implementation of this class will be used globally so methods on
|
||||
* this class will be invoked concurrently from multiple threads so all
|
||||
* functionality must be thread-safe.
|
||||
* <p>
|
||||
* Methods are also invoked synchronously and will add to execution time of the
|
||||
* observable so all behavior should be fast. If anything time-consuming is to
|
||||
* be done it should be spawned asynchronously onto separate worker threads.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public abstract class RxJavaObservableExecutionHook {
|
||||
|
||||
/**
|
||||
* Invoked before {@link Observable#subscribe(rx.Observer)} is about to be
|
||||
* executed.
|
||||
* <p>
|
||||
* This can be used to decorate or replace the <code>onSubscribe</code>
|
||||
* function or just perform extra logging, metrics and other such things and
|
||||
* pass-thru the function.
|
||||
*
|
||||
* @param observableInstance The executing {@link Observable} instance.
|
||||
* @param onSubscribe original {@link Func1}<{@link
|
||||
* Observer}{@code<T>},
|
||||
* {@link Subscription}> to be executed
|
||||
*
|
||||
* @return {@link Func1}<{@link Observer}{@code<T>}, {@link Subscription}>
|
||||
* function that can be modified, decorated, replaced or just returned as a
|
||||
* pass-thru.
|
||||
*/
|
||||
public <T> Func1<Observer<T>, Subscription> onSubscribeStart(Observable<T> observableInstance, Func1<Observer<T>, Subscription> onSubscribe) {
|
||||
// pass-thru by default
|
||||
return onSubscribe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked after successful execution of
|
||||
* {@link Observable#subscribe(rx.Observer)} with returned
|
||||
* {@link Subscription}.
|
||||
* <p>
|
||||
* This can be used to decorate or replace the {@link Subscription} instance
|
||||
* or just perform extra logging, metrics and other such things and
|
||||
* pass-thru the subscription.
|
||||
*
|
||||
* @param observableInstance The executing {@link Observable} instance.
|
||||
* @param subscription original {@link Subscription}
|
||||
*
|
||||
* @return {@link Subscription} subscription that can be modified,
|
||||
* decorated, replaced or just returned as a pass-thru.
|
||||
*/
|
||||
public <T> Subscription onSubscribeReturn(Observable<T> observableInstance, Subscription subscription) {
|
||||
// pass-thru by default
|
||||
return subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked after failed execution of {@link Observable#subscribe(Observer)}
|
||||
* with thrown Exception.
|
||||
* <p>
|
||||
* This is NOT errors emitted via {@link Observer#onError(Exception)} but
|
||||
* exceptions thrown when attempting to subscribe to a {@link Func1}<{@link
|
||||
* Observer}{@code<T>}, {@link Subscription}>.
|
||||
*
|
||||
* @param observableInstance The executing {@link Observable} instance.
|
||||
* @param e Exception thrown by
|
||||
* {@link Observable#subscribe(Observer)}
|
||||
*
|
||||
* @return Exception that can be decorated, replaced or just returned as a
|
||||
* pass-thru.
|
||||
*/
|
||||
public <T> Exception onSubscribeError(Observable<T> observableInstance, Exception e) {
|
||||
// pass-thru by default
|
||||
return e;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.plugins;
|
||||
|
||||
/**
|
||||
* Default no-op implementation of {@link RxJavaObservableExecutionHook}
|
||||
*/
|
||||
/*
|
||||
* package
|
||||
*/
|
||||
class RxJavaObservableExecutionHookDefault extends RxJavaObservableExecutionHook {
|
||||
|
||||
private static RxJavaObservableExecutionHookDefault INSTANCE = new RxJavaObservableExecutionHookDefault();
|
||||
|
||||
public static RxJavaObservableExecutionHook getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.plugins;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Registry for plugin implementations that allows global override and handles
|
||||
* the retrieval of correct implementation based on order of precedence:
|
||||
* <ol>
|
||||
* <li>plugin registered globally via <code>register</code> methods in this
|
||||
* class</li>
|
||||
* <li>plugin registered and retrieved using
|
||||
* {@link java.lang.System#getProperty(String)} (see get methods for property
|
||||
* names)</li>
|
||||
* <li>default implementation</li>
|
||||
* </ol>
|
||||
* See the RxJava GitHub Wiki for more information:
|
||||
* <a href="https://github.com/Netflix/RxJava/wiki/Plugins">https://github.com/Netflix/RxJava/wiki/Plugins</a>.
|
||||
*/
|
||||
public class RxJavaPlugins {
|
||||
|
||||
private final static RxJavaPlugins INSTANCE = new RxJavaPlugins();
|
||||
|
||||
private final AtomicReference<RxJavaErrorHandler> errorHandler = new AtomicReference<RxJavaErrorHandler>();
|
||||
private final AtomicReference<RxJavaObservableExecutionHook> observableExecutionHook = new AtomicReference<RxJavaObservableExecutionHook>();
|
||||
|
||||
private RxJavaPlugins() {
|
||||
|
||||
}
|
||||
|
||||
public static RxJavaPlugins getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve instance of {@link RxJavaErrorHandler} to use based on order of
|
||||
* precedence as defined in {@link RxJavaPlugins} class header.
|
||||
* <p>
|
||||
* Override default by using
|
||||
* {@link #registerErrorHandler(RxJavaErrorHandler)} or setting property:
|
||||
* <code>rxjava.plugin.RxJavaErrorHandler.implementation</code> with the
|
||||
* full classname to load.
|
||||
*
|
||||
* @return {@link RxJavaErrorHandler} implementation to use
|
||||
*/
|
||||
public RxJavaErrorHandler getErrorHandler() {
|
||||
if (errorHandler.get() == null) {
|
||||
// check for an implementation from System.getProperty first
|
||||
Object impl = getPluginImplementationViaProperty(RxJavaErrorHandler.class);
|
||||
if (impl == null)
|
||||
// nothing set via properties so initialize with default
|
||||
errorHandler.compareAndSet(null, RxJavaErrorHandlerDefault.getInstance()); // we don't return from here but call get() again in case of thread-race so the winner will always get returned
|
||||
else
|
||||
// we received an implementation from the system property so use it
|
||||
errorHandler.compareAndSet(null, (RxJavaErrorHandler) impl);
|
||||
}
|
||||
return errorHandler.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link RxJavaErrorHandler} implementation as a global override
|
||||
* of any injected or default implementations.
|
||||
*
|
||||
* @param impl {@link RxJavaErrorHandler} implementation
|
||||
*
|
||||
* @throws IllegalStateException if called more than once or after the
|
||||
* default was initialized (if usage occurs before trying to register)
|
||||
*/
|
||||
public void registerErrorHandler(RxJavaErrorHandler impl) {
|
||||
if (!errorHandler.compareAndSet(null, impl))
|
||||
throw new IllegalStateException("Another strategy was already registered.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve instance of {@link RxJavaObservableExecutionHook} to use based
|
||||
* on order of precedence as defined in {@link RxJavaPlugins} class header.
|
||||
* <p>
|
||||
* Override default by using
|
||||
* {@link #registerObservableExecutionHook(RxJavaObservableExecutionHook)}
|
||||
* or setting property:
|
||||
* <code>rxjava.plugin.RxJavaObservableExecutionHook.implementation</code>
|
||||
* with the full classname to load.
|
||||
*
|
||||
* @return {@link RxJavaObservableExecutionHook} implementation to use
|
||||
*/
|
||||
public RxJavaObservableExecutionHook getObservableExecutionHook() {
|
||||
if (observableExecutionHook.get() == null) {
|
||||
// check for an implementation from System.getProperty first
|
||||
Object impl = getPluginImplementationViaProperty(RxJavaObservableExecutionHook.class);
|
||||
if (impl == null)
|
||||
// nothing set via properties so initialize with default
|
||||
observableExecutionHook.compareAndSet(null, RxJavaObservableExecutionHookDefault.getInstance()); // we don't return from here but call get() again in case of thread-race so the winner will always get returned
|
||||
else
|
||||
// we received an implementation from the system property so use it
|
||||
observableExecutionHook.compareAndSet(null, (RxJavaObservableExecutionHook) impl);
|
||||
}
|
||||
return observableExecutionHook.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link RxJavaObservableExecutionHook} implementation as a
|
||||
* global override of any injected or default implementations.
|
||||
*
|
||||
* @param impl {@link RxJavaObservableExecutionHook} implementation
|
||||
*
|
||||
* @throws IllegalStateException if called more than once or after the
|
||||
* default was initialized (if usage occurs before trying to register)
|
||||
*/
|
||||
public void registerObservableExecutionHook(RxJavaObservableExecutionHook impl) {
|
||||
if (!observableExecutionHook.compareAndSet(null, impl))
|
||||
throw new IllegalStateException("Another strategy was already registered.");
|
||||
}
|
||||
|
||||
private static Object getPluginImplementationViaProperty(Class<?> pluginClass) {
|
||||
String classSimpleName = pluginClass.getSimpleName();
|
||||
/*
|
||||
* Check system properties for plugin class.
|
||||
* <p>
|
||||
* This will only happen during system startup thus it's okay to use the
|
||||
* synchronized System.getProperties
|
||||
* as it will never get called in normal operations.
|
||||
*/
|
||||
String implementingClass = System.getProperty("rxjava.plugin." + classSimpleName + ".implementation");
|
||||
if (implementingClass != null)
|
||||
try {
|
||||
Class<?> cls = Class.forName(implementingClass);
|
||||
// narrow the scope (cast) to the type we're expecting
|
||||
cls = cls.asSubclass(pluginClass);
|
||||
return cls.newInstance();
|
||||
} catch (ClassCastException e) {
|
||||
throw new RuntimeException(classSimpleName + " implementation is not an instance of " + classSimpleName + ": " + implementingClass);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(classSimpleName + " implementation class not found: " + implementingClass, e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException(classSimpleName + " implementation not able to be instantiated: " + implementingClass, e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(classSimpleName + " implementation not able to be accessed: " + implementingClass, e);
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package rx.subjects;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.SynchronizedObserver;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public class Subject<T> extends Observable<T> implements Observer<T> {
|
||||
|
||||
public static <T> Subject<T> create() {
|
||||
final ConcurrentHashMap<Subscription, Observer<T>> observers = new ConcurrentHashMap<>();
|
||||
|
||||
Func1<Observer<T>, Subscription> onSubscribe = observer -> {
|
||||
final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
subscription.wrap(() -> {
|
||||
// on unsubscribe remove it from the map of outbound observers to notify
|
||||
observers.remove(subscription);
|
||||
});
|
||||
|
||||
// on subscribe add it to the map of outbound observers to notify
|
||||
observers.put(subscription, new SynchronizedObserver<>(observer, subscription));
|
||||
return subscription;
|
||||
};
|
||||
|
||||
return new Subject<>(onSubscribe, observers);
|
||||
}
|
||||
|
||||
private final ConcurrentHashMap<Subscription, Observer<T>> observers;
|
||||
|
||||
protected Subject(Func1<Observer<T>, Subscription> onSubscribe, ConcurrentHashMap<Subscription, Observer<T>> observers) {
|
||||
super(onSubscribe);
|
||||
this.observers = observers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
for (Observer<T> observer : observers.values())
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
for (Observer<T> observer : observers.values())
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
for (Observer<T> observer : observers.values())
|
||||
observer.onNext(args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package rx.subscriptions;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Subscription;
|
||||
|
||||
/**
|
||||
* Subscription that can be checked for status such as in a loop inside an
|
||||
* {@link Observable} to exit the loop if unsubscribed.
|
||||
*
|
||||
* @see Rx.Net equivalent BooleanDisposable at
|
||||
* http://msdn.microsoft.com/en-us/library/system.reactive.disposables.booleandisposable(v=vs.103).aspx
|
||||
*/
|
||||
public class BooleanSubscription implements Subscription {
|
||||
|
||||
private final AtomicBoolean unsubscribed = new AtomicBoolean(false);
|
||||
|
||||
public boolean isUnsubscribed() {
|
||||
return unsubscribed.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
unsubscribed.set(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package rx.subscriptions;
|
||||
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Action0;
|
||||
import rx.util.functions.FuncN;
|
||||
import rx.util.functions.Functions;
|
||||
|
||||
public class Subscriptions {
|
||||
|
||||
/**
|
||||
* A {@link Subscription} that does nothing.
|
||||
*
|
||||
* @return {@link Subscription}
|
||||
*/
|
||||
public static Subscription empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Subscription} implemented via a Func
|
||||
*
|
||||
* @return {@link Subscription}
|
||||
*/
|
||||
public static Subscription create(final Action0 unsubscribe) {
|
||||
return unsubscribe::call;
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Subscription} implemented via an anonymous function (such as
|
||||
* closures from other languages).
|
||||
*
|
||||
* @return {@link Subscription}
|
||||
*/
|
||||
public static Subscription create(final Object unsubscribe) {
|
||||
final FuncN<?> f = Functions.from(unsubscribe);
|
||||
return f::call;
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Subscription} that does nothing when its unsubscribe method is
|
||||
* called.
|
||||
*/
|
||||
private static final Subscription EMPTY = () -> {
|
||||
};
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import rx.Subscription;
|
||||
|
||||
/**
|
||||
* Thread-safe wrapper around Observable Subscription that ensures unsubscribe
|
||||
* can be called only once.
|
||||
* <p>
|
||||
* Also used to:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>allow the AtomicObserver to have access to the subscription in
|
||||
* asynchronous execution for checking if unsubscribed occurred without
|
||||
* onComplete/onError.</li>
|
||||
* <li>handle both synchronous and asynchronous subscribe() execution flows</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class AtomicObservableSubscription implements Subscription {
|
||||
|
||||
private final AtomicReference<Subscription> actualSubscription = new AtomicReference<>();
|
||||
private final AtomicBoolean unsubscribed = new AtomicBoolean(false);
|
||||
|
||||
public AtomicObservableSubscription() {
|
||||
|
||||
}
|
||||
|
||||
public AtomicObservableSubscription(Subscription actualSubscription) {
|
||||
this.actualSubscription.set(actualSubscription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the actual subscription once it exists (if it wasn't available when
|
||||
* constructed)
|
||||
*
|
||||
* @param actualSubscription
|
||||
*
|
||||
* @throws IllegalStateException if trying to set more than once (or use
|
||||
* this method after setting via constructor)
|
||||
*/
|
||||
public AtomicObservableSubscription wrap(Subscription actualSubscription) {
|
||||
if (!this.actualSubscription.compareAndSet(null, actualSubscription))
|
||||
throw new IllegalStateException("Can not set subscription more than once.");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
// get the real thing and set to null in an atomic operation so we will only ever call unsubscribe once
|
||||
Subscription actual = actualSubscription.getAndSet(null);
|
||||
// if it's not null we will unsubscribe
|
||||
if (actual != null) {
|
||||
actual.unsubscribe();
|
||||
unsubscribed.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isUnsubscribed() {
|
||||
return unsubscribed.get();
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package rx.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Observer;
|
||||
import rx.plugins.RxJavaPlugins;
|
||||
|
||||
/**
|
||||
* Wrapper around Observer to ensure compliance with Rx contract.
|
||||
* <p>
|
||||
* The following is taken from the Rx Design Guidelines document:
|
||||
* http://go.microsoft.com/fwlink/?LinkID=205219
|
||||
* <pre>
|
||||
* Messages sent to instances of the IObserver interface follow the following grammar:
|
||||
*
|
||||
* OnNext* (OnCompleted | OnError)?
|
||||
*
|
||||
* This grammar allows observable sequences to send any amount (0 or more) of OnNext messages to the subscribed
|
||||
* observer instance, optionally followed by a single success (OnCompleted) or failure (OnError) message.
|
||||
*
|
||||
* The single message indicating that an observable sequence has finished ensures that consumers of the observable
|
||||
* sequence can deterministically establish that it is safe to perform cleanup operations.
|
||||
*
|
||||
* A single failure further ensures that abort semantics can be maintained for operators that work on
|
||||
* multiple observable sequences (see paragraph 6.6).
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This wrapper will do the following:
|
||||
* <ul>
|
||||
* <li>Allow only single execution of either onError or onCompleted.</li>
|
||||
* <li>Once an onComplete or onError are performed, no further calls can be
|
||||
* executed</li>
|
||||
* <li>If unsubscribe is called, this means we call completed() and don't allow
|
||||
* any further onNext calls.</li>
|
||||
* <li>When onError or onComplete occur it will unsubscribe from the Observable
|
||||
* (if executing asynchronously).</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* It will not synchronize onNext execution. Use the
|
||||
* {@link SynchronizedObserver} to do that.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class AtomicObserver<T> implements Observer<T> {
|
||||
|
||||
private final Observer<T> actual;
|
||||
private final AtomicBoolean isFinished = new AtomicBoolean(false);
|
||||
private final AtomicObservableSubscription subscription;
|
||||
|
||||
public AtomicObserver(AtomicObservableSubscription subscription, Observer<T> actual) {
|
||||
this.subscription = subscription;
|
||||
this.actual = actual;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
if (isFinished.compareAndSet(false, true)) {
|
||||
try {
|
||||
actual.onCompleted();
|
||||
} catch (Exception e) {
|
||||
// handle errors if the onCompleted implementation fails, not just if the Observable fails
|
||||
onError(e);
|
||||
}
|
||||
// auto-unsubscribe
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
if (isFinished.compareAndSet(false, true)) {
|
||||
try {
|
||||
actual.onError(e);
|
||||
} catch (Exception e2) {
|
||||
// if the onError itself fails then pass to the plugin
|
||||
// see https://github.com/Netflix/RxJava/issues/216 for further discussion
|
||||
RxJavaPlugins.getInstance().getErrorHandler().handleError(e);
|
||||
RxJavaPlugins.getInstance().getErrorHandler().handleError(e2);
|
||||
// and throw exception despite that not being proper for Rx
|
||||
// https://github.com/Netflix/RxJava/issues/198
|
||||
throw new RuntimeException("Error occurred when trying to propagate error to Observer.onError", new CompositeException(Arrays.asList(e, e2)));
|
||||
}
|
||||
// auto-unsubscribe
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
try {
|
||||
if (!isFinished.get())
|
||||
actual.onNext(args);
|
||||
} catch (Exception e) {
|
||||
// handle errors if the onNext implementation fails, not just if the Observable fails
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Exception that is a composite of 1 or more other exceptions.
|
||||
* <p>
|
||||
* The <code>getMessage()</code> will return a concatenation of the composite
|
||||
* exceptions.
|
||||
*/
|
||||
public class CompositeException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 3026362227162912146L;
|
||||
|
||||
private final List<Exception> exceptions;
|
||||
private final String message;
|
||||
|
||||
public CompositeException(String messagePrefix, Collection<Exception> errors) {
|
||||
StringBuilder _message = new StringBuilder();
|
||||
if (messagePrefix != null)
|
||||
_message.append(messagePrefix).append(" => ");
|
||||
|
||||
List<Exception> _exceptions = new ArrayList<>();
|
||||
for (Exception e : errors) {
|
||||
_exceptions.add(e);
|
||||
if (_message.length() > 0)
|
||||
_message.append(", ");
|
||||
_message.append(e.getClass().getSimpleName()).append(":").append(e.getMessage());
|
||||
}
|
||||
this.exceptions = Collections.unmodifiableList(_exceptions);
|
||||
this.message = _message.toString();
|
||||
}
|
||||
|
||||
public CompositeException(Collection<Exception> errors) {
|
||||
this(null, errors);
|
||||
}
|
||||
|
||||
public List<Exception> getExceptions() {
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util;
|
||||
|
||||
public class Exceptions {
|
||||
|
||||
private Exceptions() {
|
||||
|
||||
}
|
||||
|
||||
public static RuntimeException propagate(Throwable t) {
|
||||
if (t instanceof RuntimeException)
|
||||
throw (RuntimeException) t;
|
||||
else
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public final class Range implements Iterable<Integer> {
|
||||
|
||||
private final int start;
|
||||
private final int end;
|
||||
private final int step;
|
||||
|
||||
public static Range createWithCount(int start, int count) {
|
||||
return create(start, start + count);
|
||||
}
|
||||
|
||||
public static Range create(int start, int end) {
|
||||
return new Range(start, end, 1);
|
||||
}
|
||||
|
||||
public static Range createWithStep(int start, int end, int step) {
|
||||
return new Range(start, end, step);
|
||||
}
|
||||
|
||||
private Range(int start, int end, int step) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Integer> iterator() {
|
||||
return new Iterator<Integer>() {
|
||||
private int current = start;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return current < end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer next() {
|
||||
if (!hasNext())
|
||||
throw new NoSuchElementException("No more elements");
|
||||
int result = current;
|
||||
current += step;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Read only iterator");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Range (" + start + ", " + end + "), step " + step;
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util;
|
||||
|
||||
import rx.Observer;
|
||||
|
||||
/**
|
||||
* A thread-safe Observer for transitioning states in operators.
|
||||
* <p>
|
||||
* Execution rules are:
|
||||
* <ul>
|
||||
* <li>Allow only single-threaded, synchronous, ordered execution of onNext,
|
||||
* onCompleted, onError</li>
|
||||
* <li>Once an onComplete or onError are performed, no further calls can be
|
||||
* executed</li>
|
||||
* <li>If unsubscribe is called, this means we call completed() and don't allow
|
||||
* any further onNext calls.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public final class SynchronizedObserver<T> implements Observer<T> {
|
||||
|
||||
/**
|
||||
* Intrinsic synchronized locking with double-check short-circuiting was
|
||||
* chosen after testing several other implementations.
|
||||
*
|
||||
* The code and results can be found here: -
|
||||
* https://github.com/benjchristensen/JavaLockPerformanceTests/tree/master/results/Observer
|
||||
* -
|
||||
* https://github.com/benjchristensen/JavaLockPerformanceTests/tree/master/src/com/benjchristensen/performance/locks/Observer
|
||||
*
|
||||
* The major characteristic that made me choose synchronized instead of
|
||||
* Reentrant or a customer AbstractQueueSynchronizer implementation is that
|
||||
* intrinsic locking performed better when nested, and AtomicObserver will
|
||||
* end up nested most of the time since Rx is compositional by its very
|
||||
* nature.
|
||||
*
|
||||
* // TODO composing of this class should rarely happen now with updated
|
||||
* design so this decision should be revisited
|
||||
*/
|
||||
private final Observer<T> observer;
|
||||
private final AtomicObservableSubscription subscription;
|
||||
private volatile boolean finishRequested = false;
|
||||
private volatile boolean finished = false;
|
||||
|
||||
public SynchronizedObserver(Observer<T> Observer, AtomicObservableSubscription subscription) {
|
||||
this.observer = Observer;
|
||||
this.subscription = subscription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T arg) {
|
||||
if (finished || finishRequested || subscription.isUnsubscribed())
|
||||
// if we're already stopped, or a finish request has been received, we won't allow further onNext requests
|
||||
return;
|
||||
synchronized (this) {
|
||||
// check again since this could have changed while waiting
|
||||
if (finished || finishRequested || subscription.isUnsubscribed())
|
||||
// if we're already stopped, or a finish request has been received, we won't allow further onNext requests
|
||||
return;
|
||||
observer.onNext(arg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
if (finished || subscription.isUnsubscribed())
|
||||
// another thread has already finished us, so we won't proceed
|
||||
return;
|
||||
finishRequested = true;
|
||||
synchronized (this) {
|
||||
// check again since this could have changed while waiting
|
||||
if (finished || subscription.isUnsubscribed())
|
||||
return;
|
||||
observer.onError(e);
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
if (finished || subscription.isUnsubscribed())
|
||||
// another thread has already finished us, so we won't proceed
|
||||
return;
|
||||
finishRequested = true;
|
||||
synchronized (this) {
|
||||
// check again since this could have changed while waiting
|
||||
if (finished || subscription.isUnsubscribed())
|
||||
return;
|
||||
observer.onCompleted();
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Action0 extends Function {
|
||||
|
||||
public void call();
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Action1<T1> extends Function {
|
||||
|
||||
public void call(T1 t1);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Action2<T1, T2> extends Function {
|
||||
|
||||
public void call(T1 t1, T2 t2);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Action3<T1, T2, T3> extends Function {
|
||||
|
||||
public void call(T1 t1, T2 t2, T3 t3);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Func0<R> extends Function {
|
||||
|
||||
public R call();
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Func1<T1, R> extends Function {
|
||||
|
||||
public R call(T1 t1);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Func2<T1, T2, R> extends Function {
|
||||
|
||||
public R call(T1 t1, T2 t2);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Func3<T1, T2, T3, R> extends Function {
|
||||
|
||||
public R call(T1 t1, T2 t2, T3 t3);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Func4<T1, T2, T3, T4, R> extends Function {
|
||||
|
||||
public R call(T1 t1, T2 t2, T3 t3, T4 t4);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Func5<T1, T2, T3, T4, T5, R> extends Function {
|
||||
|
||||
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Func6<T1, T2, T3, T4, T5, T6, R> extends Function {
|
||||
|
||||
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Func7<T1, T2, T3, T4, T5, T6, T7, R> extends Function {
|
||||
|
||||
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Func8<T1, T2, T3, T4, T5, T6, T7, T8, R> extends Function {
|
||||
|
||||
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface Func9<T1, T2, T3, T4, T5, T6, T7, T8, T9, R> extends Function {
|
||||
|
||||
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface FuncN<R> extends Function {
|
||||
|
||||
public R call(Object... args);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package rx.util.functions;
|
||||
|
||||
/**
|
||||
* All Func and Action interfaces extend from this.
|
||||
* <p>
|
||||
* Marker interface to allow isntanceof checks.
|
||||
*/
|
||||
public interface Function {
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
public interface FunctionLanguageAdaptor {
|
||||
|
||||
/**
|
||||
* Invoke the function and return the results.
|
||||
*
|
||||
* @param function
|
||||
* @param args
|
||||
*
|
||||
* @return Object results from function execution
|
||||
*/
|
||||
Object call(Object function, Object[] args);
|
||||
|
||||
/**
|
||||
* The Class of the Function that this adaptor serves.
|
||||
* <p>
|
||||
* Example: groovy.lang.Closure
|
||||
* <p>
|
||||
* This should not return classes of java.* packages.
|
||||
*
|
||||
* @return Class[] of classes that this adaptor should be invoked for.
|
||||
*/
|
||||
public Class<?>[] getFunctionClass();
|
||||
}
|
||||
@@ -1,503 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Netflix, Inc.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package rx.util.functions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.jackhuang.hellominecraft.utils.logging.logger.Logger;
|
||||
|
||||
/**
|
||||
* Allows execution of functions from multiple different languages.
|
||||
* <p>
|
||||
* Language support is provided via implementations of
|
||||
* {@link FunctionLanguageAdaptor}.
|
||||
* <p>
|
||||
* This class will dynamically look for known language adaptors on the classpath
|
||||
* at startup or new ones can be registered using
|
||||
* {@link #registerLanguageAdaptor(Class[], FunctionLanguageAdaptor)}.
|
||||
*/
|
||||
public class Functions {
|
||||
|
||||
private static final Logger logger = new Logger("Functions");
|
||||
|
||||
private final static ConcurrentHashMap<Class<?>, FunctionLanguageAdaptor> languageAdaptors = new ConcurrentHashMap<>();
|
||||
|
||||
private static boolean loadLanguageAdaptor(String name) {
|
||||
String className = "rx.lang." + name.toLowerCase() + "." + name + "Adaptor";
|
||||
try {
|
||||
Class<?> c = Class.forName(className);
|
||||
FunctionLanguageAdaptor a = (FunctionLanguageAdaptor) c.newInstance();
|
||||
registerLanguageAdaptor(a.getFunctionClass(), a);
|
||||
logger.info("Successfully loaded function language adaptor: " + name + " with path: " + className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.info("Could not find function language adaptor: " + name + " with path: " + className);
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed trying to initialize function language adaptor: " + className, e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void registerLanguageAdaptor(Class<?>[] functionClasses, FunctionLanguageAdaptor adaptor) {
|
||||
for (Class<?> functionClass : functionClasses) {
|
||||
if (functionClass.getPackage().getName().startsWith("java."))
|
||||
throw new IllegalArgumentException("FunctionLanguageAdaptor implementations can not specify java.lang.* classes.");
|
||||
languageAdaptors.put(functionClass, adaptor);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeLanguageAdaptor(Class<?> functionClass) {
|
||||
languageAdaptors.remove(functionClass);
|
||||
}
|
||||
|
||||
public static Collection<FunctionLanguageAdaptor> getRegisteredLanguageAdaptors() {
|
||||
return languageAdaptors.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for determining the type of closure/function and executing
|
||||
* it.
|
||||
*
|
||||
* @param function
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes" })
|
||||
public static FuncN from(final Object function) {
|
||||
if (function == null)
|
||||
throw new RuntimeException("function is null. Can't send arguments to null function.");
|
||||
|
||||
/*
|
||||
* check for typed Rx Function implementation first
|
||||
*/
|
||||
if (function instanceof Function)
|
||||
return fromFunction((Function) function);
|
||||
else
|
||||
/*
|
||||
* not an Rx Function so try language adaptors
|
||||
*/
|
||||
|
||||
// check for language adaptor
|
||||
for (final Class c : languageAdaptors.keySet())
|
||||
if (c.isInstance(function)) {
|
||||
final FunctionLanguageAdaptor la = languageAdaptors.get(c);
|
||||
// found the language adaptor so wrap in FuncN and return
|
||||
return new FuncN() {
|
||||
|
||||
@Override
|
||||
public Object call(Object... args) {
|
||||
return la.call(function, args);
|
||||
}
|
||||
|
||||
};
|
||||
} // no language adaptor found
|
||||
|
||||
// no support found
|
||||
throw new RuntimeException("Unsupported closure type: " + function.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
//
|
||||
// @SuppressWarnings("unchecked")
|
||||
// private static <R> R executionRxFunction(Function function, Object... args) {
|
||||
// // check Func* classes
|
||||
// if (function instanceof Func0) {
|
||||
// Func0<R> f = (Func0<R>) function;
|
||||
// if (args.length != 0) {
|
||||
// throw new RuntimeException("The closure was Func0 and expected no arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return (R) f.call();
|
||||
// } else if (function instanceof Func1) {
|
||||
// Func1<Object, R> f = (Func1<Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func1 and expected 1 argument, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0]);
|
||||
// } else if (function instanceof Func2) {
|
||||
// Func2<Object, Object, R> f = (Func2<Object, Object, R>) function;
|
||||
// if (args.length != 2) {
|
||||
// throw new RuntimeException("The closure was Func2 and expected 2 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1]);
|
||||
// } else if (function instanceof Func3) {
|
||||
// Func3<Object, Object, Object, R> f = (Func3<Object, Object, Object, R>) function;
|
||||
// if (args.length != 3) {
|
||||
// throw new RuntimeException("The closure was Func3 and expected 3 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return (R) f.call(args[0], args[1], args[2]);
|
||||
// } else if (function instanceof Func4) {
|
||||
// Func4<Object, Object, Object, Object, R> f = (Func4<Object, Object, Object, Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func4 and expected 4 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1], args[2], args[3]);
|
||||
// } else if (function instanceof Func5) {
|
||||
// Func5<Object, Object, Object, Object, Object, R> f = (Func5<Object, Object, Object, Object, Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func5 and expected 5 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1], args[2], args[3], args[4]);
|
||||
// } else if (function instanceof Func6) {
|
||||
// Func6<Object, Object, Object, Object, Object, Object, R> f = (Func6<Object, Object, Object, Object, Object, Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func6 and expected 6 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1], args[2], args[3], args[4], args[5]);
|
||||
// } else if (function instanceof Func7) {
|
||||
// Func7<Object, Object, Object, Object, Object, Object, Object, R> f = (Func7<Object, Object, Object, Object, Object, Object, Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func7 and expected 7 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
|
||||
// } else if (function instanceof Func8) {
|
||||
// Func8<Object, Object, Object, Object, Object, Object, Object, Object, R> f = (Func8<Object, Object, Object, Object, Object, Object, Object, Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func8 and expected 8 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
|
||||
// } else if (function instanceof Func9) {
|
||||
// Func9<Object, Object, Object, Object, Object, Object, Object, Object, Object, R> f = (Func9<Object, Object, Object, Object, Object, Object, Object, Object, Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func9 and expected 9 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
|
||||
// } else if (function instanceof FuncN) {
|
||||
// FuncN<R> f = (FuncN<R>) function;
|
||||
// return f.call(args);
|
||||
// } else if (function instanceof Action0) {
|
||||
// Action0 f = (Action0) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Action0 and expected 0 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// f.call();
|
||||
// return null;
|
||||
// } else if (function instanceof Action1) {
|
||||
// Action1<Object> f = (Action1<Object>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Action1 and expected 1 argument, but we received: " + args.length);
|
||||
// }
|
||||
// f.call(args[0]);
|
||||
// return null;
|
||||
// } else if (function instanceof Action2) {
|
||||
// Action2<Object, Object> f = (Action2<Object, Object>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Action2 and expected 2 argument, but we received: " + args.length);
|
||||
// }
|
||||
// f.call(args[0], args[1]);
|
||||
// return null;
|
||||
// } else if (function instanceof Action3) {
|
||||
// Action3<Object, Object, Object> f = (Action3<Object, Object, Object>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Action1 and expected 1 argument, but we received: " + args.length);
|
||||
// }
|
||||
// f.call(args[0], args[1], args[2]);
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// throw new RuntimeException("Unknown implementation of Function: " + function.getClass().getSimpleName());
|
||||
// }
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private static FuncN fromFunction(Function function) {
|
||||
// check Func* classes
|
||||
if (function instanceof Func0)
|
||||
return fromFunc((Func0) function);
|
||||
else if (function instanceof Func1)
|
||||
return fromFunc((Func1) function);
|
||||
else if (function instanceof Func2)
|
||||
return fromFunc((Func2) function);
|
||||
else if (function instanceof Func3)
|
||||
return fromFunc((Func3) function);
|
||||
else if (function instanceof Func4)
|
||||
return fromFunc((Func4) function);
|
||||
else if (function instanceof Func5)
|
||||
return fromFunc((Func5) function);
|
||||
else if (function instanceof Func6)
|
||||
return fromFunc((Func6) function);
|
||||
else if (function instanceof Func7)
|
||||
return fromFunc((Func7) function);
|
||||
else if (function instanceof Func8)
|
||||
return fromFunc((Func8) function);
|
||||
else if (function instanceof Func9)
|
||||
return fromFunc((Func9) function);
|
||||
else if (function instanceof FuncN)
|
||||
return (FuncN) function;
|
||||
else if (function instanceof Action0)
|
||||
return fromAction((Action0) function);
|
||||
else if (function instanceof Action1)
|
||||
return fromAction((Action1) function);
|
||||
else if (function instanceof Action2)
|
||||
return fromAction((Action2) function);
|
||||
else if (function instanceof Action3)
|
||||
return fromAction((Action3) function);
|
||||
|
||||
throw new RuntimeException("Unknown implementation of Function: " + function.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <R> FuncN<R> fromFunc(final Func0<R> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 0)
|
||||
throw new RuntimeException("Func0 expecting 0 arguments.");
|
||||
return f.call();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, R> FuncN<R> fromFunc(final Func1<T0, R> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 1)
|
||||
throw new RuntimeException("Func1 expecting 1 argument.");
|
||||
return f.call((T0) args[0]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, R> FuncN<R> fromFunc(final Func2<T0, T1, R> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 2)
|
||||
throw new RuntimeException("Func2 expecting 2 arguments.");
|
||||
return f.call((T0) args[0], (T1) args[1]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, R> FuncN<R> fromFunc(final Func3<T0, T1, T2, R> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 3)
|
||||
throw new RuntimeException("Func3 expecting 3 arguments.");
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, T3, R> FuncN<R> fromFunc(final Func4<T0, T1, T2, T3, R> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 4)
|
||||
throw new RuntimeException("Func4 expecting 4 arguments.");
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, T3, T4, R> FuncN<R> fromFunc(final Func5<T0, T1, T2, T3, T4, R> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 5)
|
||||
throw new RuntimeException("Func5 expecting 5 arguments.");
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, T3, T4, T5, R> FuncN<R> fromFunc(final Func6<T0, T1, T2, T3, T4, T5, R> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 6)
|
||||
throw new RuntimeException("Func6 expecting 6 arguments.");
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4], (T5) args[5]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, T3, T4, T5, T6, R> FuncN<R> fromFunc(final Func7<T0, T1, T2, T3, T4, T5, T6, R> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 7)
|
||||
throw new RuntimeException("Func7 expecting 7 arguments.");
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4], (T5) args[5], (T6) args[6]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, T3, T4, T5, T6, T7, R> FuncN<R> fromFunc(final Func8<T0, T1, T2, T3, T4, T5, T6, T7, R> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 8)
|
||||
throw new RuntimeException("Func8 expecting 8 arguments.");
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4], (T5) args[5], (T6) args[6], (T7) args[7]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, T3, T4, T5, T6, T7, T8, R> FuncN<R> fromFunc(final Func9<T0, T1, T2, T3, T4, T5, T6, T7, T8, R> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 9)
|
||||
throw new RuntimeException("Func9 expecting 9 arguments.");
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4], (T5) args[5], (T6) args[6], (T7) args[7], (T8) args[8]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static FuncN<Void> fromAction(final Action0 f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 0)
|
||||
throw new RuntimeException("Action0 expecting 0 arguments.");
|
||||
f.call();
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0> FuncN<Void> fromAction(final Action1<T0> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 1)
|
||||
throw new RuntimeException("Action1 expecting 1 argument.");
|
||||
f.call((T0) args[0]);
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1> FuncN<Void> fromAction(final Action2<T0, T1> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 2)
|
||||
throw new RuntimeException("Action3 expecting 2 arguments.");
|
||||
f.call((T0) args[0], (T1) args[1]);
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions
|
||||
* with different arities.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2> FuncN<Void> fromAction(final Action3<T0, T1, T2> f) {
|
||||
return (Object... args) -> {
|
||||
if (args.length != 3)
|
||||
throw new RuntimeException("Action3 expecting 3 arguments.");
|
||||
f.call((T0) args[0], (T1) args[1], (T2) args[2]);
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Func1<T, Boolean> alwaysTrue() {
|
||||
return (Func1<T, Boolean>) AlwaysTrue.INSTANCE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Func1<T, T> identity() {
|
||||
return (Func1<T, T>) Identity.INSTANCE;
|
||||
}
|
||||
|
||||
private enum AlwaysTrue implements Func1<Object, Boolean> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Boolean call(Object o) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private enum Identity implements Func1<Object, Object> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Object call(Object o) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user