anOperator) {
+ super.replaceAll(anOperator);
+
+ // Remove all instances of our default context URI
+ super.removeAll(List.of(PRESENTATION_CONTEXT_URI));
+
+ // Add back a single instance of our default context URI
+ if (!PRESENTATION_CONTEXT_URI.equals(get(0))) {
+ super.add(0, PRESENTATION_CONTEXT_URI);
+ }
+ }
+
+ /**
+ * Retains the contexts in the supplied collection and the default context (if it's also not included in the
+ * supplied collection).
+ *
+ * @param aCollection A collection of URIs to retain, removing the rest
+ * @return Whether the URIs were successfully retained
+ */
+ @Override
+ public boolean retainAll(final Collection> aCollection) {
+ final boolean result = super.retainAll(aCollection);
+
+ // If we haven't added retained the required context, we add it back
+ if (!PRESENTATION_CONTEXT_URI.equals(get(0))) {
+ super.add(0, PRESENTATION_CONTEXT_URI);
+ }
+
+ return result;
+ }
+
+ /**
+ * Sets the supplied context URI at the supplied index position. The default context can not be set with this
+ * method.
+ *
+ * @param aIndex An index position of the URI to set
+ * @param aURI A URI to set at the supplied index position
+ * @return The context URI that used to be at the supplied index position
+ */
+ @Override
+ public URI set(final int aIndex, final URI aURI) {
+ if (PRESENTATION_CONTEXT_URI.equals(aURI)) {
+ if (aIndex != 0) {
+ throw new IndexOutOfBoundsException(LOGGER.getMessage(MessageCodes.JPA_149));
+ } // else, just ignore -- this should be the current index of the default context
+ } else if (aIndex != 0) {
+ return super.set(aIndex, aURI);
+ }
+
+ throw new IndexOutOfBoundsException(LOGGER.getMessage(MessageCodes.JPA_151, aURI));
+ }
+
+ /**
+ * This method is not supported because {@code ContextList} has a prescribed sort order that can not be overridden.
+ *
+ * @param aComparator A comparator that could be used to sort the list
+ */
+ @Override
+ public void sort(final Comparator super URI> aComparator) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * A context list comparator that makes sure the required context is always last in the list.
+ *
+ * Cf. https://iiif.io/api/presentation/3.0/#46-linked-data-context-and-extensions
+ *
+ */
+ private static final class ContextListComparator implements Comparator {
+
+ @Override
+ public int compare(final URI aFirstURI, final URI aSecondURI) {
+ int result = 0;
+
+ if (!PRESENTATION_CONTEXT_URI.equals(aFirstURI) || !PRESENTATION_CONTEXT_URI.equals(aSecondURI)) {
+ if (PRESENTATION_CONTEXT_URI.equals(aFirstURI)) {
+ result = -1;
+ } else if (PRESENTATION_CONTEXT_URI.equals(aSecondURI)) {
+ result = 1;
+ }
+ }
+
+ return result;
+ }
+
+ }
+}
diff --git a/src/main/java/info/freelibrary/iiif/presentation/v3/Manifest.java b/src/main/java/info/freelibrary/iiif/presentation/v3/Manifest.java
index 2df3832e..d9a31167 100644
--- a/src/main/java/info/freelibrary/iiif/presentation/v3/Manifest.java
+++ b/src/main/java/info/freelibrary/iiif/presentation/v3/Manifest.java
@@ -1,7 +1,6 @@
package info.freelibrary.iiif.presentation.v3;
-import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -70,6 +69,7 @@ public class Manifest extends NavigableResource implements Resource getCanvases() {
return myCanvases;
}
- /**
- * Gets the primary manifest context.
- *
- * @return The manifest context
- */
- @Override
- @JsonIgnore
- public URI getContext() {
- return PRESENTATION_CONTEXT_URI;
- }
-
/**
* Gets the manifest's placeholder canvas.
*
diff --git a/src/main/java/info/freelibrary/iiif/presentation/v3/NavigableResource.java b/src/main/java/info/freelibrary/iiif/presentation/v3/NavigableResource.java
index 7ffdb952..93619c3e 100644
--- a/src/main/java/info/freelibrary/iiif/presentation/v3/NavigableResource.java
+++ b/src/main/java/info/freelibrary/iiif/presentation/v3/NavigableResource.java
@@ -1,24 +1,18 @@
package info.freelibrary.iiif.presentation.v3;
-import static info.freelibrary.util.Constants.SINGLE_INSTANCE;
-
import java.net.URI;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import info.freelibrary.util.Logger;
-import info.freelibrary.util.LoggerFactory;
import info.freelibrary.util.warnings.JDK;
import info.freelibrary.iiif.presentation.v3.exts.geo.NavPlace;
@@ -26,18 +20,19 @@
import info.freelibrary.iiif.presentation.v3.properties.Label;
import info.freelibrary.iiif.presentation.v3.properties.NavDate;
import info.freelibrary.iiif.presentation.v3.utils.JsonKeys;
-import info.freelibrary.iiif.presentation.v3.utils.MessageCodes;
+import info.freelibrary.iiif.presentation.v3.utils.json.ContextListDeserializer;
+import info.freelibrary.iiif.presentation.v3.utils.json.ContextListSerializer;
/**
* A navigable resource.
*/
class NavigableResource> extends AbstractResource {
- /** The navigable resource's logger. */
- private static final Logger LOGGER = LoggerFactory.getLogger(Manifest.class, MessageCodes.BUNDLE);
-
- /** The manifest's contexts. */
- private final List myContexts = Stream.of(PRESENTATION_CONTEXT_URI).collect(Collectors.toList());
+ /** The resource's contexts. */
+ @JsonProperty(JsonKeys.CONTEXT)
+ @JsonSerialize(using = ContextListSerializer.class)
+ @JsonDeserialize(using = ContextListDeserializer.class)
+ private ContextList myContexts;
/** The date of the navigable resource. */
private NavDate myNavDate;
@@ -79,41 +74,6 @@ protected NavigableResource(final String aType, final String aID, final Label aL
super(aType, aID, aLabel, aBehaviorClass);
}
- /**
- * Adds an array of new context URIs to the navigable resource.
- *
- * @param aContextArray Context URIs(s)
- * @return The navigable resource
- */
- @SuppressWarnings({ JDK.UNCHECKED })
- public T addContexts(final URI... aContextArray) {
- Objects.requireNonNull(aContextArray, MessageCodes.JPA_007);
-
- for (final URI uri : aContextArray) {
- Objects.requireNonNull(uri, MessageCodes.JPA_007);
-
- if (!PRESENTATION_CONTEXT_URI.equals(uri)) {
- myContexts.add(uri);
- }
- }
-
- Collections.sort(myContexts, new ContextListComparator<>());
- return (T) this;
- }
-
- /**
- * Clears all contexts, but the required one.
- *
- * @return The navigable resource
- */
- @SuppressWarnings({ JDK.UNCHECKED })
- public T clearContexts() {
- myContexts.clear();
- myContexts.add(PRESENTATION_CONTEXT_URI);
-
- return (T) this;
- }
-
@Override
@SuppressWarnings(JDK.UNCHECKED)
public boolean equals(final Object aObject) {
@@ -134,28 +94,13 @@ public boolean equals(final Object aObject) {
}
/**
- * Gets the primary context.
- *
- * @return The primary context
- */
- @JsonIgnore
- public URI getContext() {
- return PRESENTATION_CONTEXT_URI;
- }
-
- /**
- * Gets an unmodifiable list of contexts. To remove contexts, use {@link #removeContext(URI) removeContext} or
- * {@link #clearContexts() clearContexts}.
+ * Gets the resource's contexts.
*
- * @return The context
+ * @return The contexts
*/
@JsonIgnore
public List getContexts() {
- if (myContexts.isEmpty()) {
- return Collections.emptyList();
- }
-
- return Collections.unmodifiableList(myContexts);
+ return getContextList();
}
/**
@@ -184,26 +129,28 @@ public int hashCode() {
}
/**
- * Remove the supplied context. This will not remove the default required context though. If that's supplied, an
- * {@link UnsupportedOperationException} will be thrown.
+ * Sets the manifest's contexts from a list that Jackson builds.
*
- * @param aContextURI A context to be removed from the contexts list
- * @return True if the context was removed; else, false
- * @throws UnsupportedOperationException If the required context is supplied to be removed
+ * @param aContextList A list of contexts
+ * @return This resource
*/
- public boolean removeContext(final URI aContextURI) {
- if (PRESENTATION_CONTEXT_URI.equals(aContextURI)) {
- throw new UnsupportedOperationException(LOGGER.getMessage(MessageCodes.JPA_039, PRESENTATION_CONTEXT_URI));
+ @JsonIgnore
+ @SuppressWarnings({ JDK.UNCHECKED })
+ public T setContexts(final List aContextList) {
+ if (aContextList instanceof final ContextList contextList) {
+ myContexts = contextList;
+ } else {
+ myContexts = new ContextList(aContextList);
}
- return myContexts.remove(aContextURI);
+ return (T) this;
}
/**
* Sets a navigation date.
*
* @param aNavDate The navigation date
- * @return The navigable resource
+ * @return This resource
*/
@JsonSetter(JsonKeys.NAV_DATE)
@SuppressWarnings({ JDK.UNCHECKED })
@@ -216,7 +163,7 @@ public T setNavDate(final NavDate aNavDate) {
* Sets the navigation place.
*
* @param aNavPlace The navigation place
- * @return The navigable resource
+ * @return This resource
*/
@JsonSetter(JsonKeys.NAV_PLACE)
@SuppressWarnings({ JDK.UNCHECKED })
@@ -226,111 +173,16 @@ public T setNavPlace(final NavPlace aNavPlace) {
}
/**
- * Method used internally to set context from JSON.
- *
- * @param aObject A Jackson deserialization object
- */
- @JsonSetter(JsonKeys.CONTEXT)
- protected void deserializeContexts(final Object aObject) {
- if (aObject instanceof final String context) {
- deserializeContexts(List.of(context));
- } else if (aObject instanceof final List> genericList) {
- validateAndSetContexts(genericList);
- } else {
- throw new IllegalArgumentException(LOGGER.getMessage(MessageCodes.JPA_113));
- }
- }
-
- /**
- * Gets the manifest context. The manifest can either have a single context or an array of contexts (Cf.
- * https://iiif.io/api/presentation/3.0/#46-linked-data-context-and-extensions)
+ * Gets the resource's contexts.
*
- * @return The manifest context
- */
- @JsonGetter(JsonKeys.CONTEXT)
- protected Object getJsonContext() {
- if (myContexts.size() == SINGLE_INSTANCE) {
- return myContexts.get(0);
- }
-
- if (!myContexts.isEmpty()) {
- return myContexts;
- }
-
- return null;
- }
-
- /**
- * Sets the manifest's contexts from a list that Jackson builds.
- *
- * @param aContextList A list of contexts
+ * @return The contexts
*/
@JsonIgnore
- private void setContexts(final List> aContextList) {
- final List indices = new ArrayList<>();
- final List contextList = new ArrayList<>();
-
- for (int index = 0; index < aContextList.size(); index++) {
- final URI context = URI.create((String) aContextList.get(index));
-
- if (PRESENTATION_CONTEXT_URI.equals(context)) {
- indices.add(index); // We may have more than one required context in supplied list
-
- if (indices.size() == SINGLE_INSTANCE) { // Only keep one if this is the case
- contextList.add(context);
- }
- } else {
- contextList.add(context);
- }
- }
-
- // Remove required context; we'll add it back at the end
- if (!indices.isEmpty()) {
- contextList.remove((int) indices.get(0));
- }
-
- myContexts.clear();
- myContexts.addAll(contextList);
- myContexts.add(PRESENTATION_CONTEXT_URI); // Add required context at end
- }
-
- /**
- * Sets the contexts after confirming their validity.
- *
- * @param aContextList A list of contexts
- * @throws IllegalArgumentException if a supplied context isn't a string
- */
- @SuppressWarnings({ JDK.UNCHECKED })
- private void validateAndSetContexts(final List> aContextList) {
- if (aContextList.isEmpty() || aContextList.stream().anyMatch(item -> !(item instanceof String))) {
- throw new IllegalArgumentException(LOGGER.getMessage(MessageCodes.JPA_113));
- }
-
- setContexts(aContextList);
- }
-
- /**
- * A context list comparator that makes sure the required context is always last in the list.
- *
- * Cf. https://iiif.io/api/presentation/3.0/#46-linked-data-context-and-extensions
- *
- */
- static class ContextListComparator implements Comparator {
-
- @Override
- public int compare(final U aFirstURI, final U aSecondURI) {
- int result = 0;
-
- if (!PRESENTATION_CONTEXT_URI.equals(aFirstURI) || !PRESENTATION_CONTEXT_URI.equals(aSecondURI)) {
- if (PRESENTATION_CONTEXT_URI.equals(aFirstURI)) {
- result = 1;
- } else if (PRESENTATION_CONTEXT_URI.equals(aSecondURI)) {
- result = -1;
- }
- }
-
- return result;
+ protected final List getContextList() {
+ if (myContexts == null) {
+ myContexts = new ContextList();
}
+ return myContexts;
}
}
diff --git a/src/main/java/info/freelibrary/iiif/presentation/v3/PlaceholderCanvas.java b/src/main/java/info/freelibrary/iiif/presentation/v3/PlaceholderCanvas.java
index b8ff6065..ba6690a5 100644
--- a/src/main/java/info/freelibrary/iiif/presentation/v3/PlaceholderCanvas.java
+++ b/src/main/java/info/freelibrary/iiif/presentation/v3/PlaceholderCanvas.java
@@ -201,9 +201,4 @@ public final PlaceholderCanvas supplementWith(final String aCanvasRegion,
final List aContentList) {
return super.supplement(this, new MediaFragmentSelector(aCanvasRegion), false, aContentList);
}
-
- @Override
- protected Object getJsonContext() {
- return null;
- }
}
diff --git a/src/main/java/info/freelibrary/iiif/presentation/v3/Range.java b/src/main/java/info/freelibrary/iiif/presentation/v3/Range.java
index c6508c5e..1a647ffb 100644
--- a/src/main/java/info/freelibrary/iiif/presentation/v3/Range.java
+++ b/src/main/java/info/freelibrary/iiif/presentation/v3/Range.java
@@ -295,19 +295,6 @@ public Range setViewingDirection(final ViewingDirection aViewingDirection) {
return this;
}
- /**
- * Gets the manifest context. The manifest can either have a single context or an array of contexts (Cf.
- * https://iiif.io/api/presentation/3.0/#46-linked-data-context-and-extensions)
- *
- * @return The manifest context
- */
- @Override
- @JsonGetter(JsonKeys.CONTEXT)
- @JsonInclude(Include.NON_NULL)
- protected Object getJsonContext() {
- return null;
- }
-
/**
* A wrapper for the types of resources that can be put into a range's items: {@link Canvas}, {@link Range}, and
* {@link SpecificResource}.
diff --git a/src/main/java/info/freelibrary/iiif/presentation/v3/RangeCanvas.java b/src/main/java/info/freelibrary/iiif/presentation/v3/RangeCanvas.java
index a28416c7..98d791ca 100644
--- a/src/main/java/info/freelibrary/iiif/presentation/v3/RangeCanvas.java
+++ b/src/main/java/info/freelibrary/iiif/presentation/v3/RangeCanvas.java
@@ -74,16 +74,6 @@ class RangeCanvas extends Canvas {
mySourceCanvas = aCanvas;
}
- @Override
- public Canvas addContexts(final URI... aContextArray) {
- return mySourceCanvas.addContexts(aContextArray);
- }
-
- @Override
- public Canvas clearContexts() {
- return mySourceCanvas.clearContexts();
- }
-
@Override
public boolean equals(final Object aObject) {
return mySourceCanvas.equals(aObject);
@@ -99,11 +89,6 @@ public List getBehaviors() {
return mySourceCanvas.getBehaviors();
}
- @Override
- public URI getContext() {
- return mySourceCanvas.getContext();
- }
-
@Override
public List getContexts() {
return mySourceCanvas.getContexts();
@@ -149,11 +134,6 @@ public Optional getNavPlace() {
return mySourceCanvas.getNavPlace();
}
- @Override
- public List> getWebAnnotations() {
- return mySourceCanvas.getWebAnnotations();
- }
-
@Override
@JsonGetter(JsonKeys.ITEMS)
@JsonInclude(Include.NON_EMPTY) // This serialization method is why this wrapper class was created
@@ -221,6 +201,11 @@ public String getType() {
return mySourceCanvas.getType();
}
+ @Override
+ public List> getWebAnnotations() {
+ return mySourceCanvas.getWebAnnotations();
+ }
+
@Override
public int getWidth() {
return mySourceCanvas.getWidth();
@@ -231,11 +216,6 @@ public int hashCode() {
return mySourceCanvas.hashCode();
}
- @Override
- public boolean removeContext(final URI aContextURI) {
- return mySourceCanvas.removeContext(aContextURI);
- }
-
@Override
public Canvas setAccompanyingCanvas(final AccompanyingCanvas aCanvas) {
return mySourceCanvas.setAccompanyingCanvas(aCanvas);
@@ -381,11 +361,6 @@ protected float convertToFinitePositiveFloat(final Number aNumber) {
return mySourceCanvas.convertToFinitePositiveFloat(aNumber);
}
- @Override
- protected Object getJsonContext() {
- return mySourceCanvas.getJsonContext();
- }
-
@Override
boolean canFrame(final ContentResource aContent) {
return mySourceCanvas.canFrame(aContent);
diff --git a/src/main/java/info/freelibrary/iiif/presentation/v3/utils/json/ContextListDeserializer.java b/src/main/java/info/freelibrary/iiif/presentation/v3/utils/json/ContextListDeserializer.java
new file mode 100644
index 00000000..6409759b
--- /dev/null
+++ b/src/main/java/info/freelibrary/iiif/presentation/v3/utils/json/ContextListDeserializer.java
@@ -0,0 +1,66 @@
+
+package info.freelibrary.iiif.presentation.v3.utils.json;
+
+import static info.freelibrary.iiif.presentation.v3.ContextList.PRESENTATION_CONTEXT_URI;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+
+import com.fasterxml.jackson.core.JacksonException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+import info.freelibrary.iiif.presentation.v3.ContextList;
+
+/**
+ * A custom deserializer for ContextList. Deserialization is a little different because it either deserializes from a
+ * single string or a list of strings, depending on list size.
+ */
+public class ContextListDeserializer extends StdDeserializer> {
+
+ /** The {@code ContextListDeserializer}'s {@code serialVersionUID}. */
+ private static final long serialVersionUID = 3623670131870204191L;
+
+ /**
+ * Creates a new {@link ContextList} deserializer.
+ */
+ protected ContextListDeserializer() {
+ this(null);
+ }
+
+ /**
+ * Creates a new {@link ContextList} deserializer.
+ *
+ * @param aClass The class to deserialize
+ */
+ protected ContextListDeserializer(final Class> aClass) {
+ super(aClass);
+ }
+
+ @Override
+ public List deserialize(final JsonParser aParser, final DeserializationContext aContext)
+ throws IOException, JacksonException {
+ final JsonNode currentNode = aParser.getCodec().readTree(aParser);
+ final ContextList contexts = new ContextList();
+
+ if (currentNode.isArray()) {
+ currentNode.forEach(uri -> {
+ if (!PRESENTATION_CONTEXT_URI.toString().equals(uri)) {
+ contexts.add(URI.create(uri.textValue()));
+ }
+ });
+ } else {
+ final String uri = currentNode.textValue();
+
+ if (!PRESENTATION_CONTEXT_URI.toString().equals(uri)) {
+ contexts.add(URI.create(uri));
+ }
+ }
+
+ return contexts;
+ }
+
+}
diff --git a/src/main/java/info/freelibrary/iiif/presentation/v3/utils/json/ContextListSerializer.java b/src/main/java/info/freelibrary/iiif/presentation/v3/utils/json/ContextListSerializer.java
new file mode 100644
index 00000000..eb2c6252
--- /dev/null
+++ b/src/main/java/info/freelibrary/iiif/presentation/v3/utils/json/ContextListSerializer.java
@@ -0,0 +1,58 @@
+
+package info.freelibrary.iiif.presentation.v3.utils.json;
+
+import static info.freelibrary.util.Constants.SINGLE_INSTANCE;
+
+import java.io.IOException;
+import java.net.URI;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import info.freelibrary.util.ThrowingConsumer;
+
+import info.freelibrary.iiif.presentation.v3.ContextList;
+
+/**
+ * A custom serializer for ContextList. Serialization is a little different because it either serializes as a single
+ * string or a list of strings, depending on list size.
+ */
+public class ContextListSerializer extends StdSerializer {
+
+ /** The {@code ContextListSerializer}'s {@code serialVersionUID}. */
+ private static final long serialVersionUID = 7538668057735707536L;
+
+ /**
+ * Creates a new {@code ContextListSerializer}.
+ */
+ public ContextListSerializer() {
+ super(ContextList.class, true);
+ }
+
+ /**
+ * Creates a new {@code ContextListSerializer} from the supplied class.
+ *
+ * @param aClass The class to serialize
+ */
+ public ContextListSerializer(final Class aClass) {
+ super(aClass, true);
+ }
+
+ @Override
+ public void serialize(final ContextList aContextList, final JsonGenerator aJsonGenerator,
+ final SerializerProvider aProvider) throws IOException {
+ if (aContextList.size() == SINGLE_INSTANCE) {
+ aJsonGenerator.writeString(aContextList.get(0).toString());
+ } else {
+ aJsonGenerator.writeStartArray();
+
+ aContextList.stream().map(URI::toString).forEach((ThrowingConsumer) uri -> {
+ aJsonGenerator.writeString(uri);
+ });
+
+ aJsonGenerator.writeEndArray();
+ }
+ }
+
+}
diff --git a/src/main/resources/iiif_presentation_messages.xml b/src/main/resources/iiif_presentation_messages.xml
index 2c9d5f30..68fbc439 100644
--- a/src/main/resources/iiif_presentation_messages.xml
+++ b/src/main/resources/iiif_presentation_messages.xml
@@ -119,4 +119,8 @@
{} is not a valid navPlace type
A physical dimensions service must contain scale and units
Supplied Optional was unexpectedly empty
+ The IIIF Presentation context URI can only exist as the first context in the list
+ Context '{}' was added at list position 1 because 0 is reserved for the default context
+ The '{}' context cannot be set as the first context; this position is reserved for the default
+ IIIF Presentation context
diff --git a/src/test/java/info/freelibrary/iiif/presentation/v3/AbstractCanvasTest.java b/src/test/java/info/freelibrary/iiif/presentation/v3/AbstractCanvasTest.java
index b1eae6a9..06aa87f2 100644
--- a/src/test/java/info/freelibrary/iiif/presentation/v3/AbstractCanvasTest.java
+++ b/src/test/java/info/freelibrary/iiif/presentation/v3/AbstractCanvasTest.java
@@ -133,30 +133,6 @@ public final void testSetMinter() {
assertTrue(new TestClass(id).setMinter(minter).getMinter().isPresent());
}
- /**
- * Tests {@link AbstractCanvas#setWebAnnotations(AnnotationPage)}.
- */
- @Test
- public final void testSetWebAnnotations() {
- final Minter minter = MinterFactory.getMinter(HTTPS + UUID.randomUUID().toString());
- final AnnotationPage page = new AnnotationPage<>(minter, new Canvas(minter));
-
- page.addAnnotations(new BookmarkingAnnotation(minter));
- assertEquals(1, new TestClass(minter).setWebAnnotations(page).getWebAnnotations().size());
- }
-
- /**
- * Tests {@link AbstractCanvas#setWebAnnotations(AnnotationPage)}.
- */
- @Test
- public final void testSetWebAnnotationsList() {
- final Minter minter = MinterFactory.getMinter(HTTPS + UUID.randomUUID().toString());
- final AnnotationPage page = new AnnotationPage<>(minter, new Canvas(minter));
-
- page.addAnnotations(new BookmarkingAnnotation(minter));
- assertEquals(1, new TestClass(minter).setWebAnnotations(List.of(page)).getWebAnnotations().size());
- }
-
/**
* Tests {@link AbstractCanvas#setPaintingPages()}.
*/
@@ -204,6 +180,30 @@ public final void testSetSupplementingPagesList() {
.getSupplementingPages().size());
}
+ /**
+ * Tests {@link AbstractCanvas#setWebAnnotations(AnnotationPage)}.
+ */
+ @Test
+ public final void testSetWebAnnotations() {
+ final Minter minter = MinterFactory.getMinter(HTTPS + UUID.randomUUID().toString());
+ final AnnotationPage page = new AnnotationPage<>(minter, new Canvas(minter));
+
+ page.addAnnotations(new BookmarkingAnnotation(minter));
+ assertEquals(1, new TestClass(minter).setWebAnnotations(page).getWebAnnotations().size());
+ }
+
+ /**
+ * Tests {@link AbstractCanvas#setWebAnnotations(AnnotationPage)}.
+ */
+ @Test
+ public final void testSetWebAnnotationsList() {
+ final Minter minter = MinterFactory.getMinter(HTTPS + UUID.randomUUID().toString());
+ final AnnotationPage page = new AnnotationPage<>(minter, new Canvas(minter));
+
+ page.addAnnotations(new BookmarkingAnnotation(minter));
+ assertEquals(1, new TestClass(minter).setWebAnnotations(List.of(page)).getWebAnnotations().size());
+ }
+
/**
* A test class.
*/
@@ -226,11 +226,6 @@ private TestClass(final Minter aMinter) {
private TestClass(final String aID) {
super(aID);
}
-
- @Override
- protected Object getJsonContext() {
- return null;
- }
}
}
diff --git a/src/test/java/info/freelibrary/iiif/presentation/v3/CollectionTest.java b/src/test/java/info/freelibrary/iiif/presentation/v3/CollectionTest.java
index 5876dc8a..d05f3b27 100644
--- a/src/test/java/info/freelibrary/iiif/presentation/v3/CollectionTest.java
+++ b/src/test/java/info/freelibrary/iiif/presentation/v3/CollectionTest.java
@@ -216,14 +216,6 @@ public void testGetCollectionNotNull() {
assertEquals(0, new Collection(myID, myLabel).getItems().size());
}
- /**
- * Tests {@link Collection#getContext()}.
- */
- @Test
- public void testGetContext() {
- assertEquals(Collection.PRESENTATION_CONTEXT_URI, new Collection(myID, myLabel).getContext());
- }
-
/**
* Tests setting a navDate.
*/
diff --git a/src/test/java/info/freelibrary/iiif/presentation/v3/ContextListTest.java b/src/test/java/info/freelibrary/iiif/presentation/v3/ContextListTest.java
new file mode 100644
index 00000000..c8f06293
--- /dev/null
+++ b/src/test/java/info/freelibrary/iiif/presentation/v3/ContextListTest.java
@@ -0,0 +1,283 @@
+
+package info.freelibrary.iiif.presentation.v3;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+
+import org.junit.Test;
+
+/**
+ * Tests of {@link ContextList}.
+ */
+public class ContextListTest extends AbstractTest {
+
+ /** A list of test contexts. */
+ private final List myContexts = Arrays.asList(URI.create(getURL()), URI.create(getURL()), URI.create(getURL()),
+ URI.create(getURL()), URI.create(getURL()), URI.create(getURL()), URI.create(getURL()),
+ URI.create(getURL()), URI.create(getURL()), URI.create(getURL()), URI.create(getURL()),
+ ContextList.PRESENTATION_CONTEXT_URI);
+
+ /**
+ * Test method for {@link ContextList#addAll(Collection)}.
+ */
+ @Test
+ public final void testAddAllCollectionOfQextendsURI() {
+ final ContextList contexts = new ContextList();
+
+ assertEquals(1, contexts.size());
+ contexts.addAll(myContexts);
+ assertEquals(12, contexts.size());
+ }
+
+ /**
+ * Test method for {@link ContextList#addAll(int, Collection)}.
+ */
+ @Test
+ public final void testAddAllIntCollectionOfQextendsURI() {
+ final ContextList contexts = new ContextList(myContexts);
+ final URI newURI1 = URI.create(getURL());
+ final URI newURI2 = URI.create(getURL());
+ final URI oldURI = contexts.get(8);
+
+ assertEquals(12, contexts.size());
+ assertEquals(oldURI, contexts.get(8));
+ contexts.addAll(8, List.of(newURI1, newURI2));
+ assertEquals(14, contexts.size());
+ assertEquals(newURI1, contexts.get(8));
+ }
+
+ /**
+ * Test method for {@link ContextList#addFirst(URI)}.
+ */
+ @Test
+ public final void testAddFirstURI() {
+ final ContextList contexts = new ContextList();
+ final URI uri = URI.create(getURL());
+
+ assertEquals(1, contexts.size());
+ contexts.addFirst(uri);
+ assertEquals(2, contexts.size());
+ assertEquals(ContextList.PRESENTATION_CONTEXT_URI, contexts.get(0));
+ assertEquals(uri, contexts.get(1));
+ }
+
+ /**
+ * Test method for {@link ContextList#add(int, URI)}.
+ */
+ @Test
+ public final void testAddIntURI() {
+ final ContextList contexts = new ContextList();
+ final URI uri = URI.create(getURL());
+
+ assertEquals(1, contexts.size());
+ contexts.add(0, uri);
+ assertEquals(2, contexts.size());
+ assertEquals(uri, contexts.get(1));
+ assertEquals(ContextList.PRESENTATION_CONTEXT_URI, contexts.get(0));
+ }
+
+ /**
+ * Test method for {@link ContextList#addLast(URI)}.
+ */
+ @Test
+ public final void testAddLastURI() {
+ final ContextList contexts = new ContextList();
+ final URI uri = URI.create(getURL());
+
+ assertEquals(1, contexts.size());
+ contexts.addLast(uri);
+ assertEquals(2, contexts.size());
+ assertEquals(uri, contexts.get(1));
+ assertEquals(ContextList.PRESENTATION_CONTEXT_URI, contexts.get(0));
+ }
+
+ /**
+ * Test method for {@link ContextList#add(URI)}.
+ */
+ @Test
+ public final void testAddURI() {
+ final ContextList contexts = new ContextList();
+ final URI uri = URI.create(getURL());
+
+ assertEquals(1, contexts.size());
+ contexts.add(uri);
+ assertEquals(2, contexts.size());
+ assertEquals(uri, contexts.get(1));
+ assertEquals(ContextList.PRESENTATION_CONTEXT_URI, contexts.get(0));
+ }
+
+ /**
+ * Test method for {@link ContextList#clear()}.
+ */
+ @Test
+ public final void testClear() {
+ final ContextList contexts = new ContextList(myContexts);
+
+ assertEquals(12, contexts.size());
+ contexts.clear();
+ assertEquals(1, contexts.size());
+ }
+
+ /**
+ * Test method for {@link ContextList#ContextList()}.
+ */
+ @Test
+ public final void testContextList() {
+ final ContextList contexts = new ContextList();
+
+ assertEquals(1, contexts.size());
+ assertEquals(ContextList.PRESENTATION_CONTEXT_URI, contexts.get(0));
+ }
+
+ /**
+ * Test method for {@link ContextList#ContextList(List)}.
+ */
+ @Test
+ public final void testContextListListOfURI() {
+ final ContextList contexts = new ContextList(myContexts);
+
+ assertEquals(12, contexts.size());
+ assertEquals(ContextList.PRESENTATION_CONTEXT_URI, contexts.get(0));
+ }
+
+ /**
+ * Test method for {@link ContextList#equals(Object)}.
+ */
+ @Test
+ public final void testEqualsObject() {
+ final ContextList contexts1 = new ContextList(myContexts);
+ final ContextList contexts2 = new ContextList(myContexts);
+
+ assertEquals(contexts1, contexts2);
+ }
+
+ /**
+ * Test method for {@link ContextList#hashCode()}.
+ */
+ @Test
+ public final void testHashCode() {
+ final ContextList contexts1 = new ContextList(myContexts);
+ final ContextList contexts2 = new ContextList(myContexts);
+
+ assertEquals(contexts1.hashCode(), contexts2.hashCode());
+ }
+
+ /**
+ * Test method for {@link ContextList#removeAll(Collection)}.
+ */
+ @Test
+ public final void testRemoveAllCollectionOfQ() {
+ final ContextList contexts = new ContextList(myContexts);
+ final URI uri1 = contexts.get(5);
+ final URI uri2 = contexts.get(8);
+ final URI uri3 = contexts.get(11);
+ final List list = List.of(uri1, uri2, uri3);
+
+ assertEquals(12, contexts.size());
+ contexts.removeAll(list);
+ assertEquals(9, contexts.size());
+ }
+
+ /**
+ * Test method for {@link ContextList#removeIf(Predicate)}.
+ */
+ @Test
+ public final void testRemoveIfPredicateOfQsuperURI() {
+ // fail("Not yet implemented");
+ }
+
+ /**
+ * Test method for {@link ContextList#remove(int)}.
+ */
+ @Test
+ public final void testRemoveInt() {
+ final ContextList contexts = new ContextList(myContexts);
+ final URI uri = contexts.get(8);
+
+ assertEquals(12, contexts.size());
+ assertTrue(contexts.contains(uri));
+ contexts.remove(8);
+ assertEquals(11, contexts.size());
+ assertFalse(contexts.contains(uri));
+ }
+
+ /**
+ * Test method for {@link ContextList#removeLast()}.
+ */
+ @Test
+ public final void testRemoveLast() {
+ final ContextList contexts = new ContextList(myContexts);
+ final URI uri = contexts.get(contexts.size() - 1);
+
+ assertTrue(contexts.contains(uri));
+ contexts.removeLast();
+ assertFalse(contexts.contains(uri));
+ }
+
+ /**
+ * Test method for {@link ContextList#remove(Object)}.
+ */
+ @Test
+ public final void testRemoveObject() {
+ final ContextList contexts = new ContextList(myContexts);
+ final URI uri = contexts.get(8);
+
+ assertTrue(contexts.contains(uri));
+ contexts.remove(uri);
+ assertFalse(contexts.contains(uri));
+ }
+
+ /**
+ * Test method for {@link ContextList#replaceAll(UnaryOperator)}.
+ */
+ @Test
+ public final void testReplaceAllUnaryOperatorOfURI() {
+ // fail("Not yet implemented");
+ }
+
+ /**
+ * Test method for {@link ContextList#retainAll(Collection)}.
+ */
+ @Test
+ public final void testRetainAllCollectionOfQ() {
+ final ContextList contexts = new ContextList(myContexts);
+ final URI uri1 = contexts.get(5);
+ final URI uri2 = contexts.get(8);
+
+ assertEquals(12, contexts.size());
+ contexts.retainAll(List.of(uri1, uri2));
+ assertEquals(3, contexts.size());
+ }
+
+ /**
+ * Test method for {@link ContextList#set(int, URI)}.
+ */
+ @Test
+ public final void testSetIntURI() {
+ final ContextList contexts = new ContextList(myContexts);
+ final URI uri = URI.create(getURL());
+
+ assertNotEquals(uri, contexts.get(1));
+ contexts.set(1, uri);
+ assertEquals(uri, contexts.get(1));
+ }
+
+ /**
+ * Test method for {@link ContextList#sort(Comparator)}.
+ */
+ @Test(expected = UnsupportedOperationException.class)
+ public final void testSortComparatorOfQsuperURI() {
+ new ContextList(myContexts).sort((aUriOne, aUriTwo) -> 0);
+ }
+
+}
diff --git a/src/test/java/info/freelibrary/iiif/presentation/v3/ManifestTest.java b/src/test/java/info/freelibrary/iiif/presentation/v3/ManifestTest.java
index 6ea6bc34..8bc2ee40 100644
--- a/src/test/java/info/freelibrary/iiif/presentation/v3/ManifestTest.java
+++ b/src/test/java/info/freelibrary/iiif/presentation/v3/ManifestTest.java
@@ -10,10 +10,8 @@
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
-import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Stream;
@@ -86,12 +84,6 @@ public class ManifestTest extends AbstractTest {
/** A test width. */
private static final int WIDTH = 6132;
- /** A list of test contexts. */
- private final List myContexts = Arrays.asList(URI.create(getURL()), URI.create(getURL()), URI.create(getURL()),
- URI.create(getURL()), URI.create(getURL()), URI.create(getURL()), URI.create(getURL()),
- URI.create(getURL()), URI.create(getURL()), URI.create(getURL()), URI.create(getURL()),
- AbstractResource.PRESENTATION_CONTEXT_URI);
-
/** The test manifest. */
private Manifest myManifest;
@@ -203,53 +195,6 @@ public void testAddRangesList() {
assertEquals(1, myManifest.getRanges().size());
}
- /**
- * Tests adding a context URI.
- */
- @Test
- public void testAddUriContexts() {
- assertEquals(1, myManifest.getContexts().size());
- myManifest.addContexts(URI.create(myLoremIpsum.getUrl()), URI.create(myLoremIpsum.getUrl()));
- assertEquals(3, myManifest.getContexts().size());
- }
-
- /**
- * Tests clearing the contexts.
- */
- @Test
- public void testClearContexts() {
- assertEquals(1, myManifest.getContexts().size());
- myManifest.addContexts(URI.create(myLoremIpsum.getUrl()), URI.create(myLoremIpsum.getUrl()));
- assertEquals(3, myManifest.getContexts().size());
- assertEquals(1, myManifest.clearContexts().getContexts().size());
- }
-
- /**
- * Tests the comparator's sort.
- */
- @Test
- public final void testComparatorSort() {
- final int lastIndex = myContexts.size() - 1;
- final List preSort = new ArrayList<>();
-
- // Shuffle until our last list item isn't the required one
- while (AbstractResource.PRESENTATION_CONTEXT_URI.equals(myContexts.get(lastIndex))) {
- Collections.shuffle(myContexts);
- }
-
- // Remember the state of our list before the sort, minus the required Context
- assertTrue(preSort.addAll(myContexts));
- assertTrue(preSort.remove(AbstractResource.PRESENTATION_CONTEXT_URI));
-
- // Sort list items
- Collections.sort(myContexts, new NavigableResource.ContextListComparator<>());
-
- // Check that the last URI in the list is our required one and
- // that list has same pre-sort order minus the required context
- assertEquals(AbstractResource.PRESENTATION_CONTEXT_URI, myContexts.get(lastIndex));
- assertEquals(preSort, myContexts.subList(0, lastIndex));
- }
-
/**
* Tests the manifest constructor.
*/
@@ -261,15 +206,6 @@ public void testConstructorStringLabel() {
assertEquals(METADATA_PAIRS.get(0)[1], myManifest.getLabel().get().getString());
}
- /**
- * Tests {@link Manifest#getContext() getContext} method.
- */
- @Test
- public void testGetPrimaryContext() {
- assertEquals(AbstractResource.PRESENTATION_CONTEXT_URI,
- myManifest.addContexts(URI.create(myLoremIpsum.getUrl())).getContext());
- }
-
/**
* Tests {@link NavigableResource#equals(Object) NavigableResource}.
*/
@@ -355,27 +291,6 @@ public void testParsingManifest() throws IOException {
assertEquals(expected, format(found));
}
- /**
- * Tests {@link Manifest#removeContext(URI) removeContext} method.
- */
- @Test
- public void testRemoveContext() {
- final URI uri = URI.create("https://asdf.example.com");
-
- myManifest.addContexts(uri, URI.create("https://fdsa.example.com"));
- assertTrue(myManifest.getContexts().contains(uri));
- assertTrue(myManifest.removeContext(uri));
- assertEquals(2, myManifest.getContexts().size());
- }
-
- /**
- * Tests getting an exception on trying to remove the required context.
- */
- @Test(expected = UnsupportedOperationException.class)
- public void testRemovePrimaryContext() {
- myManifest.removeContext(AbstractResource.PRESENTATION_CONTEXT_URI);
- }
-
/**
* Tests setting annotations.
*/
From a7ba8ad57c6f0a177248cc103ab2931ede8996f9 Mon Sep 17 00:00:00 2001
From: "Kevin S. Clarke"
Date: Mon, 2 Sep 2024 17:17:55 -0400
Subject: [PATCH 3/8] WIP
---
.../iiif/presentation/v3/ContextList.java | 25 ++++++++++---------
.../utils/json/ContextListDeserializer.java | 18 ++++++-------
.../v3/utils/json/ContextListSerializer.java | 7 +-----
.../iiif/presentation/v3/ContextListTest.java | 15 +++++++++--
4 files changed, 36 insertions(+), 29 deletions(-)
diff --git a/src/main/java/info/freelibrary/iiif/presentation/v3/ContextList.java b/src/main/java/info/freelibrary/iiif/presentation/v3/ContextList.java
index 5506f7f4..e2a137f2 100644
--- a/src/main/java/info/freelibrary/iiif/presentation/v3/ContextList.java
+++ b/src/main/java/info/freelibrary/iiif/presentation/v3/ContextList.java
@@ -3,6 +3,7 @@
import static info.freelibrary.util.Constants.SINGLE_INSTANCE;
+import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
@@ -12,12 +13,11 @@
import java.util.NoSuchElementException;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import info.freelibrary.util.Logger;
import info.freelibrary.util.LoggerFactory;
import info.freelibrary.util.warnings.PMD;
+import info.freelibrary.util.warnings.Sonar;
import info.freelibrary.iiif.presentation.v3.utils.MessageCodes;
@@ -36,7 +36,7 @@ public class ContextList extends ArrayList implements List {
private static final long serialVersionUID = -877433424933939199L;
/** A simple sorter that only moves the required context to the first slot. */
- private final Comparator myComparator;
+ private final ContextListComparator myComparator;
/**
* Creates a new list of contexts. It includes the IIIF Presentation context by default.
@@ -136,7 +136,7 @@ public boolean addAll(final int aIndex, final Collection extends URI> aUriColl
*
* @param aURI A context URI to add at the beginning of the list
*/
- @SuppressWarnings(PMD.MISSING_OVERRIDE)
+ @SuppressWarnings({ PMD.MISSING_OVERRIDE, Sonar.OVERRIDE_REQUIRED })
public void addFirst(final URI aURI) {
if (!PRESENTATION_CONTEXT_URI.equals(aURI)) {
super.add(1, aURI);
@@ -149,7 +149,7 @@ public void addFirst(final URI aURI) {
*
* @param aURI A context URI to add at the end of the list
*/
- @SuppressWarnings(PMD.MISSING_OVERRIDE)
+ @SuppressWarnings({ PMD.MISSING_OVERRIDE, Sonar.OVERRIDE_REQUIRED })
public void addLast(final URI aURI) {
if (!PRESENTATION_CONTEXT_URI.equals(aURI)) {
super.add(aURI); // Adding, by default, adds as the last item
@@ -227,10 +227,8 @@ public boolean remove(final Object aObj) {
@Override
public boolean removeAll(final Collection> aCollection) {
if (aCollection.contains(PRESENTATION_CONTEXT_URI)) {
- final Stream> stream = aCollection.stream().filter(uri -> !PRESENTATION_CONTEXT_URI.equals(uri));
- final List> removables = stream.collect(Collectors.toList());
-
- return super.removeAll(removables);
+ final List> list = aCollection.stream().filter(uri -> !PRESENTATION_CONTEXT_URI.equals(uri)).toList();
+ return super.removeAll(list);
}
return super.removeAll(aCollection);
@@ -243,7 +241,7 @@ public boolean removeAll(final Collection> aCollection) {
* @param aFilter A filter to use to remove URIs from the list
* @return True if the URIs were removed
*/
- @SuppressWarnings(PMD.MISSING_OVERRIDE)
+ @SuppressWarnings({ PMD.MISSING_OVERRIDE, Sonar.OVERRIDE_REQUIRED })
public boolean removeIf(final Predicate super URI> aFilter) {
final boolean result = super.removeIf(aFilter);
@@ -262,7 +260,7 @@ public boolean removeIf(final Predicate super URI> aFilter) {
* @return The context URI that was removed
* @throws NoSuchElementException If the list only has the required IIIF Presentation context
*/
- @SuppressWarnings(PMD.MISSING_OVERRIDE)
+ @SuppressWarnings({ PMD.MISSING_OVERRIDE, Sonar.OVERRIDE_REQUIRED })
public URI removeLast() {
if (size() == SINGLE_INSTANCE) {
throw new NoSuchElementException(LOGGER.getMessage(MessageCodes.JPA_039, PRESENTATION_CONTEXT_URI));
@@ -345,7 +343,10 @@ public void sort(final Comparator super URI> aComparator) {
* Cf. https://iiif.io/api/presentation/3.0/#46-linked-data-context-and-extensions
*