/* * This file is part of the URI Template library. * * For licensing information please see the file license.txt included in the release. * A copy of this licence can also be found at * http://www.opensource.org/licenses/artistic-license-2.0.php */ package org.weborganic.furi; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A URI Template for constructing URIs following the same structure. * * Instances of this class implement the URI templates as defined by the URI Template (Draft 3) by * Joe Gregorio. * * A URI Template follows the URI syntax and can be expanded given a set of variable values. * * @see <a * href="http://bitworking.org/projects/URI-Templates/spec/draft-gregorio-uritemplate-03.html">URI * Template (draft 3)</a> * * @author Christophe Lauret * @version 5 November 2009 */ public class URITemplate implements Expandable { /** * The regular expression pattern to identify template expansions within the template. */ private final static Pattern EXPANSION_PATTERN = Pattern.compile("\\{[^}]*}"); /** * The string representation of the URL template. */ private final String _template; /** * The list of tokens corresponding to this URL template. */ private final List<Token> _tokens; /** * Creates a new URI Template instance. * * @param template A String following the URI template syntax. * * @throws NullPointerException If the specified template is <code>null</code>. * @throws URITemplateSyntaxException If the string provided does not follow the proper syntax. */ public URITemplate(String template) throws IllegalArgumentException { if (template == null) { throw new NullPointerException("Cannot create a URI template with a null template"); } this._tokens = digest(template); this._template = template; } /** * Creates a new URI Template instance using the specified token factory. * * <p>If the specified factory is <code>null</code>, the default is used. * * @param template A String following the URI template syntax. * @param factory A token factory in order to choose the URI template syntax to use. * * @throws NullPointerException If the specified template is <code>null</code>. * @throws URITemplateSyntaxException If the string provided does not follow the proper syntax. */ public URITemplate(String template, TokenFactory factory) throws IllegalArgumentException { if (template == null) { throw new NullPointerException("Cannot create a URI template with a null template"); } this._tokens = digest(template, factory != null ? factory : TokenFactory.getInstance()); this._template = template; } /** * Expands the template to produce a URI as defined by the URI Template specifications. * * @param parameters The list of variables and their values for substitution. */ public String expand(Parameters parameters) { StringBuffer uri = new StringBuffer(); for (Token t : this._tokens) { uri.append(t.expand(parameters)); } return uri.toString(); } /** * Method provided for convenience. * * It returns the same as: * * <pre> * return new URITemplate(template).expand(variables); * </pre> * * @param template The URI template. * @param parameters The parameter values to use for substitution. * * @return The corresponding expanded URI. */ public static String expand(String template, Parameters parameters) { return new URITemplate(template).expand(parameters); } /** * Returns the list of tokens corresponding to the specified URI template. * * @param template The URI template to digest. * * @return The corresponding list of URL tokens. * * @throws URITemplateSyntaxException If the string cannot be parsed. */ public static List<Token> digest(String template) throws URITemplateSyntaxException { return digest(template, TokenFactory.getInstance()); } /** * Returns the list of tokens corresponding to the specified URI template. * * @param template The URI template to digest. * * @return The corresponding list of URL tokens. * * @throws URITemplateSyntaxException If the string cannot be parsed. */ public static List<Token> digest(String template, TokenFactory factory) throws URITemplateSyntaxException { List<Token> tokens = new ArrayList<Token>(); Matcher m = EXPANSION_PATTERN.matcher(template); int start = 0; while (m.find()) { // any text since the last expansion if (m.start() > start) { String text = template.substring(start, m.start()); tokens.add(new TokenLiteral(text)); } // add the expansion String exp = m.group(); tokens.add(factory.newToken(exp)); // update the state variables start = m.end(); } // any text left over, including if there were no expansions if (start < template.length()) { String text = template.substring(start, template.length()); // support for wild cards only at the end of the string. if (text.endsWith("*")) { tokens.add(new TokenLiteral(text.substring(0, text.length() - 1))); tokens.add(factory.newToken("*")); } else { tokens.add(new TokenLiteral(text)); } } return tokens; } /** * Returns the underlying list of tokens. * * <p> * Note: this method exposes the underlying structure of this class and should remain protected. * * @return The underlying list of tokens. */ protected List<Token> tokens() { return this._tokens; } @Override public boolean equals(Object o) { if (o == this) { return true; } if ((o == null) || (o.getClass() != this.getClass())) { return false; } URITemplate t = (URITemplate) o; return (_template == t._template || (_template != null && _template.equals(t._template))); } @Override public int hashCode() { return 127 * this._template.hashCode() + this._template.hashCode(); } @Override public String toString() { return this._template; } }