Line | Hits | Source |
---|---|---|
1 | /* | |
2 | Chrysalis Web Framework [http://chrysalis.sourceforge.net] | |
3 | Copyright (c) 2002, 2003, 2004, Paul Strack | |
4 | ||
5 | All rights reserved. | |
6 | ||
7 | Redistribution and use in source and binary forms, with or without | |
8 | modification, are permitted provided that the following conditions are met: | |
9 | ||
10 | 1. Redistributions of source code must retain the above copyright notice, this | |
11 | list of conditions and the following disclaimer. | |
12 | ||
13 | 2. Redistributions in binary form must reproduce the above copyright notice, | |
14 | this list of conditions and the following disclaimer in the documentation | |
15 | and/or other materials provided with the distribution. | |
16 | ||
17 | 3. Neither the name of the copyright holder nor the names of its contributors | |
18 | may be used to endorse or promote products derived from this software without | |
19 | specific prior written permission. | |
20 | ||
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | |
25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | */ | |
32 | ||
33 | package org.chwf.config; | |
34 | ||
35 | import java.util.HashMap; | |
36 | import java.util.LinkedList; | |
37 | import java.util.List; | |
38 | import java.util.Map; | |
39 | ||
40 | import org.chwf.util.SequencedHashMap; | |
41 | import org.xml.sax.Attributes; | |
42 | import org.xml.sax.helpers.DefaultHandler; | |
43 | ||
44 | /** | |
45 | * Converts XML file data to a hash map, with keys based on the Xpath, | |
46 | * omitting the root tag. | |
47 | * | |
48 | * @author <a href="mailto:pfstrack@users.sourceforge.net">Paul Strack</a> | |
49 | */ | |
50 | 404 | class XMLDigester extends DefaultHandler { |
51 | ||
52 | /** Property value XML attribute. */ | |
53 | private static final String PROPERTY_ATTRIBUTE = "value"; | |
54 | ||
55 | /** Property name XML attribute. */ | |
56 | private static final String NAME_ATTRIBUTE = "name"; | |
57 | ||
58 | /** Character delimiting sub-names in configuration keys. */ | |
59 | private static final char KEY_SEPARATOR = '.'; | |
60 | ||
61 | /** Null character. */ | |
62 | private static final char NULL_CHAR = '\u0000'; | |
63 | ||
64 | /** Map containing configuration data. */ | |
65 | 202 | private Map data = new SequencedHashMap(); |
66 | ||
67 | /** Map tracking duplicate elements. */ | |
68 | 202 | private Map duplicateCount = new HashMap(); |
69 | ||
70 | /** Current element path being processed. */ | |
71 | 202 | private List path = new LinkedList(); |
72 | ||
73 | /** Current property value. */ | |
74 | 202 | private String propertyValue = null; |
75 | ||
76 | /** StringBuffer (reused). */ | |
77 | 202 | private StringBuffer buffer = new StringBuffer(); |
78 | ||
79 | /** Flag indicating whether parser is current inside a tag. */ | |
80 | 202 | private boolean inTag = false; |
81 | ||
82 | /** | |
83 | * Called at the start tag. Sets up for property retrieval. | |
84 | * | |
85 | * @param namespaceURI The namespace URI. | |
86 | * @param localName The local name (without prefix). | |
87 | * @param qName The qualified name (with prefix). | |
88 | * @param atts The attributes attached to the element. | |
89 | */ | |
90 | public void startElement( | |
91 | String namespaceURI, | |
92 | String localName, | |
93 | String qName, | |
94 | Attributes atts) { | |
95 | ||
96 | 9041 | inTag = true; |
97 | ||
98 | // Push tag name into path: | |
99 | 9041 | String name = atts.getValue(NAME_ATTRIBUTE); |
100 | 9041 | if (name != null) { |
101 | 7799 | path.add(name); // Add name attribute |
102 | } else { | |
103 | 1242 | path.add(qName); // Add tag name |
104 | } | |
105 | ||
106 | // Set property to value of property attribute, if any: | |
107 | 9041 | propertyValue = atts.getValue(PROPERTY_ATTRIBUTE); |
108 | 9041 | if (propertyValue != null) { |
109 | 110 | addProperty(); |
110 | } | |
111 | ||
112 | // Add properties for other attributes | |
113 | 22753 | for (int i = 0; i < atts.getLength(); i++) { |
114 | 13712 | String att = atts.getQName(i); |
115 | 13712 | if (!att.equals(NAME_ATTRIBUTE) && !att.equals(PROPERTY_ATTRIBUTE)) { |
116 | 5803 | propertyValue = atts.getValue(i); |
117 | 5803 | path.add(att); |
118 | 5803 | addProperty(); |
119 | 5803 | path.remove(att); |
120 | 5803 | propertyValue = null; |
121 | } | |
122 | } | |
123 | ||
124 | // Clear buffer before adding tag contents: | |
125 | 9041 | buffer.setLength(0); |
126 | 9041 | } |
127 | ||
128 | /** | |
129 | * Called for characters. Stores char data in buffer. | |
130 | * | |
131 | * @param ch The characters from the XML document. | |
132 | * @param start The start position in the array. | |
133 | * @param length The number of characters to read from the array. | |
134 | */ | |
135 | public void characters(char[] ch, int start, int length) { | |
136 | 13907 | if (textIsNeeded()) { |
137 | 5051 | buffer.append(ch, start, length); // Add text to buffer |
138 | } | |
139 | 13907 | } |
140 | ||
141 | /** | |
142 | * Called at the end tag. Save property value. | |
143 | * | |
144 | * @param namespaceURI The namespace URI. | |
145 | * @param localName The local name (without prefix). | |
146 | * @param qName The qualified name (with prefix). | |
147 | */ | |
148 | public void endElement(String namespaceURI, String localName, String qName) { | |
149 | // Retrieve the buffer, if need: | |
150 | 9041 | if (textIsNeeded() && (buffer.length() > 0)) { |
151 | 1473 | propertyValue = buffer.toString(); |
152 | 1473 | addProperty(); |
153 | } | |
154 | ||
155 | // Clean up data: | |
156 | 9041 | propertyValue = null; |
157 | 9041 | inTag = false; |
158 | ||
159 | // Pop last name in path: | |
160 | 9041 | path.remove(path.size() - 1); |
161 | 9041 | } |
162 | ||
163 | /** | |
164 | * Retrieve the finished data map. Use this after parsing finishes. | |
165 | * | |
166 | * @return The map holding accumulated data. | |
167 | */ | |
168 | public Map getData() { | |
169 | 202 | return this.data; |
170 | } | |
171 | ||
172 | /** | |
173 | * True if in a tag and a property has not been set yet. | |
174 | * | |
175 | * @return Whether tag text is needed. | |
176 | */ | |
177 | private boolean textIsNeeded() { | |
178 | 22948 | return inTag && (propertyValue == null); |
179 | } | |
180 | ||
181 | /** Add property to the hashmap, converting the path to a dot-separate key. */ | |
182 | private void addProperty() { | |
183 | 7386 | String propertyName = derivePropertyName(); |
184 | 7386 | if (data.get(propertyName) == null) { |
185 | 5979 | data.put(propertyName, propertyValue); // Store property |
186 | } else { // Duplicate property | |
187 | 1407 | Integer count = (Integer) duplicateCount.get(propertyName); |
188 | 1407 | if (count == null) { |
189 | 525 | count = new Integer(2); |
190 | } else { | |
191 | 882 | count = new Integer(count.intValue() + 1); |
192 | } | |
193 | 1407 | duplicateCount.put(propertyName, count); |
194 | 1407 | String revisedName = |
195 | propertyName + KEY_SEPARATOR + NULL_CHAR + (char) count.intValue(); | |
196 | 1407 | data.put(revisedName, propertyValue); |
197 | } | |
198 | 7386 | } |
199 | ||
200 | /** | |
201 | * Derive property name from current path. | |
202 | * | |
203 | * @return The property name. | |
204 | */ | |
205 | private String derivePropertyName() { | |
206 | 7386 | buffer.setLength(0); // Clear buffer |
207 | ||
208 | // Start loop at 1 to skip root element name: | |
209 | 31388 | for (int i = 1; i < path.size(); i++) { |
210 | 24002 | buffer.append(path.get(i)); |
211 | 24002 | buffer.append(KEY_SEPARATOR); |
212 | } | |
213 | ||
214 | // Strip final and extraneous KEY_SEPARATOR: | |
215 | 7386 | if (buffer.length() > 0) { |
216 | 7386 | buffer.setLength(buffer.length() - 1); |
217 | } | |
218 | ||
219 | 7386 | String propertyName = buffer.toString(); |
220 | 7386 | return propertyName; |
221 | } | |
222 | } |
this report was generated by version 1.0.5 of jcoverage. |
copyright © 2003, jcoverage ltd. all rights reserved. |