/**
* Copyright (C) 2012-2014 Gist Labs, LLC. (http://gistlabs.com)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.gistlabs.mechanize.document.json.hypermedia;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.gistlabs.mechanize.document.json.node.JsonNode;
import com.gistlabs.mechanize.document.node.Node;
import com.gistlabs.mechanize.document.node.NodeVisitor;
/**
* Search JSON nodes to find hyperlink defintions in the data. This is based on pattern matching attribute names.
*
* This class will find attributes that match either exact string (i.e. "href"), or patterns (i.e. "more-href").
*
* Also, in the case of exact matches then the link relation will be looked up by heuristic. Either the "rel"
* attribute is used, if found, or the name of the containing json object if present. For pattern matched attributes
* the prefix before the pattern is used as the rel.
*
*/
public class JsonLinkFinder {
static class Match {
final String attrName;
final String rel;
public Match(String attrName, String rel) {
this.attrName = attrName;
this.rel = rel;
}
}
private final Set<String> linkingNames = new HashSet<String>(Arrays.asList("href", "uri"));
public List<JsonLink> findRecursive(JsonNode node) {
if (node==null) throw new NullPointerException(String.format("root=%s", node));
final List<JsonLink> result = new ArrayList<JsonLink>();
node.visit(new NodeVisitor() {
@Override
public boolean beginNode(Node node) {
findOn((JsonNode)node, result);
return true;
}
@Override
public void endNode(Node node) {
// no op
}
});
return result;
}
public List<JsonLink> findWithChildren(JsonNode node) {
if (node==null) throw new NullPointerException(String.format("root=%s", node));
List<JsonLink> result = new ArrayList<JsonLink>();
findOn(node, result);
for (JsonNode child : node.getChildren()) {
findOn(child, result);
}
return result;
}
public List<JsonLink> findOn(JsonNode node) {
if (node==null) throw new NullPointerException(String.format("root=%s", node));
List<JsonLink> result = new ArrayList<JsonLink>();
findOn(node, result);
return result;
}
void findOn(JsonNode node, List<JsonLink> result) {
List<String> attributeNames = node.getAttributeNames();
for (String attrName : attributeNames) {
Match match = matches(attrName);
if (match!=null) {
result.add(build(node, match));
}
}
}
Match matches(String attrName) {
Match exactMatch = exactMatch(attrName);
if (exactMatch!=null) return exactMatch;
return patternMatch(attrName);
}
Match exactMatch(String attrName) {
if (linkingNames.contains(attrName.toLowerCase())) {
return new Match(attrName, null);
} else {
return null;
}
}
Match patternMatch(String attrName) {
for (String linkingName : linkingNames) {
if (attrName.endsWith("-"+linkingName) || attrName.endsWith("_"+linkingName)) {
String prefix = attrName.substring(0, attrName.length()-linkingName.length()-1); // subtract the last one for the - or _
return new Match(attrName, prefix);
}
}
return null;
}
JsonLink build(JsonNode node, Match match) {
return new JsonLink(null, node, match.attrName, match.rel);
}
}