350 lines
9.4 KiB
Plaintext
350 lines
9.4 KiB
Plaintext
note
|
|
description: "Summary description for {MIME_PARSE}."
|
|
author: ""
|
|
date: "$Date$"
|
|
revision: "$Revision$"
|
|
description : "Accept Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1"
|
|
class
|
|
MIME_PARSE
|
|
|
|
inherit
|
|
REFACTORING_HELPER
|
|
|
|
feature -- Parser
|
|
|
|
parse_mime_type (a_mime_type: STRING): PARSE_RESULTS
|
|
-- Parses a mime-type into its component parts.
|
|
-- For example, the media range 'application/xhtml;q=0.5' would get parsed
|
|
-- into:
|
|
-- ('application', 'xhtml', {'q', '0.5'})
|
|
local
|
|
l_parts: LIST [STRING]
|
|
p: STRING
|
|
sub_parts: LIST [STRING]
|
|
i: INTEGER
|
|
l_full_type: STRING
|
|
l_types: LIST [STRING]
|
|
do
|
|
fixme ("Improve code!!!")
|
|
create Result.make
|
|
l_parts := a_mime_type.split (';')
|
|
from
|
|
i := 1
|
|
until
|
|
i > l_parts.count
|
|
loop
|
|
p := l_parts.at (i)
|
|
sub_parts := p.split ('=')
|
|
if sub_parts.count = 2 then
|
|
Result.put (trim (sub_parts[2]), trim (sub_parts[1]))
|
|
end
|
|
i := i + 1
|
|
end
|
|
--Java URLConnection class sends an Accept header that includes a
|
|
--single "*" - Turn it into a legal wildcard.
|
|
|
|
l_full_type := trim (l_parts[1])
|
|
if l_full_type.same_string ("*") then
|
|
l_full_type := "*/*"
|
|
end
|
|
l_types := l_full_type.split ('/')
|
|
Result.set_type (trim (l_types[1]))
|
|
Result.set_sub_type (trim (l_types[2]))
|
|
end
|
|
|
|
parse_media_range (a_range: STRING): PARSE_RESULTS
|
|
-- Media-ranges are mime-types with wild-cards and a 'q' quality parameter.
|
|
-- 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.
|
|
do
|
|
fixme ("Improve the code!!!")
|
|
Result := parse_mime_type (a_range)
|
|
if attached Result.item ("q") as q then
|
|
if
|
|
q.is_double and then
|
|
attached {REAL_64} q.to_double as r and then
|
|
(r >= 0.0 and r <= 1.0)
|
|
then
|
|
--| Keep current value
|
|
if q.same_string ("1") then
|
|
--| Use 1.0 formatting
|
|
Result.put ("1.0", "q")
|
|
end
|
|
else
|
|
Result.put ("1.0", "q")
|
|
end
|
|
else
|
|
Result.put ("1.0", "q")
|
|
end
|
|
end
|
|
|
|
|
|
fitness_and_quality_parsed (a_mime_type: STRING; parsed_ranges: LIST [PARSE_RESULTS]): FITNESS_AND_QUALITY
|
|
-- Find the best match for a given mimeType against a list of media_ranges
|
|
-- that have already been parsed by parse_media_range. 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.
|
|
local
|
|
best_fitness: INTEGER
|
|
target_q: REAL_64
|
|
best_fit_q: REAL_64
|
|
target: PARSE_RESULTS
|
|
range: PARSE_RESULTS
|
|
keys: LIST [STRING]
|
|
param_matches: INTEGER
|
|
element: detachable STRING
|
|
l_fitness: INTEGER
|
|
do
|
|
best_fitness := -1
|
|
best_fit_q := 0.0
|
|
target := parse_media_range (a_mime_type)
|
|
if attached target.item ("q") as q and then q.is_double then
|
|
target_q := q.to_double
|
|
if target_q < 0.0 then
|
|
target_q := 0.0
|
|
elseif target_q > 1.0 then
|
|
target_q := 1.0
|
|
end
|
|
else
|
|
target_q := 1.0
|
|
end
|
|
|
|
if
|
|
attached target.type as l_target_type and
|
|
attached target.sub_type as l_target_sub_type
|
|
then
|
|
from
|
|
parsed_ranges.start
|
|
until
|
|
parsed_ranges.after
|
|
loop
|
|
range := parsed_ranges.item_for_iteration
|
|
if
|
|
(
|
|
attached range.type as l_range_type and then
|
|
(l_target_type.same_string (l_range_type) or l_range_type.same_string ("*") or l_target_type.same_string ("*"))
|
|
) and
|
|
(
|
|
attached range.sub_type as l_range_sub_type and then
|
|
(l_target_sub_type.same_string (l_range_sub_type) or l_range_sub_type.same_string ("*") or l_target_sub_type.same_string ("*"))
|
|
)
|
|
then
|
|
from
|
|
param_matches := 0
|
|
keys := target.keys
|
|
keys.start
|
|
until
|
|
keys.after
|
|
loop
|
|
element := keys.item_for_iteration
|
|
if
|
|
not element.same_string ("q") and then
|
|
range.has_key (element) and then
|
|
(attached target.item (element) as t_item and attached range.item (element) as r_item) and then
|
|
t_item.same_string (r_item)
|
|
then
|
|
param_matches := param_matches + 1
|
|
end
|
|
keys.forth
|
|
end
|
|
|
|
if l_range_type.same_string (l_target_type) then
|
|
l_fitness := 100
|
|
else
|
|
l_fitness := 0
|
|
end
|
|
|
|
if l_range_sub_type.same_string (l_target_sub_type) then
|
|
l_fitness := l_fitness + 10
|
|
end
|
|
|
|
l_fitness := l_fitness + param_matches
|
|
|
|
if l_fitness > best_fitness then
|
|
best_fitness := l_fitness
|
|
element := range.item ("q")
|
|
if element /= Void then
|
|
best_fit_q := element.to_double.min (target_q)
|
|
else
|
|
best_fit_q := 0.0
|
|
end
|
|
end
|
|
end
|
|
parsed_ranges.forth
|
|
end
|
|
end
|
|
create Result.make (best_fitness, best_fit_q)
|
|
end
|
|
|
|
quality_parsed (a_mime_type: STRING; parsed_ranges: LIST [PARSE_RESULTS]): REAL_64
|
|
-- 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.
|
|
do
|
|
Result := fitness_and_quality_parsed (a_mime_type, parsed_ranges).quality
|
|
end
|
|
|
|
quality (a_mime_type: STRING; ranges: STRING): REAL_64
|
|
-- Returns the quality 'q' of a mime-type when compared against the
|
|
-- mediaRanges in ranges.
|
|
local
|
|
l_ranges : LIST [STRING]
|
|
res : ARRAYED_LIST [PARSE_RESULTS]
|
|
p_res : PARSE_RESULTS
|
|
do
|
|
l_ranges := ranges.split (',')
|
|
from
|
|
create res.make (10);
|
|
l_ranges.start
|
|
until
|
|
l_ranges.after
|
|
loop
|
|
p_res := parse_media_range (l_ranges.item_for_iteration)
|
|
res.put_left (p_res)
|
|
l_ranges.forth
|
|
end
|
|
Result := quality_parsed (a_mime_type, res)
|
|
end
|
|
|
|
best_match (supported: LIST [STRING]; header: STRING): STRING
|
|
-- Choose the mime-type with the highest fitness score and quality ('q') from a list of candidates.
|
|
local
|
|
l_header_results: LIST [PARSE_RESULTS]
|
|
weighted_matches: LIST [FITNESS_AND_QUALITY]
|
|
l_res: LIST [STRING]
|
|
p_res: PARSE_RESULTS
|
|
fitness_and_quality, first_one: detachable FITNESS_AND_QUALITY
|
|
s: STRING
|
|
do
|
|
l_res := header.split (',')
|
|
create {ARRAYED_LIST [PARSE_RESULTS]} l_header_results.make (l_res.count)
|
|
|
|
fixme("Extract method!!!")
|
|
from
|
|
l_res.start
|
|
until
|
|
l_res.after
|
|
loop
|
|
p_res := parse_media_range (l_res.item_for_iteration)
|
|
l_header_results.force (p_res)
|
|
l_res.forth
|
|
end
|
|
|
|
create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (supported.count)
|
|
|
|
from
|
|
supported.start
|
|
until
|
|
supported.after
|
|
loop
|
|
fitness_and_quality := fitness_and_quality_parsed (supported.item_for_iteration, l_header_results)
|
|
fitness_and_quality.set_mime_type (mime_type (supported.item_for_iteration))
|
|
weighted_matches.force (fitness_and_quality)
|
|
supported.forth
|
|
end
|
|
|
|
--| Keep only top quality+fitness types
|
|
from
|
|
weighted_matches.start
|
|
first_one := weighted_matches.item
|
|
weighted_matches.forth
|
|
until
|
|
weighted_matches.after
|
|
loop
|
|
fitness_and_quality := weighted_matches.item
|
|
if first_one < fitness_and_quality then
|
|
first_one := fitness_and_quality
|
|
if not weighted_matches.isfirst then
|
|
from
|
|
weighted_matches.back
|
|
until
|
|
weighted_matches.before
|
|
loop
|
|
weighted_matches.remove
|
|
weighted_matches.back
|
|
end
|
|
weighted_matches.forth
|
|
end
|
|
check weighted_matches.item = fitness_and_quality end
|
|
weighted_matches.forth
|
|
elseif first_one.is_equal (fitness_and_quality) then
|
|
weighted_matches.forth
|
|
else
|
|
check first_one > fitness_and_quality end
|
|
weighted_matches.remove
|
|
end
|
|
end
|
|
if first_one /= Void and then first_one.quality /= 0.0 then
|
|
if weighted_matches.count = 1 then
|
|
Result := first_one.mime_type
|
|
else
|
|
from
|
|
fitness_and_quality := Void
|
|
l_header_results.start
|
|
until
|
|
l_header_results.after or fitness_and_quality /= Void
|
|
loop
|
|
s := l_header_results.item.mime_type
|
|
from
|
|
weighted_matches.start
|
|
until
|
|
weighted_matches.after or fitness_and_quality /= Void
|
|
loop
|
|
fitness_and_quality := weighted_matches.item
|
|
if fitness_and_quality.mime_type.same_string (s) then
|
|
--| Found
|
|
else
|
|
fitness_and_quality := Void
|
|
weighted_matches.forth
|
|
end
|
|
end
|
|
l_header_results.forth
|
|
end
|
|
if fitness_and_quality /= Void then
|
|
Result := fitness_and_quality.mime_type
|
|
else
|
|
Result := first_one.mime_type
|
|
end
|
|
end
|
|
else
|
|
Result := ""
|
|
end
|
|
end
|
|
|
|
feature {NONE} -- Implementation
|
|
|
|
mime_type (s: STRING): STRING
|
|
local
|
|
p: INTEGER
|
|
do
|
|
p := s.index_of (';', 1)
|
|
if p > 0 then
|
|
Result := trim (s.substring (1, p - 1))
|
|
else
|
|
Result := trim (s.string)
|
|
end
|
|
end
|
|
|
|
trim (a_string: STRING): STRING
|
|
-- trim whitespace from the beginning and end of a string
|
|
require
|
|
valid_argument : a_string /= Void
|
|
do
|
|
a_string.left_adjust
|
|
a_string.right_justify
|
|
Result := a_string
|
|
ensure
|
|
result_same_as_argument: a_string = Result
|
|
end
|
|
|
|
note
|
|
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
|
end
|