新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   >>中国XML论坛<<     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论XSL,XSLT,XSL-FO,CSS等技术
    [返回] 中文XML论坛 - 专业的XML技术讨论区XML.ORG.CN讨论区 - XML技术『 XSL/XSLT/XSL-FO/CSS 』 → 添加多个样式表支持[推荐] 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 7820 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 添加多个样式表支持[推荐] 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     宇宙人 帅哥哟,离线,有人找我吗?狮子座1981-8-20
      
      
      威望:4
      等级:大一新生
      文章:96
      积分:771
      门派:W3CHINA.ORG
      注册:2004/4/22

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给宇宙人发送一个短消息 把宇宙人加入好友 查看宇宙人的个人资料 搜索宇宙人在『 XSL/XSLT/XSL-FO/CSS 』的所有贴子 引用回复这个贴子 回复这个贴子 查看宇宙人的博客楼主
    发贴心情 添加多个样式表支持[推荐]

    [B][center]添加多个样式表支持[/center][/B]
    Benoit Marchal(bmarchal@pineapplesoft.com)
    顾问,Pineapple Software
    2001 年 9 月

    这个月,我们不辞辛劳的专栏作家将多个样式表的支持添加到 XM 内容管理项目中。 在这样做时,他涉及到了 TrAX URIResolver 并编写伪属性的解析器。如往常一样,可在 developerWorks 开放源码专区获得完整的源代码。

    在“使用 XML”专栏文章中,Beno顃 Marchal 每个月都报告其关于一个或多个开放源码 XML 开发项目的进展。 您可以随着他的进展,遵循他的设计决策和编码选择,也可以提出一些建议以及在您自己的项目中重用该开放源代码。
    继续有关 XM 的工作。这个月,我已经添加了多个样式表支持,因此可以解决读者发来的最常见建议。 我还添加了一个将一些参数传递到样式表的选项,它使 XM 基本的发布功能更完美。 在开始继续开发更高级的功能期间,我已经包括了一个目录阅读器。(您可以下载有关这篇文章以及以前专栏文章的所有相关代码; 请参阅副栏获取代码。)

    多个样式表
    XM 的头两个版本沿用“用一种模式来套用所有”策略,最初看上去似乎是一个好主意,但经过证实它是无效的。 更明确地讲,到现在为止,XM 只识别一种样式表 rules.xsl。正如本系列的第一篇文章中说明的那样, 我原先认为我本可以使用不同的 XSLT 模板来选择样式的变更:

    <xsl:template match="db:article">
       <!-- rules for an article here -->
    </xsl:template>
    <xsl:template match="xm:Directory">
       <!-- rules for a directory here -->
    </xsl:template>

    然而,我自己在 ananas.org(我完全用 XM 维护的网站)上的经历说明了我最初的计划不能很好地工作。 我还收到一些读者的建议,建议我解决那些他们察觉是缺陷的问题。最后, 当我开始着手内容生成(稍后将在本文中介绍)时,更觉得有必要添加多个样式表的支持了。

    获取代码
    跟往常一样, 可以从 CVS 资源库(请参阅参考资料)下载该专栏文章的代码。可以从同一个地方下载 ZIP 文件。 这个月,下载文件包括样本 .xml 和 .xsl 文件。
    处理指令
    您可能记起,易用性是我优先使用 XM 的原因之一, 明确地讲,我不希望使用配置文件或“构建脚本”来选择哪个样式表适用于哪个地方(有关这一需求的完整讨论, 请阅读使用 XML:将 XSLT 用于内容管理)。

    读者建议用巧妙的命名约定来选择样式表,但对于解决方案的最好提议来自一位同事, 他提醒我使用 xml-stylesheet 处理指令。

    如果您不熟悉 xml-stylesheet,则可以看于 1999 年 7 月发表的小的 W3C 建议书,其中介绍了它,xml-stylesheet 是通过 Internet Explorer 5.0 而得到普及。处理指令将样式表(XSL 或 CSS)与 XML 文档相关联。 例如:

    <?xml version="1.0"?>
    <?xml-stylesheet href="classic.xsl" type="text/xml"?>
    <?xml-stylesheet href="funky.xsl" type="text/xml" alternate="yes"?>
    <article>
    <articleinfo>
    <title>ananas.org</title>
    <!-- rest of the document goes here -->

    一般而言,处理指令对应用程序特定的数据进行编码。您已经熟悉了处理指令,因为大多数 XML 文档都是以 XML 声明开始, 该声明本身就是特殊的处理指令。一条处理指令包含一个目标(在上面示例中, xml-stylesheet),后跟数据。 用 <? 和 ?> 定界符将处理指令包起来。目标确定应用程序, 而对应用程序不能识别的目标,其会忽略这些处理指令。

    数据格式完全是自由的。XML 不指定将什么东西放进去(当然,除了 XML 声明)。 事实上,由于历史原因,处理指令可以包含 PostScript 图像或脚本等……但决不包含标记。

    象声明一样,xml-stylesheet 有特殊地位,因为它是由 W3C 定义的。 它必须出现在文档的开始(即,在第一个元素之前),并且包含几个所谓的 伪属性。这个数据之所以称为伪属性, 是因为其语法与 XML 属性相似。

    最重要的伪属性是 href,它包含指向样式表的 URI。其它有用的伪属性有 type 和 alternate。type 是样式表的 MIME 类型, 它用来区别 CSS 和 XSL。如果有多个 xml-stylesheet 指令, 则 alternate 表明哪一个替代主样式表。处理器应该用备用样式表列表提示用户。 然而,因为 XM 以批处理方式工作,所以它使用不同的策略,完全忽略备用样式表。

    虽然 xml-stylesheet 是一种 W3C 标准,但 TrAX 处理器忽略它,除非另行告知。 应用程序必须明确地调用 getAssociatedStylesheet() 来检索处理指令,如下:

    Source document = new StreamSource(file),
           stylesheet = factory.getAssociatedStylesheet(document,null,null,null);

    if(null != stylesheet)
       transformer.transform(document,new StreamResult(System.out));
    else
       throw new XMException("Cannot find the style sheet");

    然而,getAssociatedStylesheet() 带来 XM 必须避免的两个问题。首先,getAssociatedStylesheet() 使高速缓存常用样式表变得困难。其次,它假设样式表存储在与文档相同的目录中。 我喜欢将样式表存储在不同的目录中,因为我发现,如果样式表都被分组在一个目录中,可以易于维护和共享样式表。

    还传递样式表参数
    选择样式表仅仅完成了解决方案的一半。 通常,我希望做一些小小的变动,而不必编写新的样式表。对于这种情况,我喜爱的解决方案是使用参数, 如清单 1 所示:

    清单 1:样本参数 <xsl:stylesheet ...>

    <xsl:param name="sponsor" select="'none'"/>

    <xsl:template match="articleinfo">
       <xsl:if test="$sponsor='dw'">
          <center>
             <a href="http://www.ibm.com/developerWorks">
                <img align="middle" width="136" height="24" border="0"
                    alt="developerWorks" src="!images/buttons/dw.gif"/>
             </a>
          </center>
       </xsl:if>
       <xsl:apply-templates/>
    </xsl:template>


    还有,如何传递这些参数?W3C 没有提议一种机制,所以定义新的处理指令似乎也就不足为奇了。XM 可以识别 xm-xsl-param 和 xml-stylesheet。xm-xsl-param 的语法与其它处理指令相似,并且使用两个伪属性 name 和 value:

    <?xml version="1.0"?>
    <?xm-xsl-param name="sponsor" value="dw"?>
    <article>
       <articleinfo>
          <title>XM</title>


    显而易见,TrAX 不支持 xm-xsl-param,但因为我已经确定 XM 需要替换 getAssociatedStylesheet(),所以解析 xm-xsl-param 的工作不是很多。

    但这不是意味着要对文档解析两次吗?一次用于处理指令,另一次是使用 XSLT 处理器。实际上, 解析两次不会引起更多麻烦,因为处理指令必须出现在文档开始,所以 XM 只是重新解析文档的一小部分。

    ProcessingInstructionHandler 和 PseudoAttributeTokenizer
    ProcessingInstructionHandler 是一种 SAX ContentHandler,它抽取 xml-stylesheet 和 xm-xsl-param。

    处理程序截取 4 个事件。setDocumentLocator() 和 startDocument() 用于初始化。 大多数工作都发生在 processingInstruction() 中。至于 startElement(), 它用来停止解析,因为它标记这个开始的结束。要停止解析,startElement() 抛出一个异常。 这种作法近乎黑客所使用的手段,这是有争议的;异常一般用于报告错误,然而 startElement() 中没有错误,但 SAX 没有提供更“光明正大”的解决方案来停止解析。

    虽然伪属性的语法与 XML 属性相类似,但 SAX 解析器不对它们进行译码。XM 使用它自己的解析器 PseudoAttributeTokenizer 来对伪属性进行译码。

    PseudoAttributeTokenizer 每次扫描缓冲区一个字符,查找伪属性。 它使用一种典型的算法,这种算法可以在每本编译器书籍中找到。 如果您不熟悉方面的内容,那么我推荐您阅读以 Pascal 闻名的 Niklaus Wirth 的 Compiler Construction(请参阅参考资料)。

    要简化该代码,getc() 方法返回缓冲区中的下一个字符,而 putc() 替换缓冲区中 getc() 下一次调用的字符。

    PseudoAttributeTokenizer 的公用接口由三个方法组成:hasMoreTokens() 测试缓冲区中是否还有伪属性,nextName() 返回下一个名称,nextValue() 返回下一个值。

    让我们研究一下 nextName()。它通过调用 eatSpaces() 来除去前导空格。接下来, 只要它发现有数字或字母,就一直循环下去,并将字符累积在变量(token)中。因为名称只包含数字和字母, 所以任何其它字符都可以表示这个循环的结束。nextName() 特别关注读入缓冲区的、返回的最后一个字符, 其中,它将用于 nextValue()。

    清单 2:nextName() 示例 public String nextName()
       throws SAXParseException
    {
       token.setLength(0);
       int c = eatSpaces();
       for(;;)
          if(c == -1)
             throw new SAXParseException(UNEXPECTED_EOS,locator);
          // strictly speaking a name cannot start with a digit...
          else if(!Character.isLetterOrDigit((char)c) && c != '-')
          {
             putc();   // put it back for the next call
             return token.length() == 0 ? null : token.toString();
          }
          else
          {
             token.append((char)c);
             c = getc();
          }
    }

    nextValue() 与 nextName() 相似,但它先识别等号字符(由 nextName() 将它留在缓冲区中)和引号字符。nextValue() 还译码预先定义的实体(<、> 以及类似的)。

    有了 tokenizer,就很容易译码处理指令。以下代码摘自 ProcessingInstructionHandler,它用来识别 xml-stylesheet。xm-xsl-param 的代码与这类似:

    清单 3:ProcessingInstructionHandler 摘录 if(target.equals("xml-stylesheet"))
    {
       String href = null,
              type = null;
       boolean alternate = false;
       PseudoAttributeTokenizer tokenizer =
          new PseudoAttributeTokenizer(data,locator);
       while(tokenizer.hasMoreTokens())
       {
          String name = tokenizer.nextName(),
                 value = tokenizer.nextValue();
          if(name.equals("href"))
             href = value;
          else if(name.equals("alternate"))
             alternate = value.equals("yes");
          else if(name.equals("type"))
             type = value.trim();
          // ignore the media attribute...
       }
       if(type != null && href != null && !alternate &&
          (type.equals("text/xsl") || type.equals("text/xml") ||
           type.equals("application/xml+xslt")))
       {
          this.href = href;
          params.clear();
          readParams = true;
       }
       else
          readParams = false;
    }

    请记住,XM 会忽略备用样式表。W3C 建议书考虑到 HTTP,所以提供了优先于备用样式表的缺省样式表。XM 使用与这相同的规则, 应用它自己的缺省样式表,而不考虑备用样式表。

    TemplatesManager
    由于 XM 可使用多个样式表, 所以对高速缓存的逻辑进行了改进。这是由 TemplatesManager 来负责。当 StylingMover 请求 Templates 对象时,从高速缓存(如果该对象在其中)检索该对象。如果该对象不在其中, 则 TemplatesManager 装入样式表并将其放入高速缓存。本质上,TemplatesManager 是包含 java.util.Map 的封装器,并包含用于返回 Transformer 对象的附加方法。

    正如前面所解释的那样,XM 不会将文档和样式表混在一起。它使用两个目录:文档目录和规则目录。TrAX 提供 URIResolver 接口以控制 XSLT 处理器如何装入文件。XSLT 处理器的 URIResolver 与 SAX 解析器的 EntityResolver 相似; 当该处理器装入已导入的样式表(通过 xsl:import 或 xsl:include 元素)或文档(通过 document() 函数)时, 该处理器调用它的 resolve() 方法。

    TemplatesManager 使用内部类 ReferenceResolver,该类从规则目录装入样式表:

    清单 4:ReferenceResolver 示例 protected class ReferenceResolver
       implements URIResolver
    {
       protected File rulesDir;

       public ReferenceResolver(File rulesDir)
       {
          this.rulesDir = rulesDir;
       }

       public Source resolve(String href,String base)
       {
          if(href.endsWith(".xsl"))
          {
             File file = new File(rulesDir,href);
             if(file.exists())
                return new StreamSource(file);
          }
          return null;
       }
    }

    StylingMover


    当然,我已经把 StylingMover 改写成新类。它现在用 ProcessingInstructionHandler 处理程序来解析文档。 它使用处理结果来选择样式表并指定参数,如清单 5 所示。 特别要注意 try/catch 语句;因为 startElement() 使用特殊异常来停止解析,所以代码必须识别那不是一个错误。

    自动生成内容
    到目前为止, 有关 XM 的工作已经涉及了基本发布特性。虽然它们很重要,但我相信 XM 的真正价值体现在从一开始我就萦绕在脑际的自动内容生成。 简而言之,这个想法是让 XM 为您生成 XML 文档。

    例如,许多网站都包含下载区。如果经常更改文件列表, 则要维护一个带总是最新列表的 XML 文档是困难的。最好使用一种软件来自动生成列表。 该文档可能类似于清单 6。同样可以从 SQL 数据库、邮箱或者甚至远程网站生成文档!

    清单 6:由 XM 读取的目录 <?xml version="1.0" encoding="UTF-8"?>
    <xm:Directory xmlns:xm="http://www.ananas.org/2001/XM/Walk/Directory">
        <xm:File isDirectory="false" isFile="true" isHidden="false" canRead="true"
                 isMarked="false"  lastModified="2001-07-07T18:21:10" canWrite="true"
                 length="749">NotImplementedException.java</xm:File>
        <xm:File isDirectory="false" isFile="true" isHidden="false" canRead="true"
                 isMarked="false" lastModified="2001-07-20T11:49:42" canWrite="true"
                 length="6229">ContentHandlerExtractor.java</xm:File>
        <xm:File isDirectory="false" isFile="true" isHidden="false" canRead="true"
                 isMarked="false" lastModified="2001-09-05T07:10:10" canWrite="true"
                 length="2351">JAXPHelper.java</xm:File>
    </xm:Directory>

    上个月的专栏文章中介绍了 Mover,其用于简化添加自动内容生成的过程。 这个月,我已经在代码中预先包含了目录生成,并打算下个月再讲述它。同时, 如果您对此感兴趣,可回顾一下 DirectoryReader、WalkHandler 和 WalkMover。

    轮到您了
    目前,我正在用 XM 维护两个网站:ananas.org 和一个内部网。从使用 XM 而得到的实际经验对于确定如何更改软件十分有用。 欢迎您的加入,希望您下载 XM 副本,尝试它,来构建您自己的网站。 请在 ananas-discussion 邮件列表报告您的发现(请参阅参考资料)。

    我已经将 ananas.org 网站的代码(.xml 文档和 .xsl 样式表)添加到 CVS 资源库中, 您可以从那出发来设计您自己的网站。

    如果安装了早期版本的 XM,则需要更新软件以利用这个月的改进:将 rules.xsl 文件重命名为 default.xsl,并将它移到 rules 目录。 这与用于选择样式表的新标准匹配。

    参考资料

    参与本文的论坛。
    可以从 ananas.org 下载该项目的代码。在那里有到 developerWorks 上的 CVS 资源库以及 ananas-discussion 邮件列表的链接。 我希望您加入列表,并就该项目,提出您的想法。
    如果想要 ZIP 文件,也可以获得它。
    XM 将 Xalan 和 Xerces-J 分别用作 XSLT 处理器和 XML 解析器。最初,IBM(和Lotus)开发了这两个工具,后来将代码赠予 Apache Foundation。
    XML Extender for DB2 与 DirectoryReader 类似,但它用于 DB2 数据库。它允许您将数据库作为 XML 文档访问……可以用 XSLT 来转换 XML 文档。
    Niklaus Wirth 的 Compiler Construction (ISBN 0-2014-0353-6)是对解析的最好介绍之一。共 180 页,可以很快读完。
    在 developerWorks XML 专区中查找更多的 XML 参考资料。


    关于作者
    Beno顃 Marchal 是比利时纳慕尔的顾问和作家。他是 XML by Example 、 Applied XML Solutions 和 XML and the Enterprise 的作者。他是 Gamelan 的专栏作家。有关他最新项目的详细信息,可在 marchal.com 上找到。可以通过 bmarchal@pineapplesoft.com 与 Beno顃 联系。
    原文章网址:http://www-900.ibm.com/developerWorks/cn/xml/x-wxxm/part3/


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    对工作,我投入;对爱情,我专一。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2004/5/18 16:40:00
     
     GoogleAdSense狮子座1981-8-20
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 XSL/XSLT/XSL-FO/CSS 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/4/29 6:26:59

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    91.797ms