blob: 0c843abd9f7f92fafd2a116e40b308f10ea71fe0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. The ASF licenses this file to You
* under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. For additional information regarding
* copyright in this work, please see the NOTICE file in the top level
* directory of this distribution.
*/
package org.apache.abdera2.common.templates;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.abdera2.common.anno.URITemplate;
import org.apache.abdera2.common.templates.Context;
import org.apache.abdera2.common.templates.Expression;
import org.apache.abdera2.common.templates.MapContext;
import org.apache.abdera2.common.templates.ObjectContext;
import org.apache.abdera2.common.templates.Template;
import org.apache.abdera2.common.templates.Expression.VarSpec;
@SuppressWarnings("unchecked")
public final class Template implements Iterable<Expression>, Serializable {
private static final long serialVersionUID = -613907262632631896L;
private static final Pattern EXPRESSION = Pattern.compile("\\{[^{}]+\\}");
private static final String EXP_START = "\\{";
private static final String EXP_STOP = "\\}";
private final String pattern;
private final Set<Expression> expressions = new HashSet<Expression>();
private final Set<String> variables = new HashSet<String>();
/**
* @param pattern A URI Template
*/
public Template(String pattern) {
if (pattern == null)
throw new IllegalArgumentException("Template pattern must not be null");
this.pattern = pattern;
initExpressions();
}
public Template(Object object) {
this(extractPattern(object));
}
private static String extractPattern(Object object) {
if (object == null)
return null;
if (object instanceof String)
return (String)object;
else if (object instanceof Template)
return ((Template)object).pattern;
Class<?> _class = object instanceof Class ? (Class<?>)object : object.getClass();
URITemplate uriTemplate = (URITemplate)_class.getAnnotation(URITemplate.class);
String pattern =
uriTemplate != null ?
uriTemplate.value() :
object instanceof TemplateProvider ?
((TemplateProvider)object).getTemplate() :
null;
if (pattern == null)
throw new IllegalArgumentException();
return pattern;
}
/**
* Iterate the template expressions
*/
public Iterator<Expression> iterator() {
return expressions.iterator();
}
/**
* Return the array of template variables
*/
private void initExpressions() {
Matcher matcher = EXPRESSION.matcher(pattern);
while (matcher.find()) {
String token = matcher.group();
token = token.substring(1, token.length() - 1);
Expression exp = new Expression(token);
for (VarSpec varspec : exp)
variables.add(varspec.getName());
expressions.add(exp);
}
}
/**
* Return the array of template variables
*/
public Iterable<String> getVariables() {
return variables;
}
/**
* Expand the URI Template using the specified Context.
*
* @param context The Context impl used to resolve variable values
* @return An expanded URI
*/
public String expand(Context context) {
String pattern = this.pattern;
for (Expression exp : this)
pattern =
replace(
pattern,
exp,
exp.evaluate(context));
return pattern;
}
/**
* Expand the URI Template using the non-private fields and methods of the specified object to resolve the template
* tokens
*/
public String expand(Object object) {
return expand(object, false);
}
/**
* Expand the template using the non-private fields and methods of the specified object to resolve the template
* tokens. If isiri is true, IRI escaping rules will be used.
*/
public String expand(Object object, boolean isiri) {
return expand(asContext(object,isiri));
}
private String replace(String pattern, Expression exp, String value) {
return pattern.replaceAll(EXP_START + Pattern.quote(exp.toString()) + EXP_STOP, value);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((pattern == null) ? 0 : pattern.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Template other = (Template)obj;
if (pattern == null) {
if (other.pattern != null)
return false;
} else if (!pattern.equals(other.pattern))
return false;
return true;
}
@Override
public String toString() {
return pattern;
}
public static String expand(String pattern, Context context) {
if (context == null || pattern == null)
throw new IllegalArgumentException();
return new Template(pattern).expand(context);
}
public static String expand(String pattern, Object object) {
return expand(pattern, object, false);
}
public static String expand(String pattern, Object object, boolean isiri) {
if (object == null || pattern == null)
throw new IllegalArgumentException();
return new Template(pattern).expand(object, isiri);
}
public static String expandAnnotated(Object object) {
return expandAnnotated(object,null);
}
@SuppressWarnings("rawtypes")
private static Context asContext(Object obj, boolean isiri) {
return
obj instanceof Context ?
(Context)obj :
obj instanceof Map ?
new MapContext((Map)obj, isiri) :
new ObjectContext(obj, isiri);
}
/**
* Use an Object annotated with the URITemplate annotation to expand a template
*/
public static String expandAnnotated(Object object, Object additional) {
if (object == null)
throw new IllegalArgumentException();
Object contextObject = null;
Class<?> _class = null;
if (object instanceof Class<?>) {
_class = (Class<?>)object;
contextObject = new AnnotationContext(_class);
} else {
_class = object.getClass();
contextObject = object;
if (_class.isAnnotationPresent(org.apache.abdera2.common.anno.Context.class)) {
additional = new AnnotationContext(_class);
}
}
URITemplate uritemplate = (URITemplate)_class.getAnnotation(URITemplate.class);
if (uritemplate != null) {
if (additional != null) {
Context add = asContext(additional, uritemplate.isiri());
Context main = asContext(contextObject, uritemplate.isiri());
contextObject = new DefaultingContext(add,main);
}
return expand(
uritemplate.value(),
contextObject,
uritemplate.isiri());
} else {
throw new IllegalArgumentException("No URI Template provided");
}
}
public static Context getAnnotatedContext(Object object) {
return new AnnotationContext(object);
}
/**
* Create a new Template by appending the given template to this
*/
public Template extend(Template template) {
StringBuilder buf = new StringBuilder(pattern);
if (template != null)
buf.append(template.pattern);
return new Template(buf.toString());
}
public Template extend(String template) {
StringBuilder buf = new StringBuilder(pattern);
if (template != null)
buf.append(template);
return new Template(buf.toString());
}
}