From 1a7c03891808dda37bee059ecb929e4044b5e191 Mon Sep 17 00:00:00 2001 From: Norman Walsh Date: Wed, 26 Jun 2002 11:03:05 +0000 Subject: [PATCH] Unwrap nested links --- .../.classes/com/nwalsh/saxon/Makefile.incl | 2 + .../com/nwalsh/saxon/UnwrapLinks.java | 131 ++++++++++ .../com/nwalsh/saxon/UnwrapLinksEmitter.java | 233 ++++++++++++++++++ xsl/html/inline.xsl | 114 +++++---- xsl/html/xref.xsl | 57 +++-- 5 files changed, 463 insertions(+), 74 deletions(-) create mode 100644 xsl/extensions/saxon643/com/nwalsh/saxon/UnwrapLinks.java create mode 100644 xsl/extensions/saxon643/com/nwalsh/saxon/UnwrapLinksEmitter.java diff --git a/xsl/extensions/saxon643/.classes/com/nwalsh/saxon/Makefile.incl b/xsl/extensions/saxon643/.classes/com/nwalsh/saxon/Makefile.incl index de568b476..8842cd369 100644 --- a/xsl/extensions/saxon643/.classes/com/nwalsh/saxon/Makefile.incl +++ b/xsl/extensions/saxon643/.classes/com/nwalsh/saxon/Makefile.incl @@ -14,5 +14,7 @@ NumberLinesEmitter.class \ Table.class \ Text.class \ TextFactory.class \ +UnwrapLinks.class \ +UnwrapLinksEmitter.class \ Verbatim.class diff --git a/xsl/extensions/saxon643/com/nwalsh/saxon/UnwrapLinks.java b/xsl/extensions/saxon643/com/nwalsh/saxon/UnwrapLinks.java new file mode 100644 index 000000000..ec1a337b1 --- /dev/null +++ b/xsl/extensions/saxon643/com/nwalsh/saxon/UnwrapLinks.java @@ -0,0 +1,131 @@ +// UnwrapLinks.java - Saxon extension for unwrapping nested links + +package com.nwalsh.saxon; + +import java.util.Stack; +import java.util.StringTokenizer; +import org.xml.sax.*; +import org.w3c.dom.*; +import javax.xml.transform.TransformerException; +import com.icl.saxon.Controller; +import com.icl.saxon.expr.*; +import com.icl.saxon.om.*; +import com.icl.saxon.pattern.*; +import com.icl.saxon.Context; +import com.icl.saxon.tree.*; +import com.icl.saxon.functions.Extensions; +import com.nwalsh.saxon.UnwrapLinksEmitter; + +/** + *

Saxon extension for unwrapping nested links

+ * + *

$Id$

+ * + *

Copyright (C) 2000, 2002 Norman Walsh.

+ * + *

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

+ * + *

Change Log:

+ *
+ *
1.0
+ *

Initial release.

+ *
+ * + * @author Norman Walsh + * ndw@nwalsh.com + * + * @version $Id$ + * + */ +public class UnwrapLinks { + /** True if the stylesheet is producing formatting objects */ + private static boolean foStylesheet = false; + + /** + *

Constructor for UnwrapLinks

+ * + *

All of the methods are static, so the constructor does nothing.

+ */ + public UnwrapLinks() { + } + + /** + *

Find the string value of a stylesheet variable or parameter

+ * + *

Returns the string value of varName in the current + * context. Returns the empty string if the variable is + * not defined.

+ * + * @param context The current stylesheet context + * @param varName The name of the variable (without the dollar sign) + * + * @return The string value of the variable + */ + protected static String getVariable(Context context, String varName) { + Value variable = null; + String varString = null; + + try { + variable = Extensions.evaluate(context, "$" + varName); + varString = variable.asString(); + return varString; + } catch (TransformerException te) { + System.out.println("Undefined variable: " + varName); + return ""; + } catch (IllegalArgumentException iae) { + System.out.println("Undefined variable: " + varName); + return ""; + } + } + + /** + *

Setup the parameters associated with unwrapping links

+ * + * @param context The current stylesheet context + * + */ + private static void setupUnwrapLinks(Context context) { + // Get the stylesheet type + String varString = getVariable(context, "stylesheet.result.type"); + foStylesheet = (varString.equals("fo")); + } + + /** + *

Unwrap links

+ * + * @param rtf The result tree fragment of the verbatim environment. + * + * @return The modified result tree fragment. + */ + public static NodeSetValue unwrapLinks (Context context, + NodeSetValue rtf_ns) { + + FragmentValue rtf = (FragmentValue) rtf_ns; + boolean tryAgain = true; + + setupUnwrapLinks(context); + + try { + Controller controller = context.getController(); + NamePool namePool = controller.getNamePool(); + + while (tryAgain) { + UnwrapLinksEmitter ulEmitter = new UnwrapLinksEmitter(controller, + namePool, + foStylesheet); + rtf.replay(ulEmitter); + tryAgain = ulEmitter.tryAgain(); + rtf = (FragmentValue) ulEmitter.getResultTreeFragment(); + } + + return rtf; + + } catch (TransformerException e) { + // This "can't" happen. + System.out.println("Transformer Exception in unwrapLinks"); + return rtf; + } + } +} diff --git a/xsl/extensions/saxon643/com/nwalsh/saxon/UnwrapLinksEmitter.java b/xsl/extensions/saxon643/com/nwalsh/saxon/UnwrapLinksEmitter.java new file mode 100644 index 000000000..582807264 --- /dev/null +++ b/xsl/extensions/saxon643/com/nwalsh/saxon/UnwrapLinksEmitter.java @@ -0,0 +1,233 @@ +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$

+ * + *

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$ + * + */ +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; + } + } +} diff --git a/xsl/html/inline.xsl b/xsl/html/inline.xsl index 51db24a9b..c561b0caa 100644 --- a/xsl/html/inline.xsl +++ b/xsl/html/inline.xsl @@ -1,7 +1,8 @@ - - - - - - - + + + + + + + + + + - - + + - - - + + + - - - - XLink to nonexistent id: - - - ??? - - - - - - - - - - - - - - - + + + + XLink to nonexistent id: + + + ??? + + + + + + + + + + + + + + + + + + + - + + + + + + + - + diff --git a/xsl/html/xref.xsl b/xsl/html/xref.xsl index bd81ea002..c0ad04969 100644 --- a/xsl/html/xref.xsl +++ b/xsl/html/xref.xsl @@ -1,7 +1,7 @@