001: /*
002: * @(#)DigestHandler.java 1.2 04/12/06
003: *
004: * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * See the file "LICENSE.txt" for information on usage and redistribution
007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
008: */
009: package pnuts.xml;
010:
011: import org.xml.sax.helpers.DefaultHandler;
012: import org.xml.sax.Attributes;
013: import java.util.*;
014: import java.io.*;
015:
016: /**
017: * <code>DigestHandler</code> provides a base implementation for <code>DigestReader</code> class.
018: */
019: public class DigestHandler extends DefaultHandler {
020:
021: private SAXAttributeMap attributeMap;
022: private boolean useLocalNames;
023: private List stack;
024: private StringBuffer sbuf;
025: private Object value;
026:
027: private List valueStack;
028: private List paths;
029: private String path;
030:
031: Stack listPaths;
032: Stack listValues;
033:
034: private RuleSet ruleSet;
035:
036: /**
037: * Constructor
038: */
039: public DigestHandler() {
040: this (new SimpleRuleSet());
041: }
042:
043: public DigestHandler(RuleSet ruleSet) {
044: this .ruleSet = ruleSet;
045: }
046:
047: void setRuleSet(RuleSet ruleSet) {
048: this .ruleSet = ruleSet;
049: }
050:
051: /**
052: * Changes the setting of which of {qualified | local} names are used in
053: * callback handlers. When localName is used, the SAX parser should be
054: * namespace aware.
055: *
056: * @param useLocalNames use local names instead of quarified names.
057: * If this method is called with true, a Map object that is passed to
058: * DigestAction.start() method will contain {localName->value} mappings.
059: */
060: public void setUseLocalNames(boolean useLocalNames) {
061: this .useLocalNames = useLocalNames;
062: }
063:
064: /**
065: * Checks if the current setting uses local names instead of qualified names;
066: *
067: * @return true if the current setting uses local names instead of qualified names.
068: */
069: public boolean isUseLocalNames() {
070: return useLocalNames;
071: }
072:
073: /*
074: * The inital value.
075: */
076: public Object getValue() {
077: return value;
078: }
079:
080: /**
081: * Sets the initial value, which is to be modified during the parsing.
082: *
083: * @param value the intial value
084: */
085: public void setValue(Object value) {
086: this .value = value;
087: }
088:
089: void initialize() {
090: path = "";
091: stack = new ArrayList();
092: sbuf = new StringBuffer(64);
093: paths = new ArrayList();
094: valueStack = new ArrayList();
095: listPaths = new Stack();
096: listValues = new Stack();
097: pushValue("", value);
098: if (useLocalNames) {
099: attributeMap = new LocalNameMap();
100: } else {
101: attributeMap = new SAXAttributeMap();
102: }
103: }
104:
105: /**
106: * Adds a rule.
107: *
108: * @param action the action to be invoked at the begining/end of the element.
109: * @param path the path identifier. as a '/'-separated QName list.
110: */
111: public void addRule(DigestAction action, String path) {
112: addRule(action, path, null);
113: }
114:
115: /**
116: * Adds a rule.
117: *
118: * @param action the action to be invoked at the begining/end of the element.
119: * @param path the path identifier. as a '/'-separated QName list.
120: * @param keyword the key to access the intermediate/final result.
121: */
122: public void addRule(DigestAction action, String path, String keyword) {
123: if (action != null) {
124: action.handler = this ;
125: ruleSet.add(path, action, keyword);
126: }
127: }
128:
129: String getKey(String path) {
130: String base = getStackTopPath();
131: if (sameBranch(path, base) && base.length() + 1 < path.length()) {
132: return path.substring(base.length() + 1);
133: } else {
134: int idx = path.lastIndexOf('/');
135: if (idx > 0) {
136: return path.substring(idx + 1);
137: } else {
138: return path;
139: }
140: }
141: }
142:
143: static boolean sameBranch(String longer, String shorter) {
144: int idx = longer.indexOf(shorter);
145: if (idx < 0) {
146: return false;
147: }
148: int len2 = shorter.length();
149: if (longer.length() == len2) {
150: return true;
151: }
152: return (longer.charAt(len2) == '/');
153: }
154:
155: public void startElement(String uri, String localName,
156: String qName, Attributes attributes)
157: throws org.xml.sax.SAXException {
158: stack.add(qName);
159:
160: String _path = this .path;
161: StringBuffer sb = new StringBuffer(_path);
162: sb.append('/');
163: sb.append(qName);
164: final String path = this .path = sb.toString();
165:
166: int pos = paths.size() - 1;
167: while (pos > 0 && !sameBranch(path, (String) paths.get(pos))) {
168: popValue();
169: pos--;
170: }
171: attributeMap.setAttributes(attributes);
172: final String defaultKey = getKey(path);
173: try {
174: ruleSet.scan(path, stack, new TargetHandler() {
175: public void handle(DigestAction action, String keyword)
176: throws Exception {
177: if (keyword == null) {
178: keyword = defaultKey;
179: }
180: action.start(path, keyword, attributeMap,
181: getStackTopValue());
182: }
183: });
184: } catch (Exception e) {
185: throw new SAXException(e);
186: }
187: }
188:
189: public void endElement(String uri, String localName, String qName)
190: throws org.xml.sax.SAXException {
191: final String path = this .path;
192: String valuePath = getStackTopPath();
193:
194: int pos = paths.size() - 1;
195: while (pos > 0 && !sameBranch(path, valuePath)) {
196: popValue();
197: pos--;
198: valuePath = getStackTopPath();
199: break;
200: }
201:
202: final String defaultKey = getKey(path);
203: final String text = sbuf.toString().trim();
204: try {
205: ruleSet.scan(path, stack, new TargetHandler() {
206: public void handle(DigestAction action, String keyword)
207: throws Exception {
208: if (keyword == null) {
209: keyword = defaultKey;
210: }
211: action.end(path, keyword, text, getStackTopValue());
212: }
213: });
214: } catch (Exception e) {
215: throw new SAXException(e);
216: }
217: sbuf.setLength(0);
218:
219: stack.remove(stack.size() - 1);
220: int idx = path.lastIndexOf('/');
221: if (idx > 0) {
222: this .path = path.substring(0, idx);
223: } else {
224: this .path = "";
225: }
226: }
227:
228: Object getStackTopValue() {
229: return valueStack.get(valueStack.size() - 1);
230: }
231:
232: String getStackTopPath() {
233: return (String) paths.get(paths.size() - 1);
234: }
235:
236: void pushValue(String path, Object value) {
237: paths.add(path);
238: valueStack.add(value);
239: }
240:
241: Object popValue() {
242: paths.remove(paths.size() - 1);
243: Object value = valueStack.remove(valueStack.size() - 1);
244: if (!listValues.isEmpty() && value == listValues.peek()) {
245: listPaths.pop();
246: listValues.pop();
247: }
248: return value;
249: }
250:
251: /**
252: * Registers <em>list</em> for the specified <em>path</em>.
253: * The registered <em>list</em> is unregistered when different branch from the one
254: * the list is registered with, or an element of parent path is found by the parser.
255: */
256: public synchronized void registerListPath(String path, Object list) {
257: listPaths.push(path);
258: listValues.push(list);
259: }
260:
261: /**
262: * Checks if the list registered with <em>path</em> is still managed by the DigestReader.
263: *
264: * @param path the path
265: * @return true if it is still managed by the DigestReader.
266: */
267: public boolean listAlive(String path) {
268: return listPaths.size() > 0 && listPaths.peek().equals(path);
269: }
270:
271: /**
272: * Returns the most recent managed list.
273: *
274: * @return the list object
275: */
276: public Object currentListValue() {
277: return listValues.peek();
278: }
279:
280: public void characters(char ch[], int start, int length)
281: throws org.xml.sax.SAXException {
282: sbuf.append(ch, start, length);
283: }
284:
285: public void startDocument() throws org.xml.sax.SAXException {
286: initialize();
287: }
288: }
|