#195 - HalEmbeddedBuilder now considers RelAware on objects added.

This change introduces a RelAware interface for objects to implement which want to enforce a particular rel to be used in the _embedded clause. This is useful if the objects being added are not categorized by type but rather by their role in the context of the main object.
This commit is contained in:
Oliver Gierke
2014-06-10 10:46:46 +02:00
parent 673f82d388
commit 20bfa86c5f
4 changed files with 99 additions and 12 deletions

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2014 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;
/**
* Interface to mark objects that are aware of the rel they'd like to be exposed under.
*
* @author Oliver Gierke
*/
public interface RelAware {
/**
* Returns the rel to be used with the given object.
*
* @return
*/
String getRel();
}

View File

@@ -22,9 +22,11 @@ import java.util.List;
import java.util.Map;
import org.springframework.aop.support.AopUtils;
import org.springframework.hateoas.RelAware;
import org.springframework.hateoas.RelProvider;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.core.ObjectUtils;
import org.springframework.util.Assert;
/**
* Builder class that allows collecting objects under the relation types defined for the objects but moving from the
@@ -39,16 +41,22 @@ class HalEmbeddedBuilder {
private final Map<String, List<Object>> embeddeds = new HashMap<String, List<Object>>();
private final RelProvider provider;
private final boolean enforceCollections;
private final boolean preferCollectionRels;
private boolean relAwareFound;
/**
* Creates a new {@link HalEmbeddedBuilder} using the given {@link RelProvider}.
* Creates a new {@link HalEmbeddedBuilder} using the given {@link RelProvider} and prefer collection rels flag.
*
* @param provider can be {@literal null}.
* @param preferCollectionRels whether to prefer to ask the provider for collection rels.
*/
public HalEmbeddedBuilder(RelProvider provider, boolean enforceCollections) {
public HalEmbeddedBuilder(RelProvider provider, boolean preferCollectionRels) {
Assert.notNull(provider, "Relprovider must not be null!");
this.provider = provider;
this.enforceCollections = enforceCollections;
this.preferCollectionRels = preferCollectionRels;
}
/**
@@ -59,16 +67,14 @@ class HalEmbeddedBuilder {
*/
public void add(Object value) {
Object unwrapped = ObjectUtils.getResourceType(value);
if (unwrapped == null) {
if (ObjectUtils.getResourceType(value) == null) {
return;
}
String rel = getDefaultedRelFor(unwrapped, true);
String rel = getDefaultedRelFor(value, true);
if (!embeddeds.containsKey(rel)) {
rel = getDefaultedRelFor(unwrapped, enforceCollections);
rel = getDefaultedRelFor(value, preferCollectionRels);
}
List<Object> currentValue = embeddeds.get(rel);
@@ -80,7 +86,7 @@ class HalEmbeddedBuilder {
} else if (currentValue.size() == 1) {
currentValue.add(value);
embeddeds.remove(rel);
embeddeds.put(getDefaultedRelFor(unwrapped, true), currentValue);
embeddeds.put(getDefaultedRelFor(value, true), currentValue);
} else {
currentValue.add(value);
}
@@ -88,16 +94,32 @@ class HalEmbeddedBuilder {
private String getDefaultedRelFor(Object value, boolean forCollection) {
Object unwrapped = ObjectUtils.getResourceType(value);
if (value instanceof RelAware) {
this.relAwareFound = true;
return ((RelAware) value).getRel();
}
if (provider == null) {
return DEFAULT_REL;
}
Class<?> type = AopUtils.getTargetClass(value);
Class<?> type = AopUtils.getTargetClass(unwrapped);
String rel = forCollection ? provider.getCollectionResourceRelFor(type) : provider.getItemResourceRelFor(type);
return rel == null ? DEFAULT_REL : rel;
}
/**
* Returns whether the builder only created collection rels.
*
* @return
*/
public boolean hasOnlyCollections() {
return preferCollectionRels && !relAwareFound;
}
/**
* Returns the added objects keyed up by their relation types.
*

View File

@@ -274,7 +274,7 @@ public class Jackson2HalModule extends SimpleModule {
JavaType valueType = typeFactory.constructCollectionType(ArrayList.class, Resource.class);
JavaType mapType = typeFactory.constructMapType(HashMap.class, keyType, valueType);
JsonSerializer<Object> valueSerializer = enforceEmbeddedCollections ? provider.findValueSerializer(valueType,
JsonSerializer<Object> valueSerializer = builder.hasOnlyCollections() ? provider.findValueSerializer(valueType,
property) : new OptionalListJackson2Serializer(property);
MapSerializer serializer = MapSerializer.construct(new String[] {}, mapType, true, null,