blob: ddd250f47975d4df7585ea90833624657d3f0826 [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.olingo.odata2.core.batch;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.olingo.odata2.api.batch.BatchException;
import org.apache.olingo.odata2.api.exception.MessageReference;
/**
*
*/
public class AcceptParser {
private static final String BAD_REQUEST = "400";
private static final String ALL = "*";
private static final String REG_EX_QUALITY_FACTOR = "q=((?:1\\.0{0,3})|(?:0\\.[0-9]{0,2}[1-9]))";
private static final String REG_EX_OPTIONAL_WHITESPACE = "\\s?";
private static final Pattern REG_EX_ACCEPT = Pattern.compile("([a-z\\*]+/[a-z0-9\\+\\*\\-=;\\s]+)");
private static final Pattern REG_EX_ACCEPT_WITH_Q_FACTOR = Pattern.compile(REG_EX_ACCEPT + "(?:;"
+ REG_EX_OPTIONAL_WHITESPACE + REG_EX_QUALITY_FACTOR + ")?");
private static final Pattern REG_EX_ACCEPT_LANGUAGES = Pattern
.compile("((?:(?:[a-zA-Z]{1,8})(?:-[a-zA-Z0-9]{1,8}){0,})|(?:\\*))");
private static final Pattern REG_EX_ACCEPT_LANGUAGES_WITH_Q_FACTOR = Pattern.compile(REG_EX_ACCEPT_LANGUAGES + "(?:;"
+ REG_EX_OPTIONAL_WHITESPACE + REG_EX_QUALITY_FACTOR + ")?");
private static final double QUALITY_PARAM_FACTOR = 0.001;
private List<String> acceptHeaderValues = new ArrayList<String>();
private List<String> acceptLanguageHeaderValues = new ArrayList<String>();
public List<String> parseAcceptHeaders() throws BatchException {
return parseQualifiedHeader(acceptHeaderValues,
REG_EX_ACCEPT_WITH_Q_FACTOR,
BatchException.INVALID_ACCEPT_HEADER);
}
public List<String> parseAcceptableLanguages() throws BatchException {
return parseQualifiedHeader(acceptLanguageHeaderValues,
REG_EX_ACCEPT_LANGUAGES_WITH_Q_FACTOR,
BatchException.INVALID_ACCEPT_LANGUAGE_HEADER);
}
private List<String> parseQualifiedHeader(List<String> headerValues, Pattern regEx, MessageReference exectionMessage)
throws BatchException {
final TreeSet<Accept> acceptTree = new TreeSet<AcceptParser.Accept>();
final List<String> acceptHeaders = new ArrayList<String>();
for (final String headerValue : headerValues) {
final String[] acceptParts = headerValue.split(",");
for (final String part : acceptParts) {
final Matcher matcher = regEx.matcher(part.trim());
if (matcher.matches() && matcher.groupCount() == 2) {
final Accept acceptHeader = getQualifiedHeader(matcher);
acceptTree.add(acceptHeader);
} else {
throw new BatchException(exectionMessage.addContent(part), BAD_REQUEST);
}
}
}
for (Accept accept : acceptTree) {
if (!acceptHeaders.contains(accept.getValue())) {
acceptHeaders.add(accept.getValue());
}
}
return acceptHeaders;
}
private Accept getQualifiedHeader(final Matcher matcher) {
final String acceptHeaderValue = matcher.group(1);
double qualityFactor = matcher.group(2) != null ? Double.parseDouble(matcher.group(2)) : 1d;
qualityFactor = getQualityFactor(acceptHeaderValue, qualityFactor);
return new Accept().setQuality(qualityFactor).setValue(acceptHeaderValue);
}
private double getQualityFactor(final String acceptHeaderValue, double qualityFactor) {
int paramNumber = 0;
double typeFactor = 0.0;
double subtypeFactor = 0.0;
String[] mediaRange = acceptHeaderValue.split("(?=[^;]+);");
String[] mediaTypes = mediaRange[0].split("/");
if (mediaTypes.length == 2) {
String type = mediaTypes[0];
String subtype = mediaTypes[1];
if (!ALL.equals(type)) {
typeFactor = 0.001;
}
if (!ALL.equals(subtype)) {
subtypeFactor = 0.001;
}
}
if (mediaRange.length == 2) {
String[] parameters = mediaRange[1].split(";\\s?");
paramNumber = parameters.length;
}
qualityFactor = qualityFactor + paramNumber * QUALITY_PARAM_FACTOR + typeFactor + subtypeFactor;
return qualityFactor;
}
public void addAcceptHeaderValue(final String headerValue) {
acceptHeaderValues.add(headerValue);
}
public void addAcceptLanguageHeaderValue(final String headerValue) {
acceptLanguageHeaderValues.add(headerValue);
}
private static class Accept implements Comparable<Accept> {
private double quality;
private String value;
public String getValue() {
return value;
}
public Accept setValue(final String value) {
this.value = value;
return this;
}
public Accept setQuality(final double quality) {
this.quality = quality;
return this;
}
@Override
public int compareTo(Accept o) {
if (quality <= o.quality) {
return 1;
} else {
return -1;
}
}
}
}