001: package com.nwalsh.saxon;
002:
003: import java.util.Stack;
004: import org.xml.sax.*;
005: import org.w3c.dom.*;
006: import javax.xml.transform.TransformerException;
007: import com.icl.saxon.output.*;
008: import com.icl.saxon.om.*;
009: import com.icl.saxon.Controller;
010: import com.icl.saxon.tree.AttributeCollection;
011:
012: /**
013: * <p>Saxon extension to unwrap links in a result tree fragment.</p>
014: *
015: * <p>$Id: UnwrapLinksEmitter.java,v 1.4 2005-08-30 08:14:58 draganr Exp $</p>
016: *
017: * <p>Copyright (C) 2000, 2002 Norman Walsh.</p>
018: *
019: * <p>This class provides the guts of a
020: * <a href="http://saxon.sf.net/">Saxon 6.*</a>
021: * implementation of a link unwrapper.</p>
022: *
023: * <p>The general design is this: the stylesheets construct a result tree
024: * fragment for some environment. Then the result tree fragment
025: * is "replayed" through the UnwrapLinksEmitter; the UnwrapLinksEmitter
026: * builds a
027: * new result tree fragment from this event stream with top-level links unwrapped.
028: * That RTF is returned. Note that only a <i>single</i> level of unwrapping
029: * is performed. This is clearly a crude implementation.
030: * </p>
031: *
032: * <p><b>Change Log:</b></p>
033: * <dl>
034: * <dt>1.0</dt>
035: * <dd><p>Initial release.</p></dd>
036: * </dl>
037: *
038: * @author Norman Walsh
039: * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
040: *
041: * @version $Id: UnwrapLinksEmitter.java,v 1.4 2005-08-30 08:14:58 draganr Exp $
042: *
043: */
044: public class UnwrapLinksEmitter extends CopyEmitter {
045: /** A stack for the preserving information about open elements. */
046: protected Stack elementStack = null;
047: protected Stack saveStack = null;
048:
049: /** The FO namespace name. */
050: protected static String foURI = "http://www.w3.org/1999/XSL/Format";
051:
052: /** The XHTML namespace name. */
053: protected static String xhURI = "http://www.w3.org/1999/xhtml";
054:
055: /** Is the stylesheet currently running an FO stylesheet? */
056: protected boolean foStylesheet = false;
057:
058: /** Are we currently in a link? How deep? */
059: protected int linkDepth = 0;
060: protected int skipDepth = 0;
061:
062: protected int htmlAFingerprint = 0;
063: protected int xhtmlAFingerprint = 0;
064: protected boolean inSkip = false;
065: protected boolean tryAgain = false;
066:
067: /** <p>Constructor for the UnwrapLinksEmitter.</p>
068: *
069: * @param namePool The name pool to use for constructing elements and attributes.
070: * @param foStylesheet Is this an FO stylesheet?
071: */
072: public UnwrapLinksEmitter(Controller controller, NamePool namePool,
073: boolean foStylesheet) {
074: super (controller, namePool);
075: elementStack = new Stack();
076: this .foStylesheet = foStylesheet;
077:
078: htmlAFingerprint = namePool.getFingerprint("", "a");
079: xhtmlAFingerprint = namePool.getFingerprint(xhURI, "a");
080: }
081:
082: /** Process start element events. */
083: public void startElement(int nameCode,
084: org.xml.sax.Attributes attributes, int[] namespaces,
085: int nscount) throws TransformerException {
086:
087: int this Fingerprint = namePool.getFingerprint(nameCode);
088: boolean isLink = (this Fingerprint == htmlAFingerprint || this Fingerprint == xhtmlAFingerprint);
089:
090: if (isLink) {
091: linkDepth++;
092: tryAgain = tryAgain || inSkip;
093: }
094:
095: if (isLink && linkDepth > 1 && !inSkip) {
096: inSkip = true;
097:
098: // Close all the open elements
099: saveStack = new Stack();
100: Stack tempStack = new Stack();
101: while (!elementStack.empty()) {
102: StartElementInfo elem = (StartElementInfo) elementStack
103: .pop();
104: rtfEmitter.endElement(elem.getNameCode());
105: saveStack.push(elem);
106: tempStack.push(elem);
107: }
108:
109: while (!tempStack.empty()) {
110: StartElementInfo elem = (StartElementInfo) tempStack
111: .pop();
112: elementStack.push(elem);
113: }
114: }
115:
116: if (inSkip) {
117: skipDepth++;
118: } else {
119: }
120:
121: rtfEmitter.startElement(nameCode, attributes, namespaces,
122: nscount);
123:
124: StartElementInfo sei = new StartElementInfo(nameCode,
125: attributes, namespaces, nscount);
126: elementStack.push(sei);
127: }
128:
129: /** Process end element events. */
130: public void endElement(int nameCode) throws TransformerException {
131: int this Fingerprint = namePool.getFingerprint(nameCode);
132: boolean isLink = (this Fingerprint == htmlAFingerprint || this Fingerprint == xhtmlAFingerprint);
133:
134: rtfEmitter.endElement(nameCode);
135: elementStack.pop();
136:
137: if (isLink) {
138: linkDepth--;
139: }
140:
141: if (inSkip) {
142: skipDepth--;
143: inSkip = (skipDepth > 0);
144: if (!inSkip) {
145: // Reopen all the ones we closed before...
146: while (!saveStack.empty()) {
147: StartElementInfo elem = (StartElementInfo) saveStack
148: .pop();
149:
150: AttributeCollection attr = (AttributeCollection) elem
151: .getAttributes();
152: AttributeCollection newAttr = new AttributeCollection(
153: 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
164: .indexOf(':'));
165: localName = localName.substring(localName
166: .indexOf(':') + 1);
167: }
168:
169: if (uri.equals("")
170: && ((foStylesheet && localName
171: .equals("id")) || (!foStylesheet && (localName
172: .equals("id") || localName
173: .equals("name"))))) {
174: // skip this attribute
175: } else {
176: newAttr.addAttribute(prefix, uri,
177: localName, type, value);
178: }
179: }
180:
181: rtfEmitter.startElement(elem.getNameCode(),
182: newAttr, elem.getNamespaces(), elem
183: .getNSCount());
184: }
185: }
186: }
187: }
188:
189: public boolean tryAgain() 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, int[] namespaces,
209: int nscount) {
210: _nameCode = nameCode;
211: _attributes = attributes;
212: _namespaces = namespaces;
213: _nscount = nscount;
214: }
215:
216: public int getNameCode() {
217: return _nameCode;
218: }
219:
220: public org.xml.sax.Attributes getAttributes() {
221: return _attributes;
222: }
223:
224: public int[] getNamespaces() {
225: return _namespaces;
226: }
227:
228: public int getNSCount() {
229: return _nscount;
230: }
231: }
232: }
|