KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > taglibs > standard > extra > spath > SPathFilter


1 /*
2  * Copyright 1999,2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.taglibs.standard.extra.spath;
18
19 import java.io.IOException JavaDoc;
20 import java.util.List JavaDoc;
21 import java.util.Stack JavaDoc;
22
23 import org.apache.xalan.serialize.Serializer;
24 import org.apache.xalan.serialize.SerializerFactory;
25 import org.apache.xalan.templates.OutputProperties;
26 import org.xml.sax.Attributes JavaDoc;
27 import org.xml.sax.InputSource JavaDoc;
28 import org.xml.sax.SAXException JavaDoc;
29 import org.xml.sax.XMLFilter JavaDoc;
30 import org.xml.sax.XMLReader JavaDoc;
31 import org.xml.sax.helpers.XMLFilterImpl JavaDoc;
32 import org.xml.sax.helpers.XMLReaderFactory JavaDoc;
33
34 /**
35  * <p>Filters a SAX stream based on a single supplied SPath
36  * expression.</p>
37  *
38  * @author Shawn Bayern
39  */

40 public class SPathFilter extends XMLFilterImpl JavaDoc {
41
42     //*********************************************************************
43
// Protected state
44

45     /** The steps in the SPath expression we use for filtering. */
46     protected List JavaDoc steps;
47
48     //*********************************************************************
49
// Private state in support of filtering
50

51     private int depth; // depth in parsed document
52
private Stack JavaDoc acceptedDepths; // depth of acceptance
53
private int excludedDepth; // depth of exclusion
54

55     private static final boolean DEBUG = false;
56
57     //*********************************************************************
58
// Main method (for testing)
59

60     /** Simple command-line interface, mostly for testing. */
61     public static void main(String JavaDoc args[])
62         throws ParseException, IOException JavaDoc, SAXException JavaDoc {
63 // temporary...
64
System.setProperty("org.xml.sax.driver", "org.apache.xerces.parsers.SAXParser");
65
66     // retrieve and parse the expression
67
String JavaDoc expr = args[0];
68     SPathParser s = new SPathParser(expr);
69     Path p = s.expression();
70
71     // construct the appropriate SAX chain
72
// (reader -> us -> serializer)
73
XMLReader JavaDoc r = XMLReaderFactory.createXMLReader();
74     XMLFilter JavaDoc f1 = new SPathFilter(p);
75     XMLFilter JavaDoc f2 = new XMLFilterImpl JavaDoc();
76     f1.setParent(r);
77     f2.setParent(f1);
78     Serializer sz = SerializerFactory.getSerializer
79         (OutputProperties.getDefaultMethodProperties("xml"));
80     sz.setOutputStream(System.out);
81     f2.setContentHandler(sz.asContentHandler());
82
83     // go!
84
f2.parse(new InputSource JavaDoc(System.in));
85     System.out.println();
86     }
87
88     //*********************************************************************
89
// Constructor and initialization methods
90

91     /** Constructs a new SPathFilter, given a Path. */
92     public SPathFilter(Path path) {
93     init();
94     this.steps = path.getSteps();
95     }
96
97     /** Initializes state used for filtering. */
98     private void init() {
99     depth = 0;
100     excludedDepth = -1;
101     acceptedDepths = new Stack JavaDoc();
102     }
103
104     //*********************************************************************
105
// ContentHandler methods
106

107     // startElement() and endElement() both require and modify filter
108
// state. They contain and direct the bulk of the filter's operation.
109

110     /** Filter for startElement(). */
111     public void startElement(String JavaDoc uri,
112                  String JavaDoc localName,
113                  String JavaDoc qName,
114                  Attributes JavaDoc a) throws SAXException JavaDoc {
115     // always update the depth
116
depth++;
117
118     // if we're in an accepted section, simply pass through
119
if (isAccepted()) {
120         getContentHandler().startElement(uri, localName, qName, a);
121         return;
122     }
123
124     // likewise, if we're excluded, then simply block and return
125
if (isExcluded())
126         return;
127
128     // now, not accepted or excluded, let's see if we've got a match.
129
// we need to get the appropriate step based on the number of
130
// steps we've previously accepted
131
Step currentStep = (Step) steps.get(acceptedDepths.size());
132
133     if (nodeMatchesStep(currentStep, uri, localName, qName, a)) {
134         if (DEBUG)
135         System.err.println("*** Progressive match (" + acceptedDepths.size() + "): " + localName);
136         // new match (progressive)
137
acceptedDepths.push(new Integer JavaDoc(depth - 1));
138
139         // is it enough? give acceptance another chance...
140
if (isAccepted())
141             getContentHandler().startElement(uri, localName, qName, a);
142     } else if (!currentStep.isDepthUnlimited()) {
143         // if the step was preceded by '/' instead of '//', then
144
// we can't have a match at this node or beneath it
145
excludedDepth = depth - 1;
146     }
147
148     // nothing left to check; no reason to include node
149
return;
150     }
151
152     /** Filter for endElement(). */
153     public void endElement(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName)
154         throws SAXException JavaDoc {
155     // reduce the depth
156
depth--;
157
158     if (isExcluded()) {
159         // determine if exclusion ends with us
160
if (excludedDepth == depth)
161             excludedDepth = -1;
162
163         // either way, we have been excluded, so pass nothing through
164
return;
165     }
166
167     // if we're excepted (for now), include ourselves...
168
if (isAccepted())
169         getContentHandler().endElement(uri, localName, qName);
170
171         if (DEBUG) {
172         System.err.println("*** Closing tag: " + localName);
173         System.err.println("*** acceptedDepths.size(): " + acceptedDepths.size());
174         System.err.println("*** last accepted depth: " + ((Integer JavaDoc)acceptedDepths.peek()).intValue());
175         System.err.println("*** depth: " + depth);
176         }
177
178     // now, back off if we correspond to a "successful" start tag
179
if (acceptedDepths.size() > 0 &&
180         (((Integer JavaDoc)acceptedDepths.peek()).intValue()) == depth)
181         acceptedDepths.pop();
182     }
183
184     // The remaining ContentHandler functions require only one bit of
185
// state: are we in a mode where we pass them through, or does
186
// the current state dictate that we ignore them. They need no other
187
// information and cannot have any effect on the current state.
188

189     /** Filter for ignoreableWhitespace(). */
190     public void ignorableWhitespace(char[] ch, int start, int length)
191         throws SAXException JavaDoc {
192     if (isAccepted())
193         getContentHandler().ignorableWhitespace(ch, start, length);
194     }
195
196     /** Filter for characters(). */
197     public void characters(char[] ch, int start, int length)
198         throws SAXException JavaDoc {
199     if (isAccepted())
200         getContentHandler().characters(ch, start, length);
201     }
202
203     /** Filter for startPrefixMapping(). */
204     public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri)
205         throws SAXException JavaDoc {
206     if (isAccepted())
207         getContentHandler().startPrefixMapping(prefix, uri);
208     }
209
210     /** Filter for endPrefixMapping(). */
211     public void endPrefixMapping(String JavaDoc prefix)
212         throws SAXException JavaDoc {
213     if (isAccepted())
214         getContentHandler().endPrefixMapping(prefix);
215     }
216
217     /** Filter for processingInstruction(). */
218     public void processingInstruction(String JavaDoc target, String JavaDoc data)
219         throws SAXException JavaDoc {
220     if (isAccepted())
221         getContentHandler().processingInstruction(target, data);
222     }
223
224     /** Filter for skippedEntity(). */
225     public void skippedEntity(String JavaDoc name) throws SAXException JavaDoc {
226     if (isAccepted())
227         getContentHandler().skippedEntity(name);
228     }
229
230     // We reset state in startDocument(), in case we're reused
231
/** Resets state. */
232     public void startDocument() {
233     init();
234     }
235
236     //*********************************************************************
237
// Private utility methods
238

239     public static boolean nodeMatchesStep(Step s,
240                   String JavaDoc uri,
241                   String JavaDoc localName,
242                   String JavaDoc qName,
243                   Attributes JavaDoc a) {
244     // if the name doesn't match, then we've got a loser
245
if (!s.isMatchingName(uri, localName))
246         return false;
247
248     // it's still in the game; check the predicates
249
List JavaDoc l = s.getPredicates();
250     for (int i = 0; l != null && i < l.size(); i++) {
251         Predicate p = (Predicate) l.get(i);
252         if (!(p instanceof AttributePredicate))
253         throw new UnsupportedOperationException JavaDoc
254             ("only attribute predicates are supported by filter");
255         if (!((AttributePredicate) p).isMatchingAttribute(a))
256         return false; // all predicates must match
257
}
258
259     // it's survived
260
return true;
261     }
262
263     /** Returns true if events should be passed through, false otherwise. */
264     private boolean isAccepted() {
265     return (acceptedDepths.size() >= steps.size());
266     }
267
268     /** Returns true if events should be blocked, false otherwise. */
269     private boolean isExcluded() {
270     return (excludedDepth != -1);
271     }
272 }
273
Popular Tags