Commit | Line | Data |
---|---|---|
15443216 FT |
1 | package dolda.jsvc.util; |
2 | ||
3 | import java.util.*; | |
4 | import java.io.*; | |
5 | ||
6 | public class AcceptMap implements Iterable<AcceptMap.Entry> { | |
7 | private static final Entry[] typehint = new Entry[0]; | |
8 | private final Entry[] list; | |
9 | private static final Comparator<Entry> lcmp = new Comparator<Entry>() { | |
10 | public int compare(Entry a, Entry b) { | |
11 | if(a.q > b.q) | |
12 | return(-1); | |
13 | else if(a.q < b.q) | |
14 | return(1); | |
15 | return(0); | |
16 | } | |
17 | }; | |
18 | ||
19 | public static class Entry { | |
20 | public String type; | |
21 | public double q = 1.0; | |
22 | public Map<String, String> pars = new HashMap<String, String>(); | |
23 | } | |
24 | ||
25 | private AcceptMap(Entry[] list) { | |
26 | this.list = list; | |
27 | } | |
28 | ||
29 | private static String token(PushbackReader in) throws IOException { | |
30 | Misc.eatws(in); | |
31 | StringBuilder buf = new StringBuilder(); | |
32 | while(true) { | |
33 | int c = in.read(); | |
34 | if((c < 0) || Character.isWhitespace((char)c) || (",;=:\\\"?".indexOf((char)c) >= 0)) { | |
35 | if(c >= 0) | |
36 | in.unread(c); | |
37 | if(buf.length() == 0) | |
38 | return(null); | |
39 | return(buf.toString()); | |
40 | } else { | |
41 | buf.append((char)c); | |
42 | } | |
43 | } | |
44 | } | |
45 | ||
46 | public static AcceptMap parse(Reader in) throws IOException { | |
47 | PushbackReader pin = new PushbackReader(in); | |
48 | List<Entry> lbuf = new LinkedList<Entry>(); | |
49 | List<Entry> ebuf = new LinkedList<Entry>(); | |
50 | while(true) { | |
51 | Entry e = new Entry(); | |
52 | if((e.type = token(pin)) == null) | |
53 | throw(new Http.EncodingException("Illegal format for Accept* header (expected type)")); | |
54 | ebuf.add(e); | |
55 | Misc.eatws(pin); | |
56 | int sep = pin.read(); | |
57 | if(sep < 0) { | |
58 | lbuf.addAll(ebuf); | |
59 | break; | |
60 | } else if(sep == ';') { | |
61 | String st = "tp"; | |
62 | boolean flush = false; | |
63 | boolean end = false; | |
64 | while(true) { | |
65 | String key = Http.tokenunquote(pin); | |
66 | Misc.eatws(pin); | |
67 | if(pin.read() != '=') | |
68 | throw(new Http.EncodingException("Illegal format for Accept* header (expected `=' in parameter)")); | |
69 | String val = Http.tokenunquote(pin); | |
70 | Misc.eatws(pin); | |
71 | int psep = pin.read(); | |
72 | if(st == "tp") { | |
73 | if(key.equals("q")) { | |
74 | double q; | |
75 | try { | |
76 | q = Double.parseDouble(val); | |
77 | } catch(NumberFormatException exc) { | |
78 | throw(new Http.EncodingException(exc.getMessage())); | |
79 | } | |
80 | for(Entry e2 : ebuf) | |
81 | e2.q = q; | |
82 | flush = true; | |
83 | st = "ap"; | |
89fea15a | 84 | } else { |
15443216 FT |
85 | e.pars.put(key, val); |
86 | } | |
89fea15a | 87 | } else if(st == "ap") { |
15443216 FT |
88 | /* No known accept-params */ |
89 | } | |
90 | if(psep < 0) { | |
91 | end = true; | |
92 | flush = true; | |
93 | break; | |
94 | } else if(psep == ';') { | |
95 | } else if(psep == ',') { | |
96 | break; | |
97 | } else { | |
98 | throw(new Http.EncodingException("Illegal format for Accept* header (expected `;', `,' or end of parameters)")); | |
99 | } | |
100 | } | |
101 | if(flush) { | |
102 | lbuf.addAll(ebuf); | |
103 | ebuf = new LinkedList<Entry>(); | |
104 | } | |
105 | if(end) | |
106 | break; | |
107 | } else if(sep == ',') { | |
108 | } else { | |
109 | throw(new Http.EncodingException("Illegal format for Accept* header (expected `;', `,' or end of list)")); | |
110 | } | |
111 | } | |
112 | Entry[] list = lbuf.toArray(typehint); | |
113 | Arrays.sort(list, lcmp); | |
114 | return(new AcceptMap(list)); | |
115 | } | |
116 | ||
117 | public static AcceptMap parse(String input) { | |
118 | try { | |
119 | return(parse(new StringReader(input))); | |
120 | } catch(IOException e) { | |
121 | throw(new Error(e)); | |
122 | } | |
123 | } | |
124 | ||
125 | public Iterator<Entry> iterator() { | |
126 | return(new Iterator<Entry>() { | |
127 | private int i = 0; | |
128 | ||
129 | public Entry next() { | |
130 | return(list[i++]); | |
131 | } | |
132 | ||
133 | public boolean hasNext() { | |
134 | return(i < list.length); | |
135 | } | |
136 | ||
137 | public void remove() { | |
138 | throw(new UnsupportedOperationException()); | |
139 | } | |
140 | }); | |
141 | } | |
142 | ||
143 | public Entry accepts(String type) { | |
144 | for(Entry e : list) { | |
145 | if(e.type.equals(type)) | |
146 | return(e); | |
147 | } | |
148 | return(null); | |
149 | } | |
150 | ||
151 | public String toString() { | |
152 | StringBuilder buf = new StringBuilder(); | |
153 | for(Entry e : list) | |
154 | buf.append(String.format("%s %f %s\n", e.type, e.q, e.pars)); | |
155 | return(buf.toString()); | |
156 | } | |
157 | } |