blob: 54186e793507caf7bdcdbb1842e07b2e541143bb [file] [log] [blame]
package org.apache.abdera2.common.http;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.abdera2.common.text.CharUtils;
import static org.apache.abdera2.common.text.CharUtils.quotedIfNotToken;
import org.apache.abdera2.common.text.Codec;
import org.apache.abdera2.common.text.CharUtils.Profile;
/**
* Implementation of the Prefer HTTP Header, e.g.
*
* Prefer: return-no-content, my-preference=abc;xyz=123
*/
public class Preference implements Serializable {
public static final String RETURN_NO_CONTENT = "return-no-content";
public static final String RETURN_ACCEPTED = "return-accepted";
public static final String RETURN_CONTENT = "return-content";
public static final String RETURN_STATUS = "return-status";
/**
* The "return-no-content" token indicates that the client prefers that
* the server not include an entity in the response to a successful
* request. Typically, such responses would use the 204 No Content
* status code as defined in Section 10.2.5 of [RFC2616], but other
* status codes can be used as appropriate.
*/
public static final Preference PREF_RETURN_NO_CONTENT =
new Preference(RETURN_NO_CONTENT);
/**
* The "return-accepted" token indicates that the client prefers that
* the server respond with a 202 Accepted response indicating that the
* request has been accepted for processing.
*/
public static final Preference PREF_RETURN_ACCEPTED =
new Preference(RETURN_ACCEPTED);
/**
* The "return-content" token indicates that the client prefers that the
* server include an entity representing the current state of the
* resource in the response to a successful request.
*/
public static final Preference PREF_RETURN_CONTENT =
new Preference(RETURN_CONTENT);
/**
* The "return-status" token indicates that the client prefers that the
* server include an entity describing the status of the request in the
* response to a successful request.
*/
public static final Preference PREF_RETURN_STATUS =
new Preference(RETURN_STATUS);
private static final long serialVersionUID = -6238673046322517740L;
private final String token;
private String value;
private final Map<String,String> params =
new HashMap<String,String>();
public Preference(String token) {
CharUtils.verify(token, Profile.TOKEN);
this.token = token.toLowerCase();
}
public String getToken() {
return token;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
private static final Set<String> reserved =
new HashSet<String>();
static {
// no reserved yet
}
private static boolean reserved(String name) {
return reserved.contains(name);
}
public void addParam(String name, String value) {
if (name == null || reserved(name))
throw new IllegalArgumentException();
if (value == null && params.containsKey(name))
params.remove(name);
else {
params.put(name, value);
}
}
public boolean matches(String token) {
if (token == null) return false;
return this.token.equalsIgnoreCase(token.toLowerCase());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((params == null) ? 0 : params.hashCode());
result = prime * result + ((token == null) ? 0 : token.hashCode());
result = prime * result + ((value == null) ? 0 : value.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;
Preference other = (Preference) obj;
if (params == null) {
if (other.params != null)
return false;
} else if (!params.equals(other.params))
return false;
if (token == null) {
if (other.token != null)
return false;
} else if (!token.equals(other.token))
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
public String getParam(String name) {
if (name == null || reserved(name))
throw new IllegalArgumentException();
return params.get(name);
}
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(token);
if (value != null) {
String encval = Codec.encode(value, Codec.STAR);
if (value.equals(encval)) {
buf.append('=')
.append(quotedIfNotToken(value));
} else {
buf.append('*')
.append('=')
.append(encval);
}
}
for (Map.Entry<String, String> entry : params.entrySet()) {
String val = entry.getValue();
String encval = Codec.encode(val,Codec.STAR);
buf.append(';')
.append(entry.getKey());
if (!val.equals(encval)) {
buf.append('*')
.append('=')
.append(encval);
} else {
buf.append('=')
.append(quotedIfNotToken(val));
}
}
return buf.toString();
}
private final static String TOKEN = "[\\!\\#\\$\\%\\&\\'\\*\\+\\-\\.\\^\\_\\`\\|\\~a-zA-Z0-9]+";
private final static String PARAM = TOKEN+"\\s*={1}\\s*(?:(?:\"[^\"]+\")|(?:"+TOKEN+"))";
private final static String PREF = TOKEN+"(?:\\s*={1}\\s*(?:(?:\"[^\"]+\")|(?:"+TOKEN+"))){0,1}";
private final static String PARAMS = "(?:\\s*;\\s*(" + PARAM + "(?:\\s*;\\s*"+PARAM+")))*";
private final static String PATTERN = "("+PREF+")" + PARAMS;
private final static Pattern pattern =
Pattern.compile(PATTERN);
private final static Pattern param =
Pattern.compile("("+PARAM+")");
public static Iterable<Preference> parse(String text) {
List<Preference> prefs = new ArrayList<Preference>();
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
String pref = matcher.group(1);
String params = matcher.group(2);
String token = null, tokenval = null;
if (pref != null) {
String[] ps = pref.split("\\s*=\\s*", 2);
token = ps[0].trim();
if (ps.length == 2)
tokenval = Codec.decode(CharUtils.unquote(ps[1]));
}
Preference preference = new Preference(token);
preference.setValue(tokenval);
prefs.add(preference);
if (params != null) {
Matcher mparams = param.matcher(params);
while(mparams.find()) {
String p = mparams.group(1);
String[] ps = p.split("\\s*=\\s*", 2);
preference.addParam(ps[0], Codec.decode(CharUtils.unquote(ps[1])));
}
}
}
return prefs;
}
public static String toString(
Preference preference,
Preference... preferences) {
if (preference == null)
return null;
StringBuilder buf = new StringBuilder();
buf.append(preference.toString());
for (Preference pref : preferences) {
buf.append(',').append(pref.toString());
}
return buf.toString();
}
public static String toString(Iterable<Preference> preferences) {
StringBuilder buf = new StringBuilder();
boolean first = true;
for (Preference pref : preferences) {
if (!first) buf.append(',');
else first = !first;
buf.append(pref.toString());
}
return buf.toString();
}
public static boolean contains(
Iterable<Preference> preferences,
String token) {
for (Preference pref : preferences)
if (pref.matches(token))
return true;
return false;
}
}