Source code: com/nwalsh/saxon/UnwrapLinksEmitter.java
1 package com.nwalsh.saxon;
2
3 import java.util.Stack;
4 import org.xml.sax.*;
5 import org.w3c.dom.*;
6 import javax.xml.transform.TransformerException;
7 import com.icl.saxon.output.*;
8 import com.icl.saxon.om.*;
9 import com.icl.saxon.Controller;
10 import com.icl.saxon.tree.AttributeCollection;
11
12 /**
13 * <p>Saxon extension to unwrap links in a result tree fragment.</p>
14 *
15 * <p>$Id: UnwrapLinksEmitter.java,v 1.1 2002/06/26 11:02:05 nwalsh Exp $</p>
16 *
17 * <p>Copyright (C) 2000, 2002 Norman Walsh.</p>
18 *
19 * <p>This class provides the guts of a
20 * <a href="http://saxon.sf.net/">Saxon 6.*</a>
21 * implementation of a link unwrapper.</p>
22 *
23 * <p>The general design is this: the stylesheets construct a result tree
24 * fragment for some environment. Then the result tree fragment
25 * is "replayed" through the UnwrapLinksEmitter; the UnwrapLinksEmitter
26 * builds a
27 * new result tree fragment from this event stream with top-level links unwrapped.
28 * That RTF is returned. Note that only a <i>single</i> level of unwrapping
29 * is performed. This is clearly a crude implementation.
30 * </p>
31 *
32 * <p><b>Change Log:</b></p>
33 * <dl>
34 * <dt>1.0</dt>
35 * <dd><p>Initial release.</p></dd>
36 * </dl>
37 *
38 * @author Norman Walsh
39 * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
40 *
41 * @version $Id: UnwrapLinksEmitter.java,v 1.1 2002/06/26 11:02:05 nwalsh Exp $
42 *
43 */
44 public class UnwrapLinksEmitter extends CopyEmitter {
45 /** A stack for the preserving information about open elements. */
46 protected Stack elementStack = null;
47 protected Stack saveStack = null;
48
49 /** The FO namespace name. */
50 protected static String foURI = "http://www.w3.org/1999/XSL/Format";
51
52 /** The XHTML namespace name. */
53 protected static String xhURI = "http://www.w3.org/1999/xhtml";
54
55 /** Is the stylesheet currently running an FO stylesheet? */
56 protected boolean foStylesheet = false;
57
58 /** Are we currently in a link? How deep? */
59 protected int linkDepth = 0;
60 protected int skipDepth = 0;
61
62 protected int htmlAFingerprint = 0;
63 protected int xhtmlAFingerprint = 0;
64 protected boolean inSkip = false;
65 protected boolean tryAgain = false;
66
67
68 /** <p>Constructor for the UnwrapLinksEmitter.</p>
69 *
70 * @param namePool The name pool to use for constructing elements and attributes.
71 * @param foStylesheet Is this an FO stylesheet?
72 */
73 public UnwrapLinksEmitter(Controller controller,
74 NamePool namePool,
75 boolean foStylesheet) {
76 super(controller,namePool);
77 elementStack = new Stack();
78 this.foStylesheet = foStylesheet;
79
80 htmlAFingerprint = namePool.getFingerprint("", "a");
81 xhtmlAFingerprint = namePool.getFingerprint(xhURI, "a");
82 }
83
84 /** Process start element events. */
85 public void startElement(int nameCode,
86 org.xml.sax.Attributes attributes,
87 int[] namespaces,
88 int nscount)
89 throws TransformerException {
90
91 int thisFingerprint = namePool.getFingerprint(nameCode);
92 boolean isLink = (thisFingerprint == htmlAFingerprint
93 || thisFingerprint == xhtmlAFingerprint);
94
95 if (isLink) {
96 linkDepth++;
97 tryAgain = tryAgain || inSkip;
98 }
99
100 if (isLink && linkDepth > 1 && !inSkip) {
101 inSkip = true;
102
103 // Close all the open elements
104 saveStack = new Stack();
105 Stack tempStack = new Stack();
106 while (!elementStack.empty()) {
107 StartElementInfo elem = (StartElementInfo) elementStack.pop();
108 rtfEmitter.endElement(elem.getNameCode());
109 saveStack.push(elem);
110 tempStack.push(elem);
111 }
112
113 while (!tempStack.empty()) {
114 StartElementInfo elem = (StartElementInfo) tempStack.pop();
115 elementStack.push(elem);
116 }
117 }
118
119 if (inSkip) {
120 skipDepth++;
121 } else {
122 }
123
124 rtfEmitter.startElement(nameCode,attributes,namespaces,nscount);
125
126 StartElementInfo sei = new StartElementInfo(nameCode, attributes,
127 namespaces, nscount);
128 elementStack.push(sei);
129 }
130
131 /** Process end element events. */
132 public void endElement(int nameCode) throws TransformerException {
133 int thisFingerprint = namePool.getFingerprint(nameCode);
134 boolean isLink = (thisFingerprint == htmlAFingerprint
135 || thisFingerprint == xhtmlAFingerprint);
136
137 rtfEmitter.endElement(nameCode);
138 elementStack.pop();
139
140 if (isLink) {
141 linkDepth--;
142 }
143
144 if (inSkip) {
145 skipDepth--;
146 inSkip = (skipDepth > 0);
147 if (!inSkip) {
148 // Reopen all the ones we closed before...
149 while (!saveStack.empty()) {
150 StartElementInfo elem = (StartElementInfo) saveStack.pop();
151
152 AttributeCollection attr = (AttributeCollection)elem.getAttributes();
153 AttributeCollection newAttr = new AttributeCollection(namePool);
154
155 for (int acount = 0; acount < attr.getLength(); acount++) {
156 String localName = attr.getLocalName(acount);
157 String type = attr.getType(acount);
158 String value = attr.getValue(acount);
159 String uri = attr.getURI(acount);
160 String prefix = "";
161
162 if (localName.indexOf(':') > 0) {
163 prefix = localName.substring(0, localName.indexOf(':'));
164 localName = localName.substring(localName.indexOf(':')+1);
165 }
166
167 if (uri.equals("")
168 && ((foStylesheet
169 && localName.equals("id"))
170 || (!foStylesheet
171 && (localName.equals("id")
172 || localName.equals("name"))))) {
173 // skip this attribute
174 } else {
175 newAttr.addAttribute(prefix, uri, localName, type, value);
176 }
177 }
178
179 rtfEmitter.startElement(elem.getNameCode(),
180 newAttr,
181 elem.getNamespaces(),
182 elem.getNSCount());
183 }
184 }
185 }
186 }
187
188 public boolean tryAgain()
189 throws TransformerException {
190 return tryAgain;
191 }
192
193 /**
194 * <p>A private class for maintaining the information required to call
195 * the startElement method.</p>
196 *
197 * <p>In order to close and reopen elements, information about those
198 * elements has to be maintained. This class is just the little record
199 * that we push on the stack to keep track of that info.</p>
200 */
201 private class StartElementInfo {
202 private int _nameCode;
203 org.xml.sax.Attributes _attributes;
204 int[] _namespaces;
205 int _nscount;
206
207 public StartElementInfo(int nameCode,
208 org.xml.sax.Attributes attributes,
209 int[] namespaces,
210 int nscount) {
211 _nameCode = nameCode;
212 _attributes = attributes;
213 _namespaces = namespaces;
214 _nscount = nscount;
215 }
216
217 public int getNameCode() {
218 return _nameCode;
219 }
220
221 public org.xml.sax.Attributes getAttributes() {
222 return _attributes;
223 }
224
225 public int[] getNamespaces() {
226 return _namespaces;
227 }
228
229 public int getNSCount() {
230 return _nscount;
231 }
232 }
233 }