package org.multibit.mbm.api.hal; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.theoryinpractise.halbuilder.spi.Link; import com.theoryinpractise.halbuilder.spi.ReadableResource; import com.theoryinpractise.halbuilder.spi.Renderer; import com.theoryinpractise.halbuilder.spi.Resource; import javax.annotation.Nullable; import java.io.IOException; import java.io.Writer; import java.util.Collection; import java.util.List; import java.util.Map; import static com.theoryinpractise.halbuilder.impl.api.Support.*; /** * <p>JSON renderer to provide the following to resources:</p> * <ul> * <li>Minified JSON representation</li> * </ul> * * @since 0.0.1 *   */ public class MinifiedJsonRenderer implements Renderer<String> { public Optional<String> render(ReadableResource resource, Writer writer) { com.fasterxml.jackson.core.JsonFactory f = new com.fasterxml.jackson.core.JsonFactory(); f.enable(com.fasterxml.jackson.core.JsonGenerator.Feature.QUOTE_FIELD_NAMES); try { com.fasterxml.jackson.core.JsonGenerator g = f.createJsonGenerator(writer); // Turn off pretty printing since it interferes with testing and bloats output // g.setPrettyPrinter(new DefaultPrettyPrinter()); g.writeStartObject(); renderJson(g, resource, false); g.writeEndObject(); g.close(); } catch (IOException e) { throw new RuntimeException(e); } return Optional.absent(); } private void renderJson(com.fasterxml.jackson.core.JsonGenerator g, ReadableResource resource, boolean embedded) throws IOException { final Link resourceLink = resource.getResourceLink(); final String href = resourceLink.getHref(); if (!resource.getCanonicalLinks().isEmpty() || (!embedded && !resource.getNamespaces().isEmpty())) { g.writeObjectFieldStart(LINKS); List<Link> links = Lists.newArrayList(); // Include namespaces as links when not embedded if (!embedded) { for (Map.Entry<String, String> entry : resource.getNamespaces().entrySet()) { links.add(new Link(null, entry.getValue(), CURIE, Optional.of(entry.getKey()), Optional.<String>absent(), Optional.<String>absent())); } } // Add resource links links.addAll(resource.getLinks()); // Partition resource links by rel Multimap<String, Link> linkMap = Multimaps.index(links, new Function<Link, String>() { public String apply(@Nullable Link link) { return link.getRel(); } }); for (Map.Entry<String, Collection<Link>> linkEntry : linkMap.asMap().entrySet()) { if (linkEntry.getValue().size() == 1) { Link link = linkEntry.getValue().iterator().next(); g.writeObjectFieldStart(linkEntry.getKey()); writeJsonLinkContent(g, link); g.writeEndObject(); } else { g.writeArrayFieldStart(linkEntry.getKey()); for (Link link : linkEntry.getValue()) { g.writeStartObject(); writeJsonLinkContent(g, link); g.writeEndObject(); } g.writeEndArray(); } } g.writeEndObject(); } for (Map.Entry<String, Optional<Object>> entry : resource.getProperties().entrySet()) { if (entry.getValue().isPresent()) { g.writeObjectField(entry.getKey(), entry.getValue().get()); } else { g.writeNullField(entry.getKey()); } } if (!resource.getResources().isEmpty()) { g.writeObjectFieldStart(EMBEDDED); Multimap<String, Resource> resourceMap = Multimaps.index(resource.getResources(), new Function<Resource, String>() { public String apply(@Nullable Resource resource) { List<String> relTypes = Lists.newArrayList(WHITESPACE_SPLITTER.split(resource.getResourceLink().getRel())); Iterables.removeIf(relTypes, new Predicate<String>() { public boolean apply(@Nullable String s) { return SELF.equals(s); } }); return Joiner.on(" ").join(relTypes); } }); for (Map.Entry<String, Collection<Resource>> resourceEntry : resourceMap.asMap().entrySet()) { if (resourceEntry.getValue().size() == 1) { g.writeObjectFieldStart(resourceEntry.getKey()); ReadableResource subResource = resourceEntry.getValue().iterator().next(); renderJson(g, subResource, true); g.writeEndObject(); } else { g.writeArrayFieldStart(resourceEntry.getKey()); for (ReadableResource subResource : resourceEntry.getValue()) { g.writeStartObject(); renderJson(g, subResource, true); g.writeEndObject(); } g.writeEndArray(); } } g.writeEndObject(); } } private void writeJsonLinkContent(com.fasterxml.jackson.core.JsonGenerator g, Link link) throws IOException { g.writeStringField(HREF, link.getHref()); if (link.getName().isPresent()) { g.writeStringField(NAME, link.getName().get()); } if (link.getTitle().isPresent()) { g.writeStringField(TITLE, link.getTitle().get()); } if (link.getHreflang().isPresent()) { g.writeStringField(HREFLANG, link.getHreflang().get()); } if (link.hasTemplate()) { g.writeBooleanField(TEMPLATED, true); } } }