/*
* 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.
* 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.hal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.hateoas.RelProvider;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.core.EmbeddedWrapper;
import org.springframework.hateoas.core.EmbeddedWrappers;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Builder class that allows collecting objects under the relation types defined for the objects but moving from the
* single resource relation to the collection one, once more than one object of the same type is added.
*
* @author Oliver Gierke
* @author Dietrich Schulten
*/
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;
private final CurieProvider curieProvider;
private final EmbeddedWrappers wrappers;
/**
* 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, CurieProvider curieProvider, boolean preferCollectionRels) {
Assert.notNull(provider, "Relprovider must not be null!");
this.provider = provider;
this.curieProvider = curieProvider;
this.wrappers = new EmbeddedWrappers(preferCollectionRels);
}
/**
* Adds the given value to the embeddeds. Will skip doing so if the value is {@literal null} or the content of a
* {@link Resource} is {@literal null}.
*
* @param source can be {@literal null}.
*/
public void add(Object source) {
EmbeddedWrapper wrapper = wrappers.wrap(source);
if (wrapper == null) {
return;
}
String collectionRel = getDefaultedRelFor(wrapper, true);
String collectionOrItemRel = collectionRel;
if (!embeddeds.containsKey(collectionRel)) {
collectionOrItemRel = getDefaultedRelFor(wrapper, wrapper.isCollectionValue());
}
Object currentValue = embeddeds.get(collectionOrItemRel);
Object value = wrapper.getValue();
if (currentValue == null && !wrapper.isCollectionValue()) {
embeddeds.put(collectionOrItemRel, value);
return;
}
List<Object> list = new ArrayList<Object>();
list.addAll(asCollection(currentValue));
list.addAll(asCollection(wrapper.getValue()));
embeddeds.remove(collectionOrItemRel);
embeddeds.put(collectionRel, list);
}
@SuppressWarnings("unchecked")
private Collection<Object> asCollection(Object source) {
return source instanceof Collection ? (Collection<Object>) source : source == null ? Collections.emptySet()
: Collections.singleton(source);
}
private String getDefaultedRelFor(EmbeddedWrapper wrapper, boolean forCollection) {
String valueRel = wrapper.getRel();
if (StringUtils.hasText(valueRel)) {
return valueRel;
}
if (provider == null) {
return DEFAULT_REL;
}
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) {
rel = curieProvider.getNamespacedRelFor(rel);
}
return rel == null ? DEFAULT_REL : rel;
}
/**
* Returns the added objects keyed up by their relation types.
*
* @return
*/
public Map<String, Object> asMap() {
return Collections.unmodifiableMap(embeddeds);
}
}