EMMA Coverage Report (generated Thu Nov 19 17:07:02 CET 2009)
[all classes][org.apache.james.jdkim.tagvalue]

COVERAGE SUMMARY FOR SOURCE FILE [TagValue.java]

nameclass, %method, %block, %line, %
TagValue.java100% (1/1)100% (20/20)95%  (595/627)93%  (114.9/123)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class TagValue100% (1/1)100% (20/20)95%  (595/627)93%  (114.9/123)
equals (Object): boolean 100% (1/1)70%  (26/37)62%  (8/13)
stringToColonSeparatedList (String, Pattern): List 100% (1/1)79%  (45/57)91%  (10/11)
trimWSP (CharSequence, int, int): CharSequence 100% (1/1)85%  (40/47)71%  (5/7)
hashCode (): int 100% (1/1)89%  (17/19)97%  (3.9/4)
<static initializer> 100% (1/1)100% (7/7)100% (2/2)
TagValue (String): void 100% (1/1)100% (25/25)100% (8/8)
containsTag (String): boolean 100% (1/1)100% (5/5)100% (1/1)
getDefault (String): CharSequence 100% (1/1)100% (6/6)100% (1/1)
getTags (): Set 100% (1/1)100% (4/4)100% (1/1)
getValue (String): CharSequence 100% (1/1)100% (14/14)100% (4/4)
init (): void 100% (1/1)100% (1/1)100% (1/1)
isInListCaseInsensitive (CharSequence, List): boolean 100% (1/1)100% (21/21)100% (6/6)
newTagValue (): Map 100% (1/1)100% (4/4)100% (1/1)
parse (String): void 100% (1/1)100% (167/167)100% (26/26)
setValue (String, String): void 100% (1/1)100% (10/10)100% (3/3)
tagSet (): Set 100% (1/1)100% (4/4)100% (1/1)
toString (): String 100% (1/1)100% (8/8)100% (3/3)
trimFWS (CharSequence, int, int, boolean): CharSequence 100% (1/1)100% (121/121)100% (13/13)
updateStringRepresentation (): void 100% (1/1)100% (41/41)100% (11/11)
validate (): void 100% (1/1)100% (29/29)100% (6/6)

1/****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one   *
3 * or more contributor license agreements.  See the NOTICE file *
4 * distributed with this work for additional information        *
5 * regarding copyright ownership.  The ASF licenses this file   *
6 * to you under the Apache License, Version 2.0 (the            *
7 * "License"); you may not use this file except in compliance   *
8 * with the License.  You may obtain a copy of the License at   *
9 *                                                              *
10 *   http://www.apache.org/licenses/LICENSE-2.0                 *
11 *                                                              *
12 * Unless required by applicable law or agreed to in writing,   *
13 * software distributed under the License is distributed on an  *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15 * KIND, either express or implied.  See the License for the    *
16 * specific language governing permissions and limitations      *
17 * under the License.                                           *
18 ****************************************************************/
19 
20package org.apache.james.jdkim.tagvalue;
21 
22import java.util.ArrayList;
23import java.util.HashMap;
24import java.util.HashSet;
25import java.util.Iterator;
26import java.util.List;
27import java.util.Map;
28import java.util.Set;
29import java.util.regex.Pattern;
30 
31/**
32 * This class handle a tag=value list string as defined by DKIM specification It
33 * also supports mandatoryTags and default values as a commodity to subclasses.
34 */
35public 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    // validate value chars
44    private static Pattern valuePattern = Pattern.compile("^(" + tval
45            + "((\r\n[\t ]|[\t ])+" + tval + ")*)?$");
46 
47    // we may use a TreeMap because we may need to know original order.
48    private Map/* String, CharSequence */tagValues;
49 
50    protected Set/* String */mandatoryTags = new HashSet();
51    protected Map/* String, CharSequence */defaults = new HashMap();
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        // rimozione di FWS a inizio selezione
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        // rimozione di FWS a fine selezione.
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        // extensions may override this to use TreeMaps in order to keep track
126        // of orders
127        return new HashMap();
128    }
129 
130    protected void init() {
131    }
132 
133    /**
134     * subclasses have to make sure tagValues is initialized during init().
135     * 
136     * @param data
137     *                the string to be parsed
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                // TODO check whether this is correct or not
144                // this allow FWS/WSP after the final ";"
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            // we could start from "equals" but we start from "i" in
156            // order to spot invalid values before validation.
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        // check mandatory fields
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        // calculate a new string representation
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        // TODO add folding
290        stringRepresentation = res.toString();
291    }
292 
293}

[all classes][org.apache.james.jdkim.tagvalue]
EMMA 2.0.5312 (C) Vladimir Roubtsov