com.github.hal4j.resources.ResourceSupport Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hal4j-api Show documentation
Show all versions of hal4j-api Show documentation
HAL API specification for Java
package com.github.hal4j.resources;
import java.io.Serializable;
import java.net.URI;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static com.github.hal4j.resources.HALLink.*;
import static java.util.Collections.*;
import static java.util.Optional.*;
import static java.util.stream.Collectors.toList;
/**
* Core implementation of all resources which defines all HAL contracts and data model.
*
*/
public abstract class ResourceSupport implements Serializable {
private final BindingContext context;
private final URI self;
private final Map> _links;
private final Map> _embedded;
ResourceSupport(ResourceSupport resource) {
this(resource._links, resource._embedded, resource.context);
}
ResourceSupport(Map> _links,
Map> _embedded,
BindingContext context) {
this._links = _links != null && !_links.isEmpty() ? clone(_links) : null;
HALLink self = null;
if (this._links != null) {
List all = this._links.get(REL_SELF);
if (all != null) {
for (HALLink link : all) {
if (link.name == null) {
self = link;
break;
} else if (self == null) {
self = link;
}
}
}
}
this.self = self != null ? self.uri() : null;
this._embedded = _embedded != null && ! _embedded.isEmpty() ? clone(_embedded) : null;
this.context = context;
}
private static Map> clone(Map> map) {
Map> result = new HashMap<>();
map.forEach((rel, list) -> result.put(rel, unmodifiableList(new ArrayList<>(list))));
return Collections.unmodifiableMap(result);
}
/**
* Returns binding context used to construct this resource object
* @return the binding context or null
if not used/set.
*/
public BindingContext context() {
return context;
}
/**
* Checks if this resource equals given object. Two resources are considered equal if their self
links are equal.
* @param that object to check for equality
* @return true
if that
object is a resource and it has the same self
link.
* @see HALLink#REL_SELF
*/
@Override
public boolean equals(Object that) {
if (this == that) return true;
if (!(that instanceof ResourceSupport)) return false;
ResourceSupport thatResource = (ResourceSupport) that;
URI uri;
try {
uri = self();
} catch (MissingLinkException e) {
return false;
}
return Objects.equals(uri, thatResource.self());
}
/**
* Returns hash code of this resource defined as hash code of the self
link
* @return hash code of this object
*/
@Override
public int hashCode() {
try {
return Objects.hash(self());
} catch (MissingLinkException e) {
return 0;
}
}
/**
* Returns URI of the link with the rel self
* @return the self
link
* @throws MissingLinkException if resource does not contain self
link
*/
public URI self() {
if (this.self == null) {
throw new MissingLinkException(REL_SELF);
}
return this.self;
}
/**
* Returns collection of the links associated with this resource
* @return non-null collection of the links associated with this resource
*/
public Links links() {
return new Links();
}
/**
* Returns collection of the embedded objects included with this resource
* @return non-null collection of the embedded objects included with this resource
*/
public EmbeddedObjects embedded() {
return new EmbeddedObjects();
}
/**
* Common query operations for links and embedded objects
* @param type of object (link or embedded)
*/
public abstract class MetadataElements {
private final Map> map;
private MetadataElements(Map> map) {
this.map = map;
}
/**
* Returns underlying objects "as is", i.e. as a Map with relation keys and lists of objects.
* May return null
.
* @return the underlying map or null
*/
public Map> asIs() {
return this.map;
}
/**
* Return all items with given relation
* @param rel name of relation
* @return list of items or empty list
*/
public List findAll(String rel) {
if (rel == null) {
throw new NullPointerException("Relation name cannot be null");
}
return ofNullable(map)
.map(m -> m.get(rel))
.orElse(emptyList());
}
/**
* Return all items with given relation
* @param rel name of relation as URI
* @return list of items or empty list
*/
public List findAll(URI rel) {
return findAll(rel.toString());
}
/**
* Return any of the items with given relation
* @param rel name of relation as URI
* @return any found item or empty Optional
*/
public Optional find(URI rel) {
return findAll(rel).stream().findAny();
}
/**
* Return any of the items with given relation
* @param rel name of relation
* @return any found item or empty Optional
*/
public Optional find(String rel) {
return findAll(rel).stream().findAny();
}
/**
* Checks if any item with given relation is present
* @param rel name of relation as URI
* @return true
if such relation exists, false
otherwise.
*/
public boolean include(URI rel) {
return ofNullable(map)
.map(m -> m.containsKey(rel.toString()))
.orElse(false);
}
/**
* Checks if any item with given relation is present
* @param rel name of relation
* @return true
if such relation exists, false
otherwise.
*/
public boolean include(String rel) {
return this.include(URI.create(rel));
}
/**
* Count number of items with given relation
* @param rel name of relation
* @return number of items or 0 if relation does not exist in this resource.
*/
public int count(String rel) {
return this.findAll(rel).size();
}
/**
* Count number of items with given relation
* @param rel name of relation as URI
* @return number of items or 0 if relation does not exist in this resource.
*/
public int count(URI rel) {
return this.findAll(rel).size();
}
public Stream selectAll(String uri) {
return this.findAll(uri).stream();
}
public Stream selectAll(URI uri) {
return this.findAll(uri).stream();
}
/**
* Returns underlying objects as a Map with relation keys and lists of objects.
* If underlying map is null
, returns empty map.
* @return the underlying map or empty map
*/
public Map> all() {
return ofNullable(map).orElse(emptyMap());
}
}
/**
* Wrapper for the collection of links providing convenience methods for querying them
*/
public class Links extends MetadataElements {
Links() {
super(_links);
}
/**
* Checks if there's at least one link with given relation and name
* @param rel name of relation
* @param name name of the link (see {@link HALLink#name})
* @return true
if such link exists, false
otherwise.
*/
public boolean include(String rel, String name) {
return findAll(rel).stream().anyMatch(link -> name.equals(link.name));
}
/**
* Finds a link with the given name of relation and resolves it to the permanent URI of resource
* @param rel name of relation
* @return Optional with the link if such link exists, Optional.empty
otherwise.
*/
public Optional resolve(String rel) {
return resolve(rel, link -> Objects.equals(null, link.name));
}
/**
* Finds any matching link with given relation and name
* @param rel name of relation
* @param name name of the link (see {@link HALLink#name})
* @return Optional with the link if such link exists, Optional.empty
otherwise.
*/
public Optional resolve(String rel, String name) {
return resolve(rel, link -> Objects.equals(name, link.name));
}
/**
* Finds any link with given relation that matches given condition
* @param rel name of relation
* @param condition the condition to match
* @return Optional with the link if such link exists, Optional.empty
otherwise.
*/
public Optional resolve(String rel, Predicate condition) {
List links = findAll(rel);
if (links.isEmpty()) {
return empty();
}
HALLink link = null;
HALLink self = null;
for (int i = 0; i < links.size(); i++) {
HALLink l = links.get(i);
if (condition.test(l)) {
if (!HREF_SAME_RESOURCE.equals(l.href)) {
return of(l);
}
link = l;
}
if (l.name == null && !HREF_SAME_RESOURCE.equals(l.href)) {
self = l;
}
if (link != null && self != null) {
break;
}
}
if (link == null) return empty();
if (self == null) {
self = findAll(REL_SELF).stream().filter(value -> !SAME_RESOURCE.test(value)).findFirst()
.orElseThrow(() -> new IllegalStateException("Self link not found"));
}
return Optional.of(link.resolve(self));
}
}
/**
* Wrapper for the collection of embedded objects providing convenience methods for querying them
*/
public class EmbeddedObjects extends MetadataElements