diff --git a/src/main/java/org/springframework/hateoas/ResourceAssembler.java b/src/main/java/org/springframework/hateoas/ResourceAssembler.java index 132341d2..a5b50c39 100755 --- a/src/main/java/org/springframework/hateoas/ResourceAssembler.java +++ b/src/main/java/org/springframework/hateoas/ResourceAssembler.java @@ -15,10 +15,14 @@ */ package org.springframework.hateoas; +import java.util.ArrayList; +import java.util.List; + /** * Interface for components that convert a domain type into an {@link ResourceSupport}. * * @author Oliver Gierke + * @author Greg Turnquist */ public interface ResourceAssembler { @@ -29,4 +33,20 @@ public interface ResourceAssembler { * @return */ D toResource(T entity); + + /** + * Converts an {@link Iterable} or {@code T}s into an {@link Iterable} of {@link ResourceSupport} and wraps + * them in a {@link Resources} instance. + * + * @param entities + * @return + */ + default Resources toResources(List entities) { + + List resources = new ArrayList<>(); + for (T entity : entities) { + resources.add(toResource(entity)); + } + return new Resources<>(resources); + } } diff --git a/src/main/java/org/springframework/hateoas/core/Objects.java b/src/main/java/org/springframework/hateoas/core/Objects.java new file mode 100644 index 00000000..c0effa87 --- /dev/null +++ b/src/main/java/org/springframework/hateoas/core/Objects.java @@ -0,0 +1,35 @@ +/* + * Copyright 2018 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; + +/** + * Utilities to mimic {@link java.util.Objects} helper methods. + * + * @author Greg Turnquist + */ +public final class Objects { + + /** + * Variant of {@link java.util.Objects#requireNonNull(Object, String)} that throws an {@link IllegalArgumentException}. + */ + public static T requireNonNull(T obj, String message) { + + if (obj == null) { + throw new IllegalArgumentException(message); + } + return obj; + } +} diff --git a/src/main/java/org/springframework/hateoas/mvc/ResourceAssemblerSupport.java b/src/main/java/org/springframework/hateoas/mvc/ResourceAssemblerSupport.java index 2d51ab2c..126782d2 100755 --- a/src/main/java/org/springframework/hateoas/mvc/ResourceAssemblerSupport.java +++ b/src/main/java/org/springframework/hateoas/mvc/ResourceAssemblerSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2012-2018 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. @@ -23,13 +23,15 @@ import java.util.List; import org.springframework.beans.BeanUtils; import org.springframework.hateoas.ResourceAssembler; import org.springframework.hateoas.ResourceSupport; -import org.springframework.util.Assert; +import org.springframework.hateoas.Resources; +import org.springframework.hateoas.core.Objects; /** * Base class to implement {@link ResourceAssembler}s. Will automate {@link ResourceSupport} instance creation and make * sure a self-link is always added. * * @author Oliver Gierke + * @author Greg Turnquist */ public abstract class ResourceAssemblerSupport implements ResourceAssembler { @@ -44,30 +46,20 @@ public abstract class ResourceAssemblerSupport imp */ public ResourceAssemblerSupport(Class controllerClass, Class resourceType) { - Assert.notNull(controllerClass, "ControllerClass must not be null!"); - Assert.notNull(resourceType, "ResourceType must not be null!"); + Objects.requireNonNull(controllerClass, "ControllerClass must not be null!"); + Objects.requireNonNull(resourceType, "ResourceType must not be null!"); this.controllerClass = controllerClass; this.resourceType = resourceType; } - /** - * Converts all given entities into resources. - * - * @see #toResource(Object) - * @param entities must not be {@literal null}. - * @return - */ - public List toResources(Iterable entities) { + @Override + public Resources toResources(List entities) { + return this.map(entities).toResources(); + } - Assert.notNull(entities, "Entities must not be null!"); - List result = new ArrayList(); - - for (T entity : entities) { - result.add(toResource(entity)); - } - - return result; + public Builder map(Iterable entities) { + return new Builder<>(entities, this); } /** @@ -83,11 +75,11 @@ public abstract class ResourceAssemblerSupport imp protected D createResourceWithId(Object id, T entity, Object... parameters) { - Assert.notNull(entity, "Entity must not be null!"); - Assert.notNull(id, "Id must not be null!"); + Objects.requireNonNull(entity, "Entity must not be null!"); + Objects.requireNonNull(id, "Id must not be null!"); D instance = instantiateResource(entity); - instance.add(linkTo(controllerClass, parameters).slash(id).withSelfRel()); + instance.add(linkTo(this.controllerClass, parameters).slash(id).withSelfRel()); return instance; } @@ -100,6 +92,46 @@ public abstract class ResourceAssemblerSupport imp * @return */ protected D instantiateResource(T entity) { - return BeanUtils.instantiateClass(resourceType); + return BeanUtils.instantiateClass(this.resourceType); + } + + static class Builder { + + private final Iterable entities; + private final ResourceAssemblerSupport resourceAssembler; + + Builder(Iterable entities, ResourceAssemblerSupport resourceAssembler) { + + this.entities = Objects.requireNonNull(entities, "entities must not null!"); + this.resourceAssembler = resourceAssembler; + } + + /** + * Transform a list of {@code T}s into a list of {@link ResourceSupport}s. + * + * @see {@link #toListOfResources()} if you need this transformed list rendered as hypermedia + * + * @return + */ + public List toListOfResources() { + + List result = new ArrayList<>(); + + for (T entity : this.entities) { + result.add(this.resourceAssembler.toResource(entity)); + } + + return result; + } + + /** + * Converts all given entities into resources and wraps the result in a {@link Resources} instance. + * + * @see {@link #toListOfResources()}} and {@link ResourceAssembler#toResource(Object)} + * @return + */ + public Resources toResources() { + return new Resources<>(toListOfResources()); + } } } diff --git a/src/test/java/org/springframework/hateoas/mvc/IdentifiableResourceAssemblerSupportUnitTest.java b/src/test/java/org/springframework/hateoas/mvc/IdentifiableResourceAssemblerSupportUnitTest.java index dc1b1bb4..6466e5fd 100755 --- a/src/test/java/org/springframework/hateoas/mvc/IdentifiableResourceAssemblerSupportUnitTest.java +++ b/src/test/java/org/springframework/hateoas/mvc/IdentifiableResourceAssemblerSupportUnitTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 the original author or authors. + * Copyright 2012-2018 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. @@ -28,6 +28,7 @@ import org.springframework.hateoas.Identifiable; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkBuilder; import org.springframework.hateoas.ResourceSupport; +import org.springframework.hateoas.Resources; import org.springframework.hateoas.TestUtils; import org.springframework.web.bind.annotation.RequestMapping; @@ -35,6 +36,7 @@ import org.springframework.web.bind.annotation.RequestMapping; * Unit tests for {@link IdentifiableResourceAssemblerSupport}. * * @author Oliver Gierke + * @author Greg Turnquist */ public class IdentifiableResourceAssemblerSupportUnitTest extends TestUtils { @@ -81,6 +83,34 @@ public class IdentifiableResourceAssemblerSupportUnitTest extends TestUtils { .hasValueSatisfying(it -> assertThat(it.endsWith("/people/id"))); } + /** + * @see #416 + */ + @Test + public void convertsEntitiesToListOfResources() { + + Person first = new Person(); + first.id = 1L; + Person second = new Person(); + second.id = 2L; + + List result = assembler.map(Arrays.asList(first, second)).toListOfResources(); + + LinkBuilder builder = linkTo(PersonController.class); + + PersonResource firstResource = new PersonResource(); + firstResource.add(builder.slash(1L).withSelfRel()); + + PersonResource secondResource = new PersonResource(); + secondResource.add(builder.slash(1L).withSelfRel()); + + assertThat(result).hasSize(2); + assertThat(result).contains(firstResource, secondResource); + } + + /** + * @see #416 + */ @Test public void convertsEntitiesToResources() { @@ -89,7 +119,7 @@ public class IdentifiableResourceAssemblerSupportUnitTest extends TestUtils { Person second = new Person(); second.id = 2L; - List result = assembler.toResources(Arrays.asList(first, second)); + Resources result = assembler.map(Arrays.asList(first, second)).toResources(); LinkBuilder builder = linkTo(PersonController.class); @@ -130,11 +160,11 @@ public class IdentifiableResourceAssemblerSupportUnitTest extends TestUtils { class PersonResourceAssembler extends IdentifiableResourceAssemblerSupport { - public PersonResourceAssembler() { + PersonResourceAssembler() { this(PersonController.class); } - public PersonResourceAssembler(Class controllerType) { + PersonResourceAssembler(Class controllerType) { super(controllerType, PersonResource.class); }