package com.nwalsh.saxon; import java.util.Stack; import org.xml.sax.*; import org.w3c.dom.*; import javax.xml.transform.TransformerException; import com.icl.saxon.output.*; import com.icl.saxon.om.*; import com.icl.saxon.Controller; import com.icl.saxon.tree.AttributeCollection; /** *

Saxon extension to unwrap links in a result tree fragment.

* *

$Id: UnwrapLinksEmitter.java,v 1.1 2002/06/26 11:02:05 nwalsh Exp $

* *

Copyright (C) 2000, 2002 Norman Walsh.

* *

This class provides the guts of a * Saxon 6.* * implementation of a link unwrapper.

* *

The general design is this: the stylesheets construct a result tree * fragment for some environment. Then the result tree fragment * is "replayed" through the UnwrapLinksEmitter; the UnwrapLinksEmitter * builds a * new result tree fragment from this event stream with top-level links unwrapped. * That RTF is returned. Note that only a single level of unwrapping * is performed. This is clearly a crude implementation. *

* *

Change Log:

*
*
1.0
*

Initial release.

*
* * @author Norman Walsh * ndw@nwalsh.com * * @version $Id: UnwrapLinksEmitter.java,v 1.1 2002/06/26 11:02:05 nwalsh Exp $ * */ public class UnwrapLinksEmitter extends CopyEmitter { /** A stack for the preserving information about open elements. */ protected Stack elementStack = null; protected Stack saveStack = null; /** The FO namespace name. */ protected static String foURI = "http://www.w3.org/1999/XSL/Format"; /** The XHTML namespace name. */ protected static String xhURI = "http://www.w3.org/1999/xhtml"; /** Is the stylesheet currently running an FO stylesheet? */ protected boolean foStylesheet = false; /** Are we currently in a link? How deep? */ protected int linkDepth = 0; protected int skipDepth = 0; protected int htmlAFingerprint = 0; protected int xhtmlAFingerprint = 0; protected boolean inSkip = false; protected boolean tryAgain = false; /**

Constructor for the UnwrapLinksEmitter.

* * @param namePool The name pool to use for constructing elements and attributes. * @param foStylesheet Is this an FO stylesheet? */ public UnwrapLinksEmitter(Controller controller, NamePool namePool, boolean foStylesheet) { super(controller,namePool); elementStack = new Stack(); this.foStylesheet = foStylesheet; htmlAFingerprint = namePool.getFingerprint("", "a"); xhtmlAFingerprint = namePool.getFingerprint(xhURI, "a"); } /** Process start element events. */ public void startElement(int nameCode, org.xml.sax.Attributes attributes, int[] namespaces, int nscount) throws TransformerException { int thisFingerprint = namePool.getFingerprint(nameCode); boolean isLink = (thisFingerprint == htmlAFingerprint || thisFingerprint == xhtmlAFingerprint); if (isLink) { linkDepth++; tryAgain = tryAgain || inSkip; } if (isLink && linkDepth > 1 && !inSkip) { inSkip = true; // Close all the open elements saveStack = new Stack(); Stack tempStack = new Stack(); while (!elementStack.empty()) { StartElementInfo elem = (StartElementInfo) elementStack.pop(); rtfEmitter.endElement(elem.getNameCode()); saveStack.push(elem); tempStack.push(elem); } while (!tempStack.empty()) { StartElementInfo elem = (StartElementInfo) tempStack.pop(); elementStack.push(elem); } } if (inSkip) { skipDepth++; } else { } rtfEmitter.startElement(nameCode,attributes,namespaces,nscount); StartElementInfo sei = new StartElementInfo(nameCode, attributes, namespaces, nscount); elementStack.push(sei); } /** Process end element events. */ public void endElement(int nameCode) throws TransformerException { int thisFingerprint = namePool.getFingerprint(nameCode); boolean isLink = (thisFingerprint == htmlAFingerprint || thisFingerprint == xhtmlAFingerprint); rtfEmitter.endElement(nameCode); elementStack.pop(); if (isLink) { linkDepth--; } if (inSkip) { skipDepth--; inSkip = (skipDepth > 0); if (!inSkip) { // Reopen all the ones we closed before... while (!saveStack.empty()) { StartElementInfo elem = (StartElementInfo) saveStack.pop(); AttributeCollection attr = (AttributeCollection)elem.getAttributes(); AttributeCollection newAttr = new AttributeCollection(namePool); for (int acount = 0; acount < attr.getLength(); acount++) { String localName = attr.getLocalName(acount); String type = attr.getType(acount); String value = attr.getValue(acount); String uri = attr.getURI(acount); String prefix = ""; if (localName.indexOf(':') > 0) { prefix = localName.substring(0, localName.indexOf(':')); localName = localName.substring(localName.indexOf(':')+1); } if (uri.equals("") && ((foStylesheet && localName.equals("id")) || (!foStylesheet && (localName.equals("id") || localName.equals("name"))))) { // skip this attribute } else { newAttr.addAttribute(prefix, uri, localName, type, value); } } rtfEmitter.startElement(elem.getNameCode(), newAttr, elem.getNamespaces(), elem.getNSCount()); } } } } public boolean tryAgain() throws TransformerException { return tryAgain; } /** *

A private class for maintaining the information required to call * the startElement method.

* *

In order to close and reopen elements, information about those * elements has to be maintained. This class is just the little record * that we push on the stack to keep track of that info.

*/ private class StartElementInfo { private int _nameCode; org.xml.sax.Attributes _attributes; int[] _namespaces; int _nscount; public StartElementInfo(int nameCode, org.xml.sax.Attributes attributes, int[] namespaces, int nscount) { _nameCode = nameCode; _attributes = attributes; _namespaces = namespaces; _nscount = nscount; } public int getNameCode() { return _nameCode; } public org.xml.sax.Attributes getAttributes() { return _attributes; } public int[] getNamespaces() { return _namespaces; } public int getNSCount() { return _nscount; } } }