
org.kie.remote.common.rest.variant.ServerDrivenNegotiation Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* 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.kie.remote.common.rest.variant;
import java.math.BigDecimal;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Variant;
/**
* Copied from RestEasy 2.3.6.Final with no modifications
* Can be deleted when RESTEASY-960 is fixed
*
* {@link Variant} selection.
*
* @author Pascal S. de Kloe
* @see "RFC 2296"
*/
public class ServerDrivenNegotiation
{
private Map requestedMediaTypes = null;
private Map requestedCharacterSets = null;
private Map requestedEncodings = null;
private Map requestedLanguages = null;
public ServerDrivenNegotiation()
{
}
public void setAcceptHeaders(List headerValues)
{
requestedMediaTypes = null;
if (headerValues == null)
return;
Map requested = null;
for (String headerValue : headerValues)
{
Map mapping = AcceptHeaders.getMediaTypeQualityValues(headerValue);
if (mapping == null)
return;
if (requested == null)
requested = mapping;
else
requested.putAll(mapping);
}
requestedMediaTypes = requested;
}
public void setAcceptCharsetHeaders(List headerValues)
{
requestedCharacterSets = null;
if (headerValues == null)
return;
Map requested = null;
for (String headerValue : headerValues)
{
Map mapping = AcceptHeaders.getStringQualityValues(headerValue);
if (mapping == null)
return;
if (requested == null)
requested = mapping;
else
requested.putAll(mapping);
}
requestedCharacterSets = requested;
}
public void setAcceptEncodingHeaders(List headerValues)
{
requestedEncodings = null;
if (headerValues == null)
return;
Map requested = null;
for (String headerValue : headerValues)
{
Map mapping = AcceptHeaders.getStringQualityValues(headerValue);
if (mapping == null)
return;
if (requested == null)
requested = mapping;
else
requested.putAll(mapping);
}
requestedEncodings = requested;
}
public void setAcceptLanguageHeaders(List headerValues)
{
requestedLanguages = null;
if (headerValues == null)
return;
Map requested = null;
for (String headerValue : headerValues)
{
Map mapping = AcceptHeaders.getLocaleQualityValues(headerValue);
if (mapping == null)
return;
if (requested == null)
requested = mapping;
else
requested.putAll(mapping);
}
requestedLanguages = requested;
}
public Variant getBestMatch(List available)
{
BigDecimal bestQuality = BigDecimal.ZERO;
Variant bestOption = null;
for (Variant option : available)
{
VariantQuality quality = new VariantQuality();
if (!applyMediaType(option, quality))
continue;
if (!applyCharacterSet(option, quality))
continue;
if (!applyEncoding(option, quality))
continue;
if (!applyLanguage(option, quality))
continue;
BigDecimal optionQuality = quality.getOverallQuality();
if (isBetterOption(bestQuality, bestOption, optionQuality, option))
{
bestQuality = optionQuality;
bestOption = option;
}
}
return bestOption;
}
/**
* Tests whether {@code option} is preferable over the current {@code bestOption}.
*/
private static boolean isBetterOption(BigDecimal bestQuality, Variant best,
BigDecimal optionQuality, Variant option)
{
if (best == null)
return true;
MediaType bestType = best.getMediaType();
MediaType optionType = option.getMediaType();
if (bestType != null && optionType != null)
{
if (bestType.getType().equals(optionType.getType()))
{
// Same type
if (bestType.getSubtype().equals(optionType.getSubtype()))
{
// Same subtype
int bestCount = bestType.getParameters().size();
int optionCount = optionType.getParameters().size();
if (optionCount > bestCount)
return true; // more matching parameters
else if (optionCount < bestCount)
return false; // less matching parameters
}
else if ("*".equals(bestType.getSubtype()))
{
return true; // more specific subtype
}
else if ("*".equals(optionType.getSubtype()))
{
return false; // less specific subtype
}
}
else if ("*".equals(bestType.getType()))
{
return true; // more specific type
}
else if ("*".equals(optionType.getType()))
{
return false; // less specific type;
}
}
int signum = bestQuality.compareTo(optionQuality);
if (signum != 0)
return signum < 0;
return getExplicitness(best) < getExplicitness(option);
}
private static int getExplicitness(Variant variant)
{
int explicitness = 0;
if (variant.getMediaType() != null)
++explicitness;
if (variant.getEncoding() != null)
++explicitness;
if (variant.getLanguage() != null)
++explicitness;
return explicitness;
}
private boolean applyMediaType(Variant option, VariantQuality quality)
{
if (requestedMediaTypes == null)
return true;
MediaType mediaType = option.getMediaType();
if (mediaType == null)
return true;
String type = mediaType.getType();
if ("*".equals(type))
type = null;
String subtype = mediaType.getSubtype();
if ("*".equals(subtype))
subtype = null;
Map parameters = mediaType.getParameters();
if (parameters.isEmpty())
parameters = null;
QualityValue bestQuality = QualityValue.NOT_ACCEPTABLE;
int bestMatchCount = -1;
for (MediaType requested : requestedMediaTypes.keySet())
{
int matchCount = 0;
if (type != null)
{
String requestedType = requested.getType();
if (requestedType.equals(type))
++matchCount;
else if (!"*".equals(requestedType))
continue;
}
if (subtype != null)
{
String requestedSubtype = requested.getSubtype();
if (requestedSubtype.equals(subtype))
++matchCount;
else if (!"*".equals(requestedSubtype))
continue;
}
if (parameters != null)
{
Map requestedParameters = requested.getParameters();
if (!hasRequiredParameters(requestedParameters, parameters))
continue;
matchCount += requestedParameters.size();
}
if (matchCount > bestMatchCount)
{
bestMatchCount = matchCount;
bestQuality = requestedMediaTypes.get(requested);
}
else if (matchCount == bestMatchCount)
{
QualityValue qualityValue = requestedMediaTypes.get(requested);
if (bestQuality.compareTo(qualityValue) < 0)
bestQuality = qualityValue;
}
}
if (!bestQuality.isAcceptable())
return false;
quality.setMediaTypeQualityValue(bestQuality);
return true;
}
private boolean hasRequiredParameters(Map required, Map available)
{
for (Entry requiredEntry : required.entrySet())
{
String name = requiredEntry.getKey();
String value = requiredEntry.getValue();
String availableValue = available.get(name);
if (availableValue == null && "charset".equals(name))
{
if (requestedCharacterSets != null
&& !requestedCharacterSets.containsKey(null)
&& !requestedCharacterSets.containsKey(value))
return false;
}
else if (!value.equals(availableValue))
return false;
}
return true;
}
private boolean applyCharacterSet(Variant option, VariantQuality quality)
{
if (requestedCharacterSets == null)
return true;
MediaType mediaType = option.getMediaType();
if (mediaType == null)
return true;
String charsetParameter = mediaType.getParameters().get("charset");
if (charsetParameter == null)
return true;
QualityValue value = requestedCharacterSets.get(charsetParameter);
if (value == null) // try wildcard
value = requestedCharacterSets.get(null);
if (value == null) // no match
return false;
if (!value.isAcceptable()) return false;
quality.setCharacterSetQualityValue(value);
return true;
}
private boolean applyEncoding(Variant option, VariantQuality quality)
{
if (requestedEncodings == null)
return true;
String encoding = option.getEncoding();
if (encoding == null)
return true;
QualityValue value = requestedEncodings.get(encoding);
if (value == null) // try wildcard
value = requestedEncodings.get(null);
if (value == null) // no match
return false;
if (!value.isAcceptable()) return false;
quality.setEncodingQualityValue(value);
return true;
}
private boolean hasCountry(Locale locale)
{
return locale.getCountry() != null && !"".equals(locale.getCountry().trim());
}
private boolean applyLanguage(Variant option, VariantQuality quality)
{
if (requestedLanguages == null)
return true;
Locale language = option.getLanguage();
if (language == null)
return true;
QualityValue value = null;
for (Entry entry : requestedLanguages.entrySet())
{
Locale locale = entry.getKey();
QualityValue qualityValue = entry.getValue();
if (locale == null) continue;
if (locale.getLanguage().equalsIgnoreCase(language.getLanguage()))
{
if (hasCountry(locale) && hasCountry(language))
{
if (locale.getCountry().equalsIgnoreCase(language.getCountry()))
{
value = qualityValue;
break;
}
else
{
continue;
}
}
else if (hasCountry(locale) == hasCountry(language))
{
value = qualityValue;
break;
}
else
{
value = qualityValue; // might be a better match so re-loop
}
}
}
if (value == null) // try wildcard
value = requestedLanguages.get(null);
if (value == null) // no match
return false;
if (!value.isAcceptable()) return false;
quality.setLanguageQualityValue(value);
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy