/** * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can * select the license that you prefer but you may not use this file except in * compliance with one of these Licenses. * * You can obtain a copy of the Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet S.A.S. */ package org.restlet.engine.application; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; import org.restlet.data.CharacterSet; import org.restlet.data.Encoding; import org.restlet.data.Form; import org.restlet.data.Language; import org.restlet.data.MediaType; import org.restlet.data.Metadata; import org.restlet.data.Parameter; import org.restlet.data.Preference; import org.restlet.engine.resource.MethodAnnotationInfo; import org.restlet.engine.resource.VariantInfo; import org.restlet.representation.Variant; import org.restlet.service.MetadataService; /** * Content negotiation algorithm that strictly interprets the content * negotiation preferences. * * @author Jerome Louvel */ public class StrictConneg extends Conneg { /** * Constructor. * * @param request * The request including client preferences. * @param metadataService * The metadata service used to get default metadata values. */ public StrictConneg(Request request, MetadataService metadataService) { super(request, metadataService); } /** * Returns the enriched list of character set preferences. * * @return The enriched list of character set preferences. */ protected List<Preference<CharacterSet>> getCharacterSetPrefs() { return getRequest().getClientInfo().getAcceptedCharacterSets(); } /** * Returns the enriched list of encoding preferences. * * @return The enriched list of encoding preferences. */ protected List<Preference<Encoding>> getEncodingPrefs() { return getRequest().getClientInfo().getAcceptedEncodings(); } /** * Returns the enriched list of language preferences. * * @return The enriched list of language preferences. */ protected List<Preference<Language>> getLanguagePrefs() { return getRequest().getClientInfo().getAcceptedLanguages(); } /** * Returns the enriched list of media type preferences. * * @return The enriched list of media type preferences. */ protected List<Preference<MediaType>> getMediaTypePrefs() { return getRequest().getClientInfo().getAcceptedMediaTypes(); } /** * Scores the annotation descriptor. By default, it assess the quality of * the query parameters with the URI query constraint defined in the * annotation value if any. * * @param annotation * The annotation descriptor to score. * @return The annotation descriptor score. */ protected float scoreAnnotation(MethodAnnotationInfo annotation) { float result = -1.0F; if (annotation != null) { if (annotation.getQuery() != null) { if ((getRequest().getResourceRef() == null) || (getRequest().getResourceRef().getQuery() == null)) { // Query constraint defined, but no query provided, no fit result = -1.0F; } else { // Query constraint defined and a query provided, see if fit Form constraintParams = new Form(annotation.getQuery()); Form actualParams = getRequest().getResourceRef() .getQueryAsForm(); Set<Parameter> matchedParams = new HashSet<Parameter>(); Parameter constraintParam; Parameter actualParam; boolean allConstraintsMatched = true; boolean constraintMatched = false; // Verify that each query constraint has been matched for (int i = 0; (i < constraintParams.size()) && allConstraintsMatched; i++) { constraintParam = constraintParams.get(i); constraintMatched = false; for (int j = 0; j < actualParams.size(); j++) { actualParam = actualParams.get(j); if (constraintParam.getName().equals( actualParam.getName())) { // Potential match found based on name if ((constraintParam.getValue() == null) || constraintParam.getValue().equals( actualParam.getValue())) { // Actual match found! constraintMatched = true; matchedParams.add(actualParam); } } } allConstraintsMatched = allConstraintsMatched && constraintMatched; } // Test if all actual query parameters matched a constraint, // so // increase score boolean allActualMatched = (actualParams.size() == matchedParams .size()); if (allConstraintsMatched) { if (allActualMatched) { // All filter parameters matched, no additional // parameter found result = 1.0F; } else { // All filter parameters matched, but additional // parameters found result = 0.75F; } } else { result = -1.0F; } } } else { if ((getRequest().getResourceRef() == null) || (getRequest().getResourceRef().getQuery() == null)) { // No query filter, but no query provided, average fit result = 0.5F; } else { // No query filter, but a query provided, lower fit result = 0.25F; } } if (Context.getCurrentLogger().isLoggable(Level.FINE)) { Context.getCurrentLogger() .fine("Score of annotation \"" + annotation + "\"= " + result); } } else { result = 0.0F; } return result; } /** * Scores a character set relatively to enriched client preferences. * * @param characterSet * The character set to score. * @return The score. */ public float scoreCharacterSet(CharacterSet characterSet) { return scoreMetadata(characterSet, getCharacterSetPrefs()); } /** * Scores encodings relatively to enriched client preferences. * * @param encodings * The encodings to score. * @return The score. */ public float scoreEncodings(List<Encoding> encodings) { return scoreMetadata(encodings, getEncodingPrefs()); } /** * Scores languages relatively to enriched client preferences. * * @param languages * The languages to score. * @return The score. */ public float scoreLanguages(List<Language> languages) { return scoreMetadata(languages, getLanguagePrefs()); } /** * Scores a media type relatively to enriched client preferences. * * @param mediaType * The media type to score. * @return The score. */ public float scoreMediaType(MediaType mediaType) { float result = -1.0F; float current; if (mediaType != null) { for (Preference<MediaType> pref : getMediaTypePrefs()) { if (pref.getMetadata().includes(mediaType, false)) { current = pref.getQuality(); } else { current = -1.0F; } if (current > result) { result = current; } } } else { result = 0.0F; } return result; } /** * Scores a list of metadata relatively to enriched client preferences. * * @param metadataList * The list of metadata to score. * @return The score. */ protected <T extends Metadata> float scoreMetadata(List<T> metadataList, List<Preference<T>> prefs) { float result = -1.0F; float current; if ((metadataList != null) && !metadataList.isEmpty()) { for (Preference<T> pref : prefs) { for (T metadata : metadataList) { if (pref.getMetadata().includes(metadata)) { current = pref.getQuality(); } else { current = -1.0F; } if (current > result) { result = current; } } } } else { result = 0.0F; } return result; } /** * Scores a metadata relatively to enriched client preferences. * * @param metadata * The metadata to score. * @return The score. */ protected <T extends Metadata> float scoreMetadata(T metadata, List<Preference<T>> prefs) { float result = -1.0F; float current; if (metadata != null) { for (Preference<? extends Metadata> pref : prefs) { if (pref.getMetadata().includes(metadata)) { current = pref.getQuality(); } else { current = -1.0F; } if (current > result) { result = current; } } } else { result = 0.0F; } return result; } /** * Scores a variant relatively to enriched client preferences. The language * has a weight of 4, the media type 3, the character set 2 and the encoding * 1. * * @param variant * The variant to score. * @return The enriched client preferences. */ public float scoreVariant(Variant variant) { float result = -1.0F; float languageScore = scoreLanguages(variant.getLanguages()); if (languageScore != -1.0F) { float mediaTypeScore = scoreMediaType(variant.getMediaType()); if (mediaTypeScore != -1.0F) { float characterSetScore = scoreCharacterSet(variant .getCharacterSet()); if (characterSetScore != -1.0F) { float encodingScore = scoreEncodings(variant.getEncodings()); if (encodingScore != -1.0F) { if (variant instanceof VariantInfo) { float annotationScore = scoreAnnotation(((VariantInfo) variant) .getAnnotationInfo()); // Return the weighted average score result = ((languageScore * 4.0F) + (mediaTypeScore * 3.0F) + (characterSetScore * 2.0F) + (encodingScore * 1.0F) + (annotationScore * 2.0F)) / 12.0F; // Take into account the affinity with the input // entity result = result * ((VariantInfo) variant).getInputScore(); } else { // Return the weighted average score result = ((languageScore * 4.0F) + (mediaTypeScore * 3.0F) + (characterSetScore * 2.0F) + (encodingScore * 1.0F)) / 10.0F; } } } } } if (Context.getCurrentLogger().isLoggable(Level.FINE)) { Context.getCurrentLogger().fine( "Total score of variant \"" + variant + "\"= " + result); } return result; } }