#286 - EmbeddedWrappers now allows empty collections if a rel or type is provided.
EmbeddedWrappers.wrap(Object, String) now allows empty collections. Also a new emptyCollectionOfType(Class<?>) was introduced to allow the RelProvider kick in and dynamically resolve the lazily. Added more details to the JavaDoc of EmbeddedWrapper to make clear that we either expect a static rel returned or a type to resolve the rel from. Make HalEmbeddedBuilder detect invalid implementations and reject them although that should never occur if users use EmbeddedWrappers factory.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,14 +26,16 @@ public interface EmbeddedWrapper {
|
||||
|
||||
/**
|
||||
* Returns the rel to be used when embedding. If this returns {@literal null}, the rel will be calculated based on the
|
||||
* type of the embedded value.
|
||||
* type returned by {@link #getRelTargetType()}. A wrapper returning {@literal null} for both {@link #getRel()} and
|
||||
* {@link #getRelTargetType()} is considered invalid.
|
||||
*
|
||||
* @return
|
||||
* @see #getRelTargetType()
|
||||
*/
|
||||
String getRel();
|
||||
|
||||
/**
|
||||
* Returns whether the wrapper hast the given rel.
|
||||
* Returns whether the wrapper has the given rel.
|
||||
*
|
||||
* @param rel can be {@literal null}.
|
||||
* @return
|
||||
@@ -55,7 +57,9 @@ public interface EmbeddedWrapper {
|
||||
Object getValue();
|
||||
|
||||
/**
|
||||
* Returns the type to be used to calculate a type based rel.
|
||||
* Returns the type to be used to calculate a type based rel. Can return {@literal null} in case an explicit rel is
|
||||
* returned in {@link #getRel()}. A wrapper returning {@literal null} for both {@link #getRel()} and
|
||||
* {@link #getRelTargetType()} is considered invalid.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,6 +50,16 @@ public class EmbeddedWrappers {
|
||||
return wrap(source, AbstractEmbeddedWrapper.NO_REL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link EmbeddedWrapper} for an empty {@link Collection} with the given element type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public EmbeddedWrapper emptyCollectionOf(Class<?> type) {
|
||||
return new EmptyCollectionEmbeddedWrapper(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link EmbeddedWrapper} with the given rel.
|
||||
*
|
||||
@@ -123,6 +133,11 @@ public class EmbeddedWrappers {
|
||||
public Class<?> getRelTargetType() {
|
||||
|
||||
Object peek = peek();
|
||||
|
||||
if (peek == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
peek = peek instanceof Resource ? ((Resource<Object>) peek).getContent() : peek;
|
||||
|
||||
return AopUtils.getTargetClass(peek);
|
||||
@@ -204,7 +219,10 @@ public class EmbeddedWrappers {
|
||||
super(rel);
|
||||
|
||||
Assert.notNull(value, "Collection must not be null!");
|
||||
Assert.notEmpty(value, "Collection must not be empty");
|
||||
|
||||
if (AbstractEmbeddedWrapper.NO_REL.equals(rel) && value.isEmpty()) {
|
||||
throw new IllegalArgumentException("Cannot wrap an empty collection with no rel given!");
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
@@ -224,7 +242,7 @@ public class EmbeddedWrappers {
|
||||
*/
|
||||
@Override
|
||||
protected Object peek() {
|
||||
return value.iterator().next();
|
||||
return value.isEmpty() ? null : value.iterator().next();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -236,4 +254,71 @@ public class EmbeddedWrappers {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link EmbeddedWrapper} to simulate a {@link Collection} of a given element type.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 0.17
|
||||
*/
|
||||
private static class EmptyCollectionEmbeddedWrapper implements EmbeddedWrapper {
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
/**
|
||||
* Creates a new {@link EmptyCollectionEmbeddedWrapper}.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
*/
|
||||
public EmptyCollectionEmbeddedWrapper(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Element type must not be null!");
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.EmbeddedWrapper#getRel()
|
||||
*/
|
||||
@Override
|
||||
public String getRel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.EmbeddedWrapper#getValue()
|
||||
*/
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.EmbeddedWrapper#getRelTargetType()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getRelTargetType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.EmbeddedWrapper#isCollectionValue()
|
||||
*/
|
||||
@Override
|
||||
public boolean isCollectionValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.core.EmbeddedWrapper#hasRel(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public boolean hasRel(String rel) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,6 +39,7 @@ import org.springframework.util.StringUtils;
|
||||
class HalEmbeddedBuilder {
|
||||
|
||||
private static final String DEFAULT_REL = "content";
|
||||
private static final String INVALID_EMBEDDED_WRAPPER = "Embedded wrapper %s returned null for both the static rel and the rel target type! Make sure one of the two returns a non-null value!";
|
||||
|
||||
private final Map<String, Object> embeddeds = new HashMap<String, Object>();
|
||||
private final RelProvider provider;
|
||||
@@ -117,6 +118,10 @@ class HalEmbeddedBuilder {
|
||||
|
||||
Class<?> type = wrapper.getRelTargetType();
|
||||
|
||||
if (type == null) {
|
||||
throw new IllegalStateException(String.format(INVALID_EMBEDDED_WRAPPER, wrapper));
|
||||
}
|
||||
|
||||
String rel = forCollection ? provider.getCollectionResourceRelFor(type) : provider.getItemResourceRelFor(type);
|
||||
|
||||
if (curieProvider != null) {
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.hateoas.core;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link EmbeddedWrappers}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class EmbeddedWrappersUnitTests {
|
||||
|
||||
EmbeddedWrappers wrappers = new EmbeddedWrappers(false);
|
||||
|
||||
/**
|
||||
* @see #286
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void createsWrapperForEmptyCollection() {
|
||||
|
||||
EmbeddedWrapper wrapper = wrappers.emptyCollectionOf(String.class);
|
||||
|
||||
assertEmptyCollectionValue(wrapper);
|
||||
assertThat(wrapper.getRel(), is(nullValue()));
|
||||
assertThat(wrapper.getRelTargetType(), is(equalTo((Class) String.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #286
|
||||
*/
|
||||
@Test
|
||||
public void createsWrapperForEmptyCollectionAndExplicitRel() {
|
||||
|
||||
EmbeddedWrapper wrapper = wrappers.wrap(Collections.emptySet(), "rel");
|
||||
|
||||
assertEmptyCollectionValue(wrapper);
|
||||
assertThat(wrapper.getRel(), is("rel"));
|
||||
assertThat(wrapper.getRelTargetType(), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #286
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsEmptyCollectionWithoutExplicitRel() {
|
||||
wrappers.wrap(Collections.emptySet());
|
||||
}
|
||||
|
||||
private static void assertEmptyCollectionValue(EmbeddedWrapper wrapper) {
|
||||
|
||||
assertThat(wrapper.getValue(), is(instanceOf(Collection.class)));
|
||||
assertThat((Collection<?>) wrapper.getValue(), is(empty()));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@ package org.springframework.hateoas.hal;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -26,6 +27,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.hateoas.UriTemplate;
|
||||
import org.springframework.hateoas.core.EmbeddedWrapper;
|
||||
import org.springframework.hateoas.core.EmbeddedWrappers;
|
||||
import org.springframework.hateoas.core.EvoInflectorRelProvider;
|
||||
|
||||
@@ -164,6 +166,14 @@ public class HalEmbeddedBuilderUnitTest {
|
||||
assertHasValues(builder.asMap(), "curie:strings", "Sample");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #286
|
||||
*/
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void rejectsInvalidEmbeddedWrapper() {
|
||||
new HalEmbeddedBuilder(provider, curieProvider, false).add(mock(EmbeddedWrapper.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void assertHasValues(Map<String, Object> source, String rel, Object... values) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user