1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.james.jdkim.tagvalue;
21
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.regex.Pattern;
30
31
32
33
34
35 public class TagValue {
36
37 private static final boolean DEBUG = false;
38 protected static final boolean VALIDATION = true;
39
40 private static Pattern tagPattern = Pattern
41 .compile("^[A-Za-z][A-Za-z0-9_]*$");
42 private static final String tval = "[^; \t\r\n]+";
43
44 private static Pattern valuePattern = Pattern.compile("^(" + tval
45 + "((\r\n[\t ]|[\t ])+" + tval + ")*)?$");
46
47
48 private Map
49
50 protected Set
51 protected Map
52 private String stringRepresentation = null;
53
54 protected Set tagSet() {
55 return tagValues.keySet();
56 }
57 protected boolean containsTag(String tag) {
58 return tagValues.containsKey(tag);
59 }
60
61 protected CharSequence trimFWS(CharSequence data, int tStart, int tStop,
62 boolean trimWSP) {
63 if (DEBUG)
64 System.out.println("1[" + data + "]" + tStart + "|" + tStop + "="
65 + data.subSequence(tStart, tStop + 1) + "]");
66
67 while (tStart < tStop
68 && (data.charAt(tStart) == ' ' || data.charAt(tStart) == '\t')
69 || (tStart < tStop - 2 && data.charAt(tStart) == '\r'
70 && data.charAt(tStart + 1) == '\n' && (data
71 .charAt(tStart + 2) == ' ' || data.charAt(tStart + 2) == '\t'))) {
72 if (data.charAt(tStart) == '\r')
73 tStart += 3;
74 else
75 tStart++;
76 }
77
78 if (DEBUG)
79 System.out.println("2[" + data + "]" + tStart + "|" + tStop + "="
80 + data.subSequence(tStart, tStop + 1) + "]");
81
82 while (tStart < tStop
83 && (data.charAt(tStop) == ' ' || data.charAt(tStop) == '\t')) {
84 tStop--;
85 if ((tStart <= tStop - 1 && data.charAt(tStop) == '\n' && data
86 .charAt(tStop - 1) == '\r')
87 || (tStart < tStop && (data.charAt(tStop) == ' ' || data
88 .charAt(tStop) == '\t'))) {
89 if (data.charAt(tStop) == '\n')
90 tStop -= 2;
91 else
92 tStop--;
93 }
94 }
95
96 if (DEBUG)
97 System.out.println("3[" + data + "]" + tStart + "|" + tStop + "="
98 + data.subSequence(tStart, tStop + 1) + "]");
99 if (trimWSP) {
100 return trimWSP(data, tStart, tStop);
101 } else {
102 return data.subSequence(tStart, tStop + 1);
103 }
104 }
105
106 private CharSequence trimWSP(CharSequence data, int vStart, int vStop) {
107 if (vStop < vStart - 1)
108 throw new IllegalArgumentException("Stop must be >= than start");
109 while (vStart <= vStop
110 && (data.charAt(vStart) == ' ' || data.charAt(vStart) == '\t'))
111 vStart++;
112 while (vStart <= vStop
113 && (data.charAt(vStop) == ' ' || data.charAt(vStop) == '\t'))
114 vStop--;
115 return data.subSequence(vStart, vStop + 1);
116 }
117
118 public TagValue(String data) {
119 tagValues = newTagValue();
120 init();
121 parse(data);
122 }
123
124 protected Map newTagValue() {
125
126
127 return new HashMap();
128 }
129
130 protected void init() {
131 }
132
133
134
135
136
137
138
139 protected void parse(String data) {
140 for (int i = 0; i < data.length(); i++) {
141 int equal = data.indexOf('=', i);
142 if (equal == -1) {
143
144
145 String rest = data.substring(i);
146 if (rest.length() > 0
147 && trimFWS(rest, 0, rest.length() - 1, true).length() > 0) {
148 throw new IllegalStateException(
149 "Unexpected termination at position " + i + ": "
150 + data);
151 }
152 i = data.length();
153 continue;
154 }
155
156
157 int next = data.indexOf(';', i);
158 if (next == -1) {
159 next = data.length();
160 }
161
162 if (equal > next) {
163 throw new IllegalStateException("Found ';' before '=' in "
164 + data);
165 }
166
167 CharSequence tag = trimFWS(data, i, equal - 1, true).toString();
168 if (VALIDATION && !tagPattern.matcher(tag).matches()) {
169 throw new IllegalStateException("Syntax error in tag: " + tag);
170 }
171 String tagString = tag.toString();
172 if (tagValues.containsKey(tagString)) {
173 throw new IllegalStateException(
174 "Syntax error (duplicate tag): " + tag);
175 }
176
177 CharSequence value = trimFWS(data, equal + 1, next - 1, true);
178 if (VALIDATION && !valuePattern.matcher(value).matches()) {
179 throw new IllegalStateException("Syntax error in value: "
180 + value);
181 }
182
183 tagValues.put(tagString, value);
184 i = next;
185 }
186 this.stringRepresentation = data;
187 }
188
189 public int hashCode() {
190 final int prime = 31;
191 int result = 1;
192 result = prime * result
193 + ((tagValues == null) ? 0 : tagValues.hashCode());
194 return result;
195 }
196
197 public boolean equals(Object obj) {
198 if (this == obj)
199 return true;
200 if (obj == null)
201 return false;
202 if (getClass() != obj.getClass())
203 return false;
204 TagValue other = (TagValue) obj;
205 if (tagValues == null) {
206 if (other.tagValues != null)
207 return false;
208 } else if (!tagValues.equals(other.tagValues))
209 return false;
210 return true;
211 }
212
213 public Set getTags() {
214 return tagValues.keySet();
215 }
216
217 protected CharSequence getValue(String key) {
218 CharSequence val = (CharSequence) tagValues.get(key);
219 if (val == null)
220 return getDefault(key);
221 else
222 return val;
223 }
224
225 protected void setValue(String tag, String value) {
226 stringRepresentation = null;
227 tagValues.put(tag, value);
228 }
229
230
231 protected CharSequence getDefault(String key) {
232 return (CharSequence) defaults.get(key);
233 }
234
235 public void validate() {
236
237 for (Iterator i = mandatoryTags.iterator(); i.hasNext();) {
238 String tag = (String) i.next();
239 if (getValue(tag) == null)
240 throw new IllegalStateException("Missing mandatory tag: " + tag);
241 }
242 }
243
244 protected List stringToColonSeparatedList(String h, Pattern pattern) {
245 List headers = new ArrayList();
246 for (int i = 0; i < h.length(); i++) {
247 int p = h.indexOf(':', i);
248 if (p == -1)
249 p = h.length();
250 CharSequence cs = trimFWS(h, i, p - 1, false);
251 if (VALIDATION) {
252 if (!pattern.matcher(cs).matches())
253 throw new IllegalStateException(
254 "Syntax error in field name: " + cs);
255 }
256 headers.add(cs);
257 i = p;
258 }
259 return headers;
260 }
261
262 protected boolean isInListCaseInsensitive(CharSequence hash, List hashes) {
263 for (Iterator i = hashes.iterator(); i.hasNext();) {
264 CharSequence suppHash = (CharSequence) i.next();
265 if (hash.toString().equalsIgnoreCase(suppHash.toString()))
266 return true;
267 }
268 return false;
269 }
270
271 public String toString() {
272 if (stringRepresentation == null) {
273 updateStringRepresentation();
274 }
275 return stringRepresentation;
276 }
277
278 private void updateStringRepresentation() {
279
280 StringBuffer res = new StringBuffer();
281 Set s = getTags();
282 for (Iterator i = s.iterator(); i.hasNext();) {
283 String tag = (String) i.next();
284 res.append(tag);
285 res.append("=");
286 res.append(getValue(tag));
287 res.append("; ");
288 }
289
290 stringRepresentation = res.toString();
291 }
292
293 }