Fixed an AcceptMap bug.
[jsvc.git] / src / dolda / jsvc / util / AcceptMap.java
CommitLineData
15443216
FT
1package dolda.jsvc.util;
2
3import java.util.*;
4import java.io.*;
5
6public 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}