failures = new ArrayList<>();
+ for (URI uri : uris) {
+ try {
+ if (unindex(uri)) {
+ // unindexed successfully
+ if (progressCallback != null) {
+ progressCallback.accept(Collections.singleton(uri));
+ }
+ } else {
+ // failed to unindex, reason unknown
+ failures.add(new FailedUnindex(Collections.singleton(uri), null));
+ }
+ } catch (Exception ex) {
+ failures.add(new FailedUnindex(Collections.singleton(uri), ex));
+ }
+ }
+ return UnindexReport.withFailures(failures);
+ });
+ }
}
diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/StorageInterface.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/StorageInterface.java
index d574c14b4..36b9c0735 100644
--- a/sdk/src/main/java/pt/ua/dicoogle/sdk/StorageInterface.java
+++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/StorageInterface.java
@@ -47,10 +47,12 @@ public interface StorageInterface extends DicooglePlugin {
/**
* Checks whether the file in the given path can be handled by this storage plugin.
*
+ * The default implementation checks that the given location URI
+ * has the exact same scheme as the scheme returned by {@link #getScheme()}.
+ *
* @param location a URI containing a scheme to be verified
* @return true if this storage plugin is in charge of URIs in the given form
*/
- @Deprecated
public default boolean handles(URI location) {
return Objects.equals(this.getScheme(), location.getScheme());
}
@@ -61,19 +63,57 @@ public default boolean handles(URI location) {
* This method is particularly nice for use in for-each loops.
*
* The provided scheme is not relevant at this point, but the developer must avoid calling this method
- * with a path of a different schema.
- *
- * for(StorageInputStream dicomObj : storagePlugin.at("file://dataset/")){
- * System.err.println(dicomObj.getURI());
- * }
+ * with a path of a different scheme.
+ *
+ *
+ * URI uri = URI.create("file://dataset/");
+ * for (StorageInputStream dicomObj: storagePlugin.at(uri)) {
+ * System.err.println(dicomObj.getURI());
+ * }
+ *
*
* @param location the location to read
- * @param parameters a variable list of extra parameters for the retrieve
+ * @param parameters a variable list of extra retrieval parameters
* @return an iterable of storage input streams
* @see StorageInputStream
*/
public Iterable at(URI location, Object... parameters);
+ /**
+ * Obtains an item stored at the exact location specified.
+ *
+ * The provided scheme is not relevant at this point,
+ * but the developer must avoid calling this method
+ * with a path of a different scheme.
+ *
+ *
+ * URI uri = URI.create("file://dataset/CT/001.dcm");
+ * StorageInputStream item = storagePlugin.get(uri);
+ * if (item != null) {
+ * System.err.println("Item at " + dicomObj.getURI() + " is available");
+ * }
+ *
+ *
+ * The default implementation calls {@linkplain #at}
+ * and returns the first item if its URI matches the location requested.
+ * Implementations may wish to override this method for performance reasons.
+ *
+ * @param location the URI of the item to retrieve
+ * @param parameters a variable list of extra retrieval parameters
+ * @return a storage item if it was found, null
otherwise
+ * @see StorageInputStream
+ */
+ default public StorageInputStream get(URI location, Object... parameters) {
+ for (StorageInputStream sis : this.at(location, parameters)) {
+ if (Objects.equals(sis.getURI(), location)) {
+ return sis;
+ }
+ // don't try any further
+ break;
+ }
+ return null;
+ }
+
/**
* Stores a DICOM object into the storage.
*
@@ -104,6 +144,9 @@ public default boolean handles(URI location) {
* can yield intermediate URIs representing other directories rather than
* objects.
*
+ * Directories can be distinguished from regular files
+ * by the presence of a trailing forward slash in the URI.
+ *
* The provided scheme is not relevant at this point, but the developer
* must avoid calling this method with a path of a different scheme.
*
diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/UnindexReport.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/UnindexReport.java
new file mode 100644
index 000000000..34ebcc952
--- /dev/null
+++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/UnindexReport.java
@@ -0,0 +1,159 @@
+/**
+ * Copyright (C) 2014 Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/
+ *
+ * This file is part of Dicoogle/dicoogle-sdk.
+ *
+ * Dicoogle/dicoogle-sdk 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.
+ *
+ * Dicoogle/dicoogle-sdk 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 Dicoogle. If not, see .
+ */
+package pt.ua.dicoogle.sdk.datastructs;
+
+import java.io.Serializable;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+/** Describes a report for a bulk unindexing operation.
+ */
+public final class UnindexReport implements Serializable {
+
+ /** The description of an indexing error.
+ *
+ * Whether the file remains indexed or not
+ * when an error of this kind occurs
+ * is not specified.
+ */
+ public static final class FailedUnindex implements Serializable {
+ /** The URIs to the items which failed to unindex. */
+ public final Collection urisAffected;
+
+ /** The exception describing the error which led to the failure.
+ * This field can be null
+ * when no cause is specified.
+ */
+ public final Exception cause;
+
+ /** Creates a failed unindex description
+ * due to the file not being found in the database.
+ *
+ * @param uri the URI of the file which could not be unindexed
+ * @param cause the underlying exception, if any
+ */
+ public FailedUnindex(Collection urisAffected, Exception cause) {
+ Objects.requireNonNull(urisAffected);
+ this.urisAffected = urisAffected;
+ this.cause = cause;
+ }
+
+ @Override
+ public String toString() {
+ return "FailedUnindex{urisAffected=" + urisAffected + ", cause=" + cause + "}";
+ }
+ }
+
+ /** URIs of files which were not found. */
+ private final Collection notFound;
+ private final Collection failures;
+
+ /** Creates a full report for a bulk unindexing operation.
+ * All parameters are nullable,
+ * in which case is equivalent to passing an empty collection.
+ * Once created, the report is final and immutable.
+ *
+ * @param notFound the URIs of files which were not found
+ * @param failures the error reports of files which could not be unindexed
+ */
+ public UnindexReport(Collection notFound, Collection failures) {
+ if (notFound == null) {
+ notFound = Collections.emptyList();
+ }
+ if (failures == null) {
+ failures = Collections.emptyList();
+ }
+ this.notFound = notFound;
+ this.failures = failures;
+ }
+
+ /** Creates a report with no unindexing failures.
+ */
+ public static UnindexReport ok() {
+ return new UnindexReport(null, null);
+ }
+
+ /** Creates a report with the given failures.
+ */
+ public static UnindexReport withFailures(Collection failures) {
+ return new UnindexReport(null, failures);
+ }
+
+ /** Returns whether all files were successfully unindexed from the database
+ * as requested.
+ */
+ public boolean isOk() {
+ return notFound.isEmpty() && failures.isEmpty();
+ }
+
+ /** Returns whether all files are no longer unindexed,
+ * meaning that no errors occurred when trying to unindex an indexed file.
+ *
+ * This is different from {@link #isOk()} in that
+ * it does not imply that all files to unindex were found in the database.
+ *
+ * @return true if no unindex failures are reported other than files not found
+ */
+ public boolean allUnindexed() {
+ return failures.isEmpty();
+ }
+
+ /** Obtains an immutable collection to
+ * the file batches which failed to unindex due to errors.
+ */
+ public Collection getUnindexFailures() {
+ return Collections.unmodifiableCollection(this.failures);
+ }
+
+ /** Obtains an immutable collection to the files
+ * which were not found in the index.
+ */
+ public Collection getNotFound() {
+ return Collections.unmodifiableCollection(this.notFound);
+ }
+
+ /** Returns the total count of failures reported during unindexing.
+ *
+ * Note that this does not necessarily correspond to
+ * the number of files affected,
+ * and does not include files which were not found.
+ */
+ public long failureCount() {
+ return this.failures.size();
+ }
+
+ /** Returns the total count of files which were not unindexed,
+ * whether because they were not found
+ * or could not be unindexed for other reasons.
+ */
+ public long notUnindexedFileCount() {
+ return this.notFound.size() + failedFileCount();
+ }
+
+ /** Returns the total count of files which failed to unindexed
+ * for reasons other than the files not being found.
+ */
+ public long failedFileCount() {
+ return this.failures.stream()
+ .mapToLong(f -> f.urisAffected.size())
+ .sum();
+ }
+}
diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/DIMGeneric.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/DIMGeneric.java
index 5544f82f0..5462d572a 100755
--- a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/DIMGeneric.java
+++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/DIMGeneric.java
@@ -158,8 +158,7 @@ private void fillDim(Map extra, URI uri) {
* Get data to Series
*/
String serieUID = toTrimmedString(extra.get("SeriesInstanceUID"), false);
- String BodyPartThickness = (String) extra.get("BodyPartThickness");
- // System.out.println("serieUID"+serieUID);
+ String BodyPartThickness = toTrimmedString(extra.get("BodyPartThickness"), true);
String serieNumber = toTrimmedString(extra.get("SeriesNumber"), true);
String serieDescription = toTrimmedString(extra.get("SeriesDescription"), false);
String modality = toTrimmedString(extra.get("Modality"), false);
@@ -192,8 +191,6 @@ private void fillDim(Map extra, URI uri) {
/**
* Get data to Image
*/
- // TODO:Error checking here... but according to standard, all images
- // must have one of these...
String sopInstUID = toTrimmedString(extra.get("SOPInstanceUID"), true);
if (sopInstUID == null) {