From 15443216a029804091da8aa6fe87427f675e9e64 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sun, 13 Dec 2009 21:14:31 +0100 Subject: [PATCH] Added a utility class for parsing Accept* headers. --- src/dolda/jsvc/util/AcceptMap.java | 157 +++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 src/dolda/jsvc/util/AcceptMap.java diff --git a/src/dolda/jsvc/util/AcceptMap.java b/src/dolda/jsvc/util/AcceptMap.java new file mode 100644 index 0000000..b74cace --- /dev/null +++ b/src/dolda/jsvc/util/AcceptMap.java @@ -0,0 +1,157 @@ +package dolda.jsvc.util; + +import java.util.*; +import java.io.*; + +public class AcceptMap implements Iterable { + private static final Entry[] typehint = new Entry[0]; + private final Entry[] list; + private static final Comparator lcmp = new Comparator() { + public int compare(Entry a, Entry b) { + if(a.q > b.q) + return(-1); + else if(a.q < b.q) + return(1); + return(0); + } + }; + + public static class Entry { + public String type; + public double q = 1.0; + public Map pars = new HashMap(); + } + + private AcceptMap(Entry[] list) { + this.list = list; + } + + private static String token(PushbackReader in) throws IOException { + Misc.eatws(in); + StringBuilder buf = new StringBuilder(); + while(true) { + int c = in.read(); + if((c < 0) || Character.isWhitespace((char)c) || (",;=:\\\"?".indexOf((char)c) >= 0)) { + if(c >= 0) + in.unread(c); + if(buf.length() == 0) + return(null); + return(buf.toString()); + } else { + buf.append((char)c); + } + } + } + + public static AcceptMap parse(Reader in) throws IOException { + PushbackReader pin = new PushbackReader(in); + List lbuf = new LinkedList(); + List ebuf = new LinkedList(); + while(true) { + Entry e = new Entry(); + if((e.type = token(pin)) == null) + throw(new Http.EncodingException("Illegal format for Accept* header (expected type)")); + ebuf.add(e); + Misc.eatws(pin); + int sep = pin.read(); + if(sep < 0) { + lbuf.addAll(ebuf); + break; + } else if(sep == ';') { + String st = "tp"; + boolean flush = false; + boolean end = false; + while(true) { + String key = Http.tokenunquote(pin); + Misc.eatws(pin); + if(pin.read() != '=') + throw(new Http.EncodingException("Illegal format for Accept* header (expected `=' in parameter)")); + String val = Http.tokenunquote(pin); + Misc.eatws(pin); + int psep = pin.read(); + if(st == "tp") { + if(key.equals("q")) { + double q; + try { + q = Double.parseDouble(val); + } catch(NumberFormatException exc) { + throw(new Http.EncodingException(exc.getMessage())); + } + for(Entry e2 : ebuf) + e2.q = q; + flush = true; + st = "ap"; + } else if(st == "ap") { + e.pars.put(key, val); + } + } else { + /* No known accept-params */ + } + if(psep < 0) { + end = true; + flush = true; + break; + } else if(psep == ';') { + } else if(psep == ',') { + break; + } else { + throw(new Http.EncodingException("Illegal format for Accept* header (expected `;', `,' or end of parameters)")); + } + } + if(flush) { + lbuf.addAll(ebuf); + ebuf = new LinkedList(); + } + if(end) + break; + } else if(sep == ',') { + } else { + throw(new Http.EncodingException("Illegal format for Accept* header (expected `;', `,' or end of list)")); + } + } + Entry[] list = lbuf.toArray(typehint); + Arrays.sort(list, lcmp); + return(new AcceptMap(list)); + } + + public static AcceptMap parse(String input) { + try { + return(parse(new StringReader(input))); + } catch(IOException e) { + throw(new Error(e)); + } + } + + public Iterator iterator() { + return(new Iterator() { + private int i = 0; + + public Entry next() { + return(list[i++]); + } + + public boolean hasNext() { + return(i < list.length); + } + + public void remove() { + throw(new UnsupportedOperationException()); + } + }); + } + + public Entry accepts(String type) { + for(Entry e : list) { + if(e.type.equals(type)) + return(e); + } + return(null); + } + + public String toString() { + StringBuilder buf = new StringBuilder(); + for(Entry e : list) + buf.append(String.format("%s %f %s\n", e.type, e.q, e.pars)); + return(buf.toString()); + } +} -- 2.11.0