blob: 199dde96c4bfbd5312b9d6761a728636db8c69ee [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. 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.
*/
package org.apache.marmotta.commons.http;
import org.apache.commons.lang3.StringUtils;
import org.openrdf.query.resultio.QueryResultFormat;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Add file description here!
* <p/>
* Author: Sebastian Schaffert
*/
public class MarmottaHttpUtils {
/**
* A utility method for parsing HTTP Content-Type and Accept header, taking into account different parameters that
* are typically passed. Recognized parameters:
* - charset: gives the charset of the content
* - q: gives the precedence of the content
* The result is an ordered list of content types in order of the computed preference in the header value passed as
* string argument.
* <p/>
* Author: Sebastian Schaffert
*/
public static List<ContentType> parseAcceptHeader(String header) {
String[] components = header.split(",");
List<ContentType> contentTypes = new ArrayList<ContentType>(components.length);
for(String c : components) {
String mt[] = c.split(";");
String[] tst = mt[0].split("/");
if(tst.length == 2) {
ContentType type = parseContentType(c);
if(type != null) {
contentTypes.add(type);
}
}
}
Collections.sort(contentTypes);
return contentTypes;
}
public static List<ContentType> parseStringList(Collection<String> types) {
List<ContentType> contentTypes = new ArrayList<ContentType>(types.size());
for(String c : types) {
ContentType type = parseContentType(c);
if(type != null) {
contentTypes.add(type);
}
}
return contentTypes;
}
public static List<ContentType> parseQueryResultFormatList(Collection<? extends QueryResultFormat> types) {
List<ContentType> contentTypes = new ArrayList<ContentType>();
for(QueryResultFormat c : types) {
// We cannot handle formats that do not have a charset, ie, they are binary formats
if(c.hasCharset()) {
for(String nextMimeType : c.getMIMETypes()) {
if(nextMimeType.equals("application/xml")) {
// HACK: Remove application/xml so that application/sparql-results+xml
// and application/rdf+xml, for tuple/boolean and graph results, respectively,
// will be preferred
continue;
}
ContentType type = new ContentType(parseContentType(nextMimeType).getType(),
parseContentType(nextMimeType).getSubtype(), c.getCharset());
if(c.getDefaultMIMEType().equals(nextMimeType)) {
type.setParameter("q", "1.0");
} else {
type.setParameter("q", "0.5");
}
contentTypes.add(type);
}
}
}
return contentTypes;
}
public static ContentType parseContentType(String c) {
if (StringUtils.isBlank(c)) return null;
String mt[] = c.split(";");
String[] tst = mt[0].split("/");
if(tst.length == 2) {
ContentType type = new ContentType(tst[0],tst[1]);
// add parameters
for(int i=1; i<mt.length; i++) {
String[] kv = mt[i].split("=");
if(kv.length == 2) {
type.setParameter(kv[0].trim(),kv[1].trim());
if("charset".equalsIgnoreCase(kv[0].trim())) {
type.setCharset(Charset.forName(kv[1].trim()));
}
}
}
return type;
} else {
return null;
}
}
/**
* Determine the best content type out of the selection of content types we offer and the ordered list of content
* types requested by the peer.
*
* TODO: implement remove variant selection algorithm (RFC 2296, http://tools.ietf.org/html/rfc2296)
*
* @param offeredTypes list of offered types in order of precedence
* @param requestedTypes list of requested types in order of precedence - may contain wildcards
* @return
*/
public static ContentType bestContentType(List<ContentType> offeredTypes, List<ContentType> requestedTypes) {
// check for directly matching types
for(ContentType requested : requestedTypes) {
for(ContentType offered : offeredTypes) {
if(requested.matches(offered)) {
return offered;
}
}
}
// check for qualified subtypes also
for(ContentType requested : requestedTypes) {
for(ContentType offered : offeredTypes) {
if(requested.matchesSubtype(offered)) {
return offered;
}
}
}
// check for wildcard matching types
for(ContentType requested : requestedTypes) {
for(ContentType offered : offeredTypes) {
if(requested.matchesWildcard(offered)) {
return offered;
}
}
}
return null;
}
public static ContentType performContentNegotiation(String accept, Collection<String> producedContentTypes) {
return performContentNegotiation(accept, MarmottaHttpUtils.parseStringList(producedContentTypes));
}
public static ContentType performContentNegotiation(String accept, List<ContentType> producedContentTypes) {
List<ContentType> acceptedContentTypes = MarmottaHttpUtils.parseAcceptHeader(StringUtils.defaultString(accept, ""));
return MarmottaHttpUtils.bestContentType(producedContentTypes, acceptedContentTypes);
}
}