com.linkedin.restli.internal.server.util.MIMEParse Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of restli-server Show documentation
Show all versions of restli-server Show documentation
Pegasus is a framework for building robust, scalable service architectures using dynamic discovery and simple asychronous type-checked REST + JSON APIs.
package com.linkedin.restli.internal.server.util;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
/**
* Released Under the MIT license
*
* Copyright (c) 2009 Joe Gregorio, Tom Zellman
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions. The above copyright
* notice and this permission notice shall be included in all copies or substantial portions of
* the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/**
* MIME-Type Parser
*
* This class provides basic functions for handling mime-types. It can handle
* matching mime-types against a list of media-ranges. See section 14.1 of the
* HTTP specification [RFC 2616] for a complete explanation.
*
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*
* A port to Java of Joe Gregorio's MIME-Type Parser:
*
* http://code.google.com/p/mimeparse/
*
* Ported by Tom Zellman .
*
*/
public final class MIMEParse
{
/**
* Parse results container
*/
protected static class ParseResults
{
String type;
String subType;
// !a dictionary of all the parameters for the media range
Map params;
@Override
public String toString()
{
StringBuffer s = new StringBuffer("('" + type + "', '" + subType
+ "', {");
for (String k : params.keySet())
s.append("'" + k + "':'" + params.get(k) + "',");
return s.append("})").toString();
}
}
/**
* Carves up a mime-type and returns a ParseResults object
*
* For example, the media range 'application/xhtml;q=0.5' would get parsed
* into:
*
* ('application', 'xhtml', {'q', '0.5'})
*/
protected static ParseResults parseMimeType(String mimeType)
{
String[] parts = StringUtils.split(mimeType, ";");
ParseResults results = new ParseResults();
results.params = new HashMap();
for (int i = 1; i < parts.length; ++i)
{
String p = parts[i];
String[] subParts = StringUtils.split(p, '=');
if (subParts.length == 2)
results.params.put(subParts[0].trim(), subParts[1].trim());
}
String fullType = parts[0].trim();
// Java URLConnection class sends an Accept header that includes a
// single "*" - Turn it into a legal wildcard.
if (fullType.equals("*"))
fullType = "*/*";
String[] types = StringUtils.split(fullType, "/");
results.type = types[0].trim();
results.subType = types[1].trim();
return results;
}
/**
* Carves up a media range and returns a ParseResults.
*
* For example, the media range 'application/*;q=0.5' would get parsed into:
*
* ('application', '*', {'q', '0.5'})
*
* In addition this function also guarantees that there is a value for 'q'
* in the params dictionary, filling it in with a proper default if
* necessary.
*/
protected static ParseResults parseMediaRange(String range)
{
ParseResults results = parseMimeType(range);
String q = results.params.get("q");
float f = NumberUtils.toFloat(q, 1);
if (StringUtils.isBlank(q) || f < 0 || f > 1)
results.params.put("q", "1");
return results;
}
/**
* Structure for holding a fitness/quality combo
*/
protected static class FitnessAndQuality implements
Comparable
{
int fitness;
float quality;
String mimeType; // optionally used
public FitnessAndQuality(int fitness, float quality)
{
this.fitness = fitness;
this.quality = quality;
}
public int compareTo(FitnessAndQuality o)
{
if (fitness == o.fitness)
{
if (quality == o.quality)
return 0;
else
return quality < o.quality ? -1 : 1;
}
else
return fitness < o.fitness ? -1 : 1;
}
}
/**
* Find the best match for a given mimeType against a list of media_ranges
* that have already been parsed by MimeParse.parseMediaRange(). Returns a
* tuple of the fitness value and the value of the 'q' quality parameter of
* the best match, or (-1, 0) if no match was found. Just as for
* quality_parsed(), 'parsed_ranges' must be a list of parsed media ranges.
*/
protected static FitnessAndQuality fitnessAndQualityParsed(String mimeType,
Collection parsedRanges)
{
int bestFitness = -1;
float bestFitQ = 0;
ParseResults target = parseMediaRange(mimeType);
for (ParseResults range : parsedRanges)
{
if ((target.type.equals(range.type) || range.type.equals("*") || target.type
.equals("*"))
&& (target.subType.equals(range.subType)
|| range.subType.equals("*") || target.subType
.equals("*")))
{
for (String k : target.params.keySet())
{
int paramMatches = 0;
if (!k.equals("q") && range.params.containsKey(k)
&& target.params.get(k).equals(range.params.get(k)))
{
paramMatches++;
}
int fitness = (range.type.equals(target.type)) ? 100 : 0;
fitness += (range.subType.equals(target.subType)) ? 10 : 0;
fitness += paramMatches;
if (fitness > bestFitness)
{
bestFitness = fitness;
bestFitQ = NumberUtils
.toFloat(range.params.get("q"), 0);
}
}
}
}
return new FitnessAndQuality(bestFitness, bestFitQ);
}
/**
* Find the best match for a given mime-type against a list of ranges that
* have already been parsed by parseMediaRange(). Returns the 'q' quality
* parameter of the best match, 0 if no match was found. This function
* bahaves the same as quality() except that 'parsed_ranges' must be a list
* of parsed media ranges.
*/
protected static float qualityParsed(String mimeType,
Collection parsedRanges)
{
return fitnessAndQualityParsed(mimeType, parsedRanges).quality;
}
/**
* Returns the quality 'q' of a mime-type when compared against the
* mediaRanges in ranges. For example:
*
* @param mimeType
* @param ranges
*/
public static float quality(String mimeType, String ranges)
{
List results = new LinkedList();
for (String r : StringUtils.split(ranges, ','))
results.add(parseMediaRange(r));
return qualityParsed(mimeType, results);
}
/**
* Takes a list of supported mime-types and finds the best match for all the
* media-ranges listed in header. The value of header must be a string that
* conforms to the format of the HTTP Accept: header. The value of
* 'supported' is a list of mime-types.
*
* MimeParse.bestMatch(Arrays.asList(new String[]{"application/xbel+xml",
* "text/xml"}), "text/*;q=0.5,*; q=0.1") 'text/xml'
*/
public static String bestMatch(Collection supported, String header)
{
List parseResults = new LinkedList();
List weightedMatches = new LinkedList();
for (String r : StringUtils.split(header, ','))
parseResults.add(parseMediaRange(r));
for (String s : supported)
{
FitnessAndQuality fitnessAndQuality = fitnessAndQualityParsed(s,
parseResults);
fitnessAndQuality.mimeType = s;
weightedMatches.add(fitnessAndQuality);
}
Collections.sort(weightedMatches);
FitnessAndQuality lastOne = weightedMatches
.get(weightedMatches.size() - 1);
return NumberUtils.compare(lastOne.quality, 0) != 0 ? lastOne.mimeType
: "";
}
// hidden
private MIMEParse()
{
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy