+ /**
+ * Unregisters the USK watcher for the given URI.
+ *
+ * @param uri
+ * The URI to unregister the USK watcher for
+ */
+ public void unregisterUsk(FreenetURI uri) {
+ USKCallback uskCallback = uriUskCallbacks.remove(uri);
+ if (uskCallback == null) {
+ logger.log(Level.INFO, String.format("Could not unregister unknown USK: %s", uri));
+ return;
+ }
+ try {
+ node.clientCore.uskManager.unsubscribe(USK.create(uri), uskCallback);
+ } catch (MalformedURLException mue1) {
+ logger.log(Level.INFO, String.format("Could not unregister invalid USK: %s", uri), mue1);
+ }
+ }
+
+ /**
+ * Container for a fetched URI and the {@link FetchResult}.
+ *
+ * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
+ */
+ public static class Fetched {
+
+ /** The fetched URI. */
+ private final FreenetURI freenetUri;
+
+ /** The fetch result. */
+ private final FetchResult fetchResult;
+
+ /**
+ * Creates a new fetched URI.
+ *
+ * @param freenetUri
+ * The URI that was fetched
+ * @param fetchResult
+ * The fetch result
+ */
+ public Fetched(FreenetURI freenetUri, FetchResult fetchResult) {
+ this.freenetUri = freenetUri;
+ this.fetchResult = fetchResult;
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Returns the fetched URI.
+ *
+ * @return The fetched URI
+ */
+ public FreenetURI getFreenetUri() {
+ return freenetUri;
+ }
+
+ /**
+ * Returns the fetch result.
+ *
+ * @return The fetch result
+ */
+ public FetchResult getFetchResult() {
+ return fetchResult;
+ }
+
+ }
+
+ /**
+ * Callback for USK watcher events.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ public static interface Callback {
+
+ /**
+ * Notifies a listener that a new edition was found for a URI.
+ *
+ * @param uri
+ * The URI that a new edition was found for
+ * @param edition
+ * The found edition
+ * @param newKnownGood
+ * Whether the found edition was actually fetched
+ * @param newSlot
+ * Whether the found edition is higher than all previously
+ * found editions
+ */
+ public void editionFound(FreenetURI uri, long edition, boolean newKnownGood, boolean newSlot);
+
+ }
+
+ /**
+ * Insert token that can be used to add {@link ImageInsertListener}s and
+ * cancel a running insert.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ public class InsertToken implements ClientPutCallback {
+
+ /** The image being inserted. */
+ private final Image image;
+
+ /** The list of registered image insert listeners. */
+ private final List<ImageInsertListener> imageInsertListeners = Collections.synchronizedList(new ArrayList<ImageInsertListener>());
+
+ /** The client putter. */
+ private ClientPutter clientPutter;
+
+ /** The final URI. */
+ private volatile FreenetURI resultingUri;
+
+ /**
+ * Creates a new insert token for the given image.
+ *
+ * @param image
+ * The image being inserted
+ */
+ public InsertToken(Image image) {
+ this.image = image;
+ }
+
+ //
+ // LISTENER MANAGEMENT
+ //
+
+ /**
+ * Adds the given listener to the list of registered listener.
+ *
+ * @param imageInsertListener
+ * The listener to add
+ */
+ public void addImageInsertListener(ImageInsertListener imageInsertListener) {
+ imageInsertListeners.add(imageInsertListener);
+ }
+
+ /**
+ * Removes the given listener from the list of registered listener.
+ *
+ * @param imageInsertListener
+ * The listener to remove
+ */
+ public void removeImageInsertListener(ImageInsertListener imageInsertListener) {
+ imageInsertListeners.remove(imageInsertListener);
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Sets the client putter that is inserting the image. This will also
+ * signal all registered listeners that the image has started.
+ *
+ * @see ImageInsertListener#imageInsertStarted(Image)
+ * @param clientPutter
+ * The client putter
+ */
+ public void setClientPutter(ClientPutter clientPutter) {
+ this.clientPutter = clientPutter;
+ for (ImageInsertListener imageInsertListener : imageInsertListeners) {
+ imageInsertListener.imageInsertStarted(image);
+ }
+ }
+
+ //
+ // ACTIONS
+ //
+
+ /**
+ * Cancels the running insert.
+ *
+ * @see ImageInsertListener#imageInsertAborted(Image)
+ */
+ @SuppressWarnings("synthetic-access")
+ public void cancel() {
+ clientPutter.cancel(null, node.clientCore.clientContext);
+ for (ImageInsertListener imageInsertListener : imageInsertListeners) {
+ imageInsertListener.imageInsertAborted(image);
+ }
+ }
+
+ //
+ // INTERFACE ClientPutCallback
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onMajorProgress(ObjectContainer objectContainer) {
+ /* ignore, we don’t care. */
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onFailure(InsertException insertException, BaseClientPutter clientPutter, ObjectContainer objectContainer) {
+ for (ImageInsertListener imageInsertListener : imageInsertListeners) {
+ if ((insertException != null) && ("Cancelled by user".equals(insertException.getMessage()))) {
+ imageInsertListener.imageInsertAborted(image);
+ } else {
+ imageInsertListener.imageInsertFailed(image, insertException);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onFetchable(BaseClientPutter clientPutter, ObjectContainer objectContainer) {
+ /* ignore, we don’t care. */
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onGeneratedMetadata(Bucket metadata, BaseClientPutter clientPutter, ObjectContainer objectContainer) {
+ /* ignore, we don’t care. */
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onGeneratedURI(FreenetURI generatedUri, BaseClientPutter clientPutter, ObjectContainer objectContainer) {
+ resultingUri = generatedUri;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onSuccess(BaseClientPutter clientPutter, ObjectContainer objectContainer) {
+ for (ImageInsertListener imageInsertListener : imageInsertListeners) {
+ imageInsertListener.imageInsertFinished(image, resultingUri);
+ }
+ }
+
+ }
+