]> granicus.if.org Git - docbook-dsssl/commitdiff
Unwrap nested links
authorNorman Walsh <ndw@nwalsh.com>
Wed, 26 Jun 2002 11:03:05 +0000 (11:03 +0000)
committerNorman Walsh <ndw@nwalsh.com>
Wed, 26 Jun 2002 11:03:05 +0000 (11:03 +0000)
xsl/extensions/saxon643/.classes/com/nwalsh/saxon/Makefile.incl
xsl/extensions/saxon643/com/nwalsh/saxon/UnwrapLinks.java [new file with mode: 0644]
xsl/extensions/saxon643/com/nwalsh/saxon/UnwrapLinksEmitter.java [new file with mode: 0644]
xsl/html/inline.xsl
xsl/html/xref.xsl

index de568b47601eb60785e988bd6ca76d993f9de755..8842cd3695d3e1fe2091c8d3691c9c202dac7934 100644 (file)
@@ -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 (file)
index 0000000..ec1a337
--- /dev/null
@@ -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;
+
+/**
+ * <p>Saxon extension for unwrapping nested links</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2002 Norman Walsh.</p>
+ *
+ * <p>This class provides a
+ * <a href="http://saxon.sf.net/">Saxon 6.*</a>
+ * implementation of a link unwrapper.</p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @version $Id$
+ *
+ */
+public class UnwrapLinks {
+  /** True if the stylesheet is producing formatting objects */
+  private static boolean foStylesheet = false;
+
+  /**
+   * <p>Constructor for UnwrapLinks</p>
+   *
+   * <p>All of the methods are static, so the constructor does nothing.</p>
+   */
+  public UnwrapLinks() {
+  }
+
+  /**
+   * <p>Find the string value of a stylesheet variable or parameter</p>
+   *
+   * <p>Returns the string value of <code>varName</code> in the current
+   * <code>context</code>. Returns the empty string if the variable is
+   * not defined.</p>
+   *
+   * @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 "";
+    }
+  }
+
+  /**
+   * <p>Setup the parameters associated with unwrapping links</p>
+   *
+   * @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"));
+  }
+
+  /**
+   * <p>Unwrap links</p>
+   *
+   * @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 (file)
index 0000000..5828072
--- /dev/null
@@ -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;
+
+/**
+ * <p>Saxon extension to unwrap links in a result tree fragment.</p>
+ *
+ * <p>$Id$</p>
+ *
+ * <p>Copyright (C) 2000, 2002 Norman Walsh.</p>
+ *
+ * <p>This class provides the guts of a
+ * <a href="http://saxon.sf.net/">Saxon 6.*</a>
+ * implementation of a link unwrapper.</p>
+ *
+ * <p>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 <i>single</i> level of unwrapping
+ * is performed. This is clearly a crude implementation.
+ * </p>
+ *
+ * <p><b>Change Log:</b></p>
+ * <dl>
+ * <dt>1.0</dt>
+ * <dd><p>Initial release.</p></dd>
+ * </dl>
+ *
+ * @author Norman Walsh
+ * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
+ *
+ * @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;
+
+
+  /** <p>Constructor for the UnwrapLinksEmitter.</p>
+   *
+   * @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;
+  }
+
+  /**
+   * <p>A private class for maintaining the information required to call
+   * the startElement method.</p>
+   *
+   * <p>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.</p>
+   */
+  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;
+    }
+  }
+}
index 51db24a9b885f4e6efd3732887ab521bcac22b0e..c561b0caa9376cefd7d680d11a7c62bf94f0c60b 100644 (file)
@@ -1,7 +1,8 @@
 <?xml version='1.0'?>
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                 xmlns:xlink='http://www.w3.org/1999/xlink'
-                exclude-result-prefixes="xlink"
+                xmlns:suwl="http://nwalsh.com/xslt/ext/com.nwalsh.saxon.UnwrapLinks"
+                exclude-result-prefixes="xlink suwl"
                 version='1.0'>
 
 <!-- ********************************************************************
     <xsl:apply-templates/>
   </xsl:param>
 
-  <xsl:choose>
-    <xsl:when test="$node/@xlink:href
-                    and (not($node/@xlink:type) or $node/@xlink:type='simple')">
-      <a>
-        <xsl:if test="@xlink.title">
-          <xsl:attribute name="title">
-            <xsl:value-of select="@xlink:title"/>
-          </xsl:attribute>
-        </xsl:if>
+  <xsl:variable name="link">
+    <xsl:choose>
+      <xsl:when test="$node/@xlink:href
+                      and (not($node/@xlink:type) or $node/@xlink:type='simple')">
+        <a>
+          <xsl:if test="@xlink.title">
+            <xsl:attribute name="title">
+              <xsl:value-of select="@xlink:title"/>
+            </xsl:attribute>
+          </xsl:if>
 
-        <xsl:attribute name="href">
-          <xsl:choose>
-            <!-- if the href starts with # and does not contain an "(" -->
-            <!-- or if the href starts with #xpointer(id(, it's just an ID -->
-            <xsl:when test="starts-with(@xlink:href,'#')
-                            and (not(contains(@xlink:href,'&#40;'))
-                            or starts-with(@xlink:href,'#xpointer&#40;id&#40;'))">
-              <xsl:variable name="idref">
-                <xsl:call-template name="xpointer.idref">
-                  <xsl:with-param name="xpointer" select="@xlink:href"/>
-                </xsl:call-template>
-              </xsl:variable>
+          <xsl:attribute name="href">
+            <xsl:choose>
+              <!-- if the href starts with # and does not contain an "(" -->
+              <!-- or if the href starts with #xpointer(id(, it's just an ID -->
+              <xsl:when test="starts-with(@xlink:href,'#')
+                              and (not(contains(@xlink:href,'&#40;'))
+                              or starts-with(@xlink:href,'#xpointer&#40;id&#40;'))">
+                <xsl:variable name="idref">
+                  <xsl:call-template name="xpointer.idref">
+                    <xsl:with-param name="xpointer" select="@xlink:href"/>
+                  </xsl:call-template>
+                </xsl:variable>
 
-              <xsl:variable name="targets" select="key('id',$idref)"/>
-              <xsl:variable name="target" select="$targets[1]"/>
+                <xsl:variable name="targets" select="key('id',$idref)"/>
+                <xsl:variable name="target" select="$targets[1]"/>
 
-              <xsl:call-template name="check.id.unique">
-                <xsl:with-param name="linkend" select="@linkend"/>
-              </xsl:call-template>
+                <xsl:call-template name="check.id.unique">
+                  <xsl:with-param name="linkend" select="@linkend"/>
+                </xsl:call-template>
 
-              <xsl:choose>
-                <xsl:when test="count($target) = 0">
-                  <xsl:message>
-                    <xsl:text>XLink to nonexistent id: </xsl:text>
-                    <xsl:value-of select="$idref"/>
-                  </xsl:message>
-                  <xsl:text>???</xsl:text>
-                </xsl:when>
-                <xsl:otherwise>
-                  <xsl:call-template name="href.target">
-                    <xsl:with-param name="object" select="$target"/>
-                  </xsl:call-template>
-                </xsl:otherwise>
-              </xsl:choose>
-            </xsl:when>
-
-            <!-- otherwise it's a URI -->
-            <xsl:otherwise>
-              <xsl:value-of select="@xlink:href"/>
-            </xsl:otherwise>
-          </xsl:choose>
-        </xsl:attribute>
+                <xsl:choose>
+                  <xsl:when test="count($target) = 0">
+                    <xsl:message>
+                      <xsl:text>XLink to nonexistent id: </xsl:text>
+                      <xsl:value-of select="$idref"/>
+                    </xsl:message>
+                    <xsl:text>???</xsl:text>
+                  </xsl:when>
+                  <xsl:otherwise>
+                    <xsl:call-template name="href.target">
+                      <xsl:with-param name="object" select="$target"/>
+                    </xsl:call-template>
+                  </xsl:otherwise>
+                </xsl:choose>
+              </xsl:when>
+
+              <!-- otherwise it's a URI -->
+              <xsl:otherwise>
+                <xsl:value-of select="@xlink:href"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:attribute>
+          <xsl:copy-of select="$content"/>
+        </a>
+      </xsl:when>
+      <xsl:otherwise>
         <xsl:copy-of select="$content"/>
-      </a>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="function-available('suwl:unwrapLinks')">
+      <xsl:copy-of select="suwl:unwrapLinks($link)"/>
     </xsl:when>
     <xsl:otherwise>
-      <xsl:copy-of select="$content"/>
+      <xsl:copy-of select="$link"/>
     </xsl:otherwise>
   </xsl:choose>
 </xsl:template>
index bd81ea00290cbcb8565223c380d9bfbe627948b5..c0ad04969c14bb958fc15dd921a132a99fb5df6f 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version='1.0'?>
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-                xmlns:doc="http://nwalsh.com/xsl/documentation/1.0"
-                exclude-result-prefixes="doc"
+                xmlns:suwl="http://nwalsh.com/xslt/ext/com.nwalsh.saxon.UnwrapLinks"
+                exclude-result-prefixes="suwl"
                 version='1.0'>
 
 <!-- ********************************************************************
 </xsl:template>
 
 <xsl:template match="ulink" name="ulink">
-  <a>
-    <xsl:if test="@id">
-      <xsl:attribute name="name">
-        <xsl:value-of select="@id"/>
-      </xsl:attribute>
-    </xsl:if>
-    <xsl:attribute name="href"><xsl:value-of select="@url"/></xsl:attribute>
-    <xsl:if test="$ulink.target != ''">
-      <xsl:attribute name="target">
-        <xsl:value-of select="$ulink.target"/>
-      </xsl:attribute>
-    </xsl:if>
-    <xsl:choose>
-      <xsl:when test="count(child::node())=0">
-       <xsl:value-of select="@url"/>
-      </xsl:when>
-      <xsl:otherwise>
-       <xsl:apply-templates/>
-      </xsl:otherwise>
-    </xsl:choose>
-  </a>
+  <xsl:variable name="link">
+    <a>
+      <xsl:if test="@id">
+        <xsl:attribute name="name">
+          <xsl:value-of select="@id"/>
+        </xsl:attribute>
+      </xsl:if>
+      <xsl:attribute name="href"><xsl:value-of select="@url"/></xsl:attribute>
+      <xsl:if test="$ulink.target != ''">
+        <xsl:attribute name="target">
+          <xsl:value-of select="$ulink.target"/>
+        </xsl:attribute>
+      </xsl:if>
+      <xsl:choose>
+        <xsl:when test="count(child::node())=0">
+          <xsl:value-of select="@url"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:apply-templates/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </a>
+  </xsl:variable>
+
+  <xsl:choose>
+    <xsl:when test="function-available('suwl:unwrapLinks')">
+      <xsl:copy-of select="suwl:unwrapLinks($link)"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:copy-of select="$link"/>
+    </xsl:otherwise>
+  </xsl:choose>
 </xsl:template>
 
 <xsl:template match="olink" name="olink">