Skip to content

Commit

Permalink
Added support for managed relationship types
Browse files Browse the repository at this point in the history
Representations now track defined Rel instances, which are used by the
renderers to control rel specific functionality.

The core code now looks at Rel.singleton and prevents duplicate usage.
  • Loading branch information
talios committed Jul 19, 2015
1 parent e200ed8 commit 1f66607
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 17 deletions.
10 changes: 5 additions & 5 deletions pom.xml
Expand Up @@ -3,7 +3,7 @@

<groupId>com.theoryinpractise</groupId>
<artifactId>halbuilder-core</artifactId>
<version>4.0.4-SNAPSHOT</version>
<version>4.1.1-SNAPSHOT</version>
<description>Java based builder for the Hal specification http://stateless.co/hal_specification.html</description>
<packaging>jar</packaging>

Expand Down Expand Up @@ -82,7 +82,7 @@
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<version>3.3</version>
</plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
Expand All @@ -103,7 +103,7 @@
<dependency>
<groupId>com.theoryinpractise</groupId>
<artifactId>halbuilder-api</artifactId>
<version>4.0.1</version>
<version>4.1.1-SNAPSHOT</version>
</dependency>

<dependency>
Expand All @@ -115,7 +115,7 @@
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.8.8</version>
<version>6.9.4</version>
<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -133,7 +133,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>17.0</version>
<version>18.0</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
Expand Down
@@ -1,16 +1,18 @@
package com.theoryinpractise.halbuilder;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.theoryinpractise.halbuilder.api.Rel;
import com.theoryinpractise.halbuilder.api.ContentRepresentation;
import com.theoryinpractise.halbuilder.api.Link;
import com.theoryinpractise.halbuilder.api.Representation;
import com.theoryinpractise.halbuilder.api.RepresentationException;
import com.theoryinpractise.halbuilder.api.RepresentationFactory;
import com.theoryinpractise.halbuilder.api.RepresentationReader;
import com.theoryinpractise.halbuilder.api.RepresentationWriter;
import com.theoryinpractise.halbuilder.api.ContentRepresentation;
import com.theoryinpractise.halbuilder.impl.ContentType;
import com.theoryinpractise.halbuilder.impl.representations.MutableRepresentation;
import com.theoryinpractise.halbuilder.impl.representations.NamespaceManager;
Expand All @@ -29,8 +31,13 @@ public class DefaultRepresentationFactory extends AbstractRepresentationFactory
private NamespaceManager namespaceManager = new NamespaceManager();
private List<Link> links = Lists.newArrayList();
private Set<URI> flags = Sets.newHashSet();
private Map<String, Rel> rels = Maps.newHashMap();

public DefaultRepresentationFactory() {
withRel(Rel.singleton("self"));
}

public DefaultRepresentationFactory withRenderer(String contentType, Class<? extends RepresentationWriter<String>> rendererClass) {
public DefaultRepresentationFactory withRenderer(String contentType, Class<? extends RepresentationWriter<String>> rendererClass) {
contentRenderers.put(new ContentType(contentType), rendererClass);
return this;
}
Expand All @@ -46,7 +53,16 @@ public DefaultRepresentationFactory withNamespace(String namespace, String href)
return this;
}

@Override
@Override
public RepresentationFactory withRel(Rel rel) {
if (rels.containsKey(rel.rel())) {
throw new IllegalStateException(String.format("Rel %s is already declared.", rel.rel()));
}
rels.put(rel.rel(), rel);
return this;
}

@Override
public DefaultRepresentationFactory withLink(String rel, String href) {
links.add(new Link(this, rel, href));
return this;
Expand Down Expand Up @@ -77,6 +93,11 @@ public Representation newRepresentation(String href) {
representation.withNamespace(entry.getKey(), entry.getValue());
}

// Add factorry standard rels
for (Rel rel : rels.values()) {
representation.withRel(rel);
}

// Add factory standard links
for (Link link : links) {
representation.withLink(link.getRel(), link.getHref(), link.getName(), link.getTitle(), link.getHreflang(), link.getProfile());
Expand Down Expand Up @@ -121,4 +142,8 @@ public Set<URI> getFlags() {
return ImmutableSet.copyOf(flags);
}

public Map<String,Rel> getRels() {
return ImmutableMap.copyOf(rels);
}

}
Expand Up @@ -17,6 +17,7 @@
import com.google.common.collect.Ordering;
import com.google.common.collect.Table;
import com.theoryinpractise.halbuilder.AbstractRepresentationFactory;
import com.theoryinpractise.halbuilder.api.Rel;
import com.theoryinpractise.halbuilder.api.Contract;
import com.theoryinpractise.halbuilder.api.Link;
import com.theoryinpractise.halbuilder.api.ReadableRepresentation;
Expand Down Expand Up @@ -58,6 +59,7 @@ public int compare(Link l1, Link l2) {

protected NamespaceManager namespaceManager = new NamespaceManager();

protected Map<String, Rel> rels = Maps.newHashMap();
protected List<Link> links = Lists.newArrayList();
protected Map<String, Object> properties = Maps.newTreeMap(usingToString());
protected Multimap<String, ReadableRepresentation> resources = ArrayListMultimap.create();
Expand Down Expand Up @@ -298,17 +300,17 @@ public void toString(String contentType, Set<URI> flags, Writer writer) {
representationWriter.write(this, uriBuilder.build(), writer);
}

@Override
public String toString(String contentType, URI... flags) {
return toString(contentType, ImmutableSet.copyOf(flags));
}
@Override
public String toString(String contentType, URI... flags) {
return toString(contentType, ImmutableSet.copyOf(flags));
}

@Override
public void toString(String contentType, Writer writer, URI... flags) {
@Override
public void toString(String contentType, Writer writer, URI... flags) {
toString(contentType, ImmutableSet.copyOf(flags), writer);
}

@Override
@Override
public int hashCode() {
int h = namespaceManager.hashCode();
h += links.hashCode();
Expand Down
Expand Up @@ -2,6 +2,7 @@

import com.google.common.collect.ImmutableMultimap;
import com.theoryinpractise.halbuilder.AbstractRepresentationFactory;
import com.theoryinpractise.halbuilder.api.Rel;
import com.theoryinpractise.halbuilder.api.Link;
import com.theoryinpractise.halbuilder.api.ReadableRepresentation;

Expand Down Expand Up @@ -34,4 +35,7 @@ public Link getResourceLink() {
}


public Map<String, Rel> getRels() {
return rels;
}
}
@@ -1,7 +1,13 @@
package com.theoryinpractise.halbuilder.impl.representations;

import com.google.common.collect.ImmutableMap;
import com.theoryinpractise.halbuilder.AbstractRepresentationFactory;
import com.theoryinpractise.halbuilder.api.*;
import com.theoryinpractise.halbuilder.api.Rel;
import com.theoryinpractise.halbuilder.api.Link;
import com.theoryinpractise.halbuilder.api.ReadableRepresentation;
import com.theoryinpractise.halbuilder.api.Representable;
import com.theoryinpractise.halbuilder.api.Representation;
import com.theoryinpractise.halbuilder.api.RepresentationException;
import com.theoryinpractise.halbuilder.impl.api.Support;

import java.beans.BeanInfo;
Expand All @@ -12,6 +18,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.util.Map;

import static java.lang.String.format;

Expand All @@ -28,7 +35,27 @@ public MutableRepresentation(AbstractRepresentationFactory representationFactory
super(representationFactory);
}

/**
* Define rel semantics for this representation
* @param rel A defined relationship type
*/
public MutableRepresentation withRel(Rel rel) {
if (rels.containsKey(rel.rel())) {
throw new IllegalStateException(String.format("Rel %s is already declared.", rel.rel()));
}
rels.put(rel.rel(), rel);
return this;
}

/**
* Retrieve the defined rel semantics for this representation
* @return
*/
public Map<String,Rel> getRels() {
return ImmutableMap.copyOf(rels);
}

/**
* Add a link to this resource
*
* @param rel
Expand All @@ -48,11 +75,23 @@ public MutableRepresentation withLink(String rel, String href) {
*/
public MutableRepresentation withLink(String rel, String href, String name, String title, String hreflang, String profile) {
Support.checkRelType(rel);

validateSingletonRel(rel);

links.add(new Link(representationFactory, rel, href, name, title, hreflang, profile));
return this;
}

/**
private void validateSingletonRel(String rel) {
if (rels.containsKey(rel)) {
// Rel is resisted, check for duplicate singleton
if (rels.get(rel).isSingleton() && (!getLinksByRel(rel).isEmpty() || !getResourcesByRel(rel).isEmpty())) {
throw new IllegalStateException(String.format("%s is registered as a single rel and already exists.", rel));
}
}
}

/**
* Add a link to this resource
*
* @param rel
Expand Down Expand Up @@ -135,6 +174,9 @@ public Representation withNamespace(String namespace, String href) {

public MutableRepresentation withRepresentation(String rel, ReadableRepresentation resource) {
Support.checkRelType(rel);

validateSingletonRel(rel);

resources.put(rel, resource);
// Propagate null property flag to parent.
if (resource.hasNullProperties()) {
Expand Down
46 changes: 46 additions & 0 deletions src/test/java/com/theoryinpractise/halbuilder/SingleLinksTest.java
@@ -0,0 +1,46 @@
package com.theoryinpractise.halbuilder;

import com.theoryinpractise.halbuilder.api.Rel;
import org.testng.annotations.Test;

import static org.fest.assertions.api.Fail.fail;

public class SingleLinksTest {

@Test
public void testDuplicateSingleLinksFails() {

try {
new DefaultRepresentationFactory()
.withRel(Rel.singleton("bar"))
.newRepresentation("/foo")
.withLink("bar", "/bar")
.withLink("bar", "/bar");

fail("This should have failed with an InvalidStateException.)");

} catch (IllegalStateException exected) {
//
}
}

@Test
public void testDuplicateSingleEmbedFails() {

try {
final DefaultRepresentationFactory factory = new DefaultRepresentationFactory();

factory
.withRel(Rel.singleton("bar"))
.newRepresentation("/foo")
.withRepresentation("bar", factory.newRepresentation().withProperty("id", 1))
.withRepresentation("bar", factory.newRepresentation().withProperty("id", 1));

fail("This should have failed with an InvalidStateException.)");

} catch (IllegalStateException exected) {
//
}
}

}

0 comments on commit 1f66607

Please sign in to comment.