<html>
<head>
<style><!--
  body {background-color:#ffffff;}
  .file {border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;}
  .pathname {font-family:monospace; float:right;}
  .fileheader {margin-bottom:.5em;}
  .diff {margin:0;}
  .tasklist {padding:4px;border:1px dashed #000000;margin-top:1em;}
  .tasklist ul {margin-top:0;margin-bottom:0;}
  tr.alt {background-color:#eeeeee}
  #added {background-color:#ddffdd;}
  #addedchars {background-color:#99ff99;font-weight:bolder;}
  tr.alt #added {background-color:#ccf7cc;}
  #removed {background-color:#ffdddd;}
  #removedchars {background-color:#ff9999;font-weight:bolder;}
  tr.alt #removed {background-color:#f7cccc;}
  #info {color:#888888;}
  #context {background-color:#eeeeee;}
  td {padding-left:.3em;padding-right:.3em;}
  tr.head {border-bottom-width:1px;border-bottom-style:solid;}
  tr.head td {padding:0;padding-top:.2em;}
  .task {background-color:#ffff00;}
  .comment {padding:4px;border:1px dashed #000000;background-color:#ffffdd}
  .error {color:red;}
  hr {border-width:0px;height:2px;background:black;}
--></style>
</head>
<body>
<table cellspacing="0" cellpadding="0" border="0" rules="cols">
<tr class="head"><td colspan="4">Commit in <b><tt>trunk/as2api/output/diff</tt></b><span id="info"> on MAIN</span></td></tr>
<tr><td><tt><a href="#file1"><span id="added">api_deserializer.rb</span></a></tt></td><td align="right" id="added">+252</td><td></td><td nowrap="nowrap" align="right">added 310</td></tr>
<tr class="alt"><td><tt><a href="#file2"><span id="added">api_dump.rb</span></a></tt></td><td align="right" id="added">+43</td><td></td><td nowrap="nowrap" align="right">added 310</td></tr>
<tr><td><tt><a href="#file3"><span id="added">api_serializer.rb</span></a></tt></td><td align="right" id="added">+215</td><td></td><td nowrap="nowrap" align="right">added 310</td></tr>
<tr class="alt"><td><tt><a href="#file4"><span id="added">api_xml_reader.rb</span></a></tt></td><td align="right" id="added">+125</td><td></td><td nowrap="nowrap" align="right">added 310</td></tr>
<tr><td><tt><a href="#file5"><span id="added">api_xml_writer.rb</span></a></tt></td><td align="right" id="added">+97</td><td></td><td nowrap="nowrap" align="right">added 310</td></tr>
<tr><td></td><td align="right" id="added">+732</td><td></td><td></td></tr>
</table>
<small id="info">5 added files</small><br />
<div class="tasklist"><ul>
<li><a href="#task1">TODO: if there was body-text in the tag, add it to the link</a></li>
<li><a href="#task2">TODO: some handling of explicit vs implict field stuff</a></li>
</ul></div>
<pre class="comment">
Code supporting the dump/reload of the API model to/from an XML file.
Support infrastructure for API-difference-reporting mode.
</pre>
<hr /><a name="file1" /><div class="file">
<span class="pathname" id="added">trunk/as2api/output/diff</span><br />
<div class="fileheader" id="added"><big><b>api_deserializer.rb</b></big> <small id="info">added at 310</small></div>
<pre class="diff"><small id="info">--- trunk/as2api/output/diff/api_deserializer.rb        2006-03-19 00:50:24 UTC (rev 309)
+++ trunk/as2api/output/diff/api_deserializer.rb        2006-03-19 21:53:32 UTC (rev 310)
@@ -0,0 +1,252 @@
</small></pre><pre class="diff" id="added">+# 
+# Part of as2api - http://www.badgers-in-foil.co.uk/projects/as2api/
+#
+# Copyright (c) 2006 David Holroyd, and contributors.
+#
+# See the file 'COPYING' for terms of use of this code.
+#
+
+
+require 'output/diff/api_xml_reader'
+require 'api_loader'
+
+# Recreates an API model from a serialized XML stream created by APISerializer
+#
+#   File.open('api_data.xml') do |io|
+#     deser = APIDeserializer.new(io)
+#     type_aggregator, api_name, api_version = deser.deserialize_api
+#   end
+#
+class APIDeserializer
+  def initialize(io)
+    @io = io
+  end
+
+  # returns a 3 element array containing a GlobalTypeAggregator instance,
+  # the API name and the API version (both Strings).
+  def deserialize_api
+    listener = DeserializerAPIListener.new
+    reader = APIXMLReader.new(listener)
+    reader.read(@io)
+    [listener.type_aggregator, listener.api_name, listener.api_version]
+  end
+
+  private
+
+  class DeserializerAPIListener
+
+    def initialize
+      @type_aggregator = nil
+      @text_handler_stack = []
+      @api_name = nil
+      @api_version = nil
+    end
+
+    attr_accessor :type_aggregator, :api_name, :api_version
+
+    def text(text)
+      #return unless @current_comment_block
+      #@current_comment_block.add_inline(text)
+      @text_handler_stack.last.text(text) unless @text_handler_stack.empty?
+    end
+
+    def start_api(name, version)
+      @type_aggregator = GlobalTypeAggregator.new
+      @api_name = name
+      @api_version = version
+    end
+    def end_api
+      TypeResolver.new([]).resolve_types(@type_aggregator)
+    end
+
+    def start_package(name)
+      @current_package_name = name
+    end
+    def end_package; end
+
+    def start_class(name, extends)
+      @current_api_element = @current_type = ASClass.new(@current_package_name, name)
+      @current_type.type_namespace = TypeLocalNamespace.new(@current_type)
+      @current_type.import_list = ImportList.new
+      if extends
+        @current_type.extends = resolve_type(extends)
+      end
+    end
+
+    def end_class
+      @type_aggregator.add_type(@current_type)
+      @current_type = nil
+    end
+    def start_interface(name, extends)
+      @current_api_element = @current_type = ASInterface.new(@current_package_name, name)
+      @current_type.type_namespace = TypeLocalNamespace.new(@current_type)
+      @current_type.import_list = ImportList.new
+      if extends
+        @current_type.extends = resolve_type(extends)
+      end
+    end
+    def end_interface
+      @type_aggregator.add_type(@current_type)
+      @current_type = nil
+    end
+
+    def start_annotation
+      @comment_data = @current_api_element.comment || CommentData.new
+    end
+    def end_annotation
+      @current_api_element.comment = @comment_data
+      @comment_data = nil
+    end
+
+    class CommentBlockTextHandler
+      def initialize(comment_block)
+        @comment_block = comment_block
+      end
+
+      def text(text)
+        @comment_block.add_inline(text)
+      end
+    end
+
+    class SeeBlockTextHandler &lt; CommentBlockTextHandler
+      def text(text)
+        if @comment_block.inlines[0].text
+          # unlikely case, maybe a comment splitting the text?
+          @comment_block.inlines[0].text &lt;&lt; text
+        else
+          @comment_block.inlines[0].text = text.dup
+        end
+      end
+    end
+
+    class LinkBlockTextHandler
+      def initialize(link_tag)
+        @link_tag = link_tag
+      end
+      def text(text)
+        if @link_tag.text
+          # unlikely case, maybe a comment splitting the text?
+          @link_tag.text &lt;&lt; text
+        else
+          @link_tag.text = text.dup
+        end
+      end
+    end
+   
+    def push_text_handler(handler)
+      @text_handler_stack.push(handler)
+    end
+
+    def pop_text_handler
+      @text_handler_stack.pop
+    end
+
+    def start_description
+      @current_comment_block = BlockTag.new
+      push_text_handler(CommentBlockTextHandler.new(@current_comment_block))
+    end
+    def end_description
+      @comment_data.add_block(@current_comment_block)
+      @current_comment_block = nil
+      pop_text_handler
+    end
+
+    def start_see(type, member)
+      @current_comment_block = SeeBlockTag.new
+      push_text_handler(SeeBlockTextHandler.new(@current_comment_block))
+      lineno = 0
+      type_proxy = resolve_type(type) if type
+      text = nil
+      @current_comment_block.add_inline(LinkTag.new(lineno, type_proxy, member, text))
+    end
+
+    def end_see
<a name="task1" />+      # <span class="task">TODO</span>: if there was body-text in the tag, add it to the link
+      
+      @comment_data.add_block(@current_comment_block)
+      @current_comment_block = nil
+      pop_text_handler
+    end
+    def start_link(type, member)
+      lineno = 0
+      type_proxy = resolve_type(type) if type
+      text = nil
+      link_tag = LinkTag.new(lineno, type_proxy, member, text)
+      push_text_handler(LinkBlockTextHandler.new(link_tag))
+      @current_comment_block.add_inline(link_tag)
+    end
+    def end_link
+      pop_text_handler
+    end
+    def implements(interface)
+      @current_type.add_interface(resolve_type(interface))
+    end
+    def start_constructor; end
+    def end_constructor; end
+    def start_method(name, visibility, static)
+      access = ASAccess.new(visibility, static)
+      @current_api_element = @current_method = ASMethod.new(@current_type, access, name)
+    end
+    def end_method
+      @current_type.add_method(@current_method)
+      @current_api_element = @current_method = nil
+    end
+    def start_param(name, type)
+      @current_comment_block = @current_param_block = ParamBlockTag.new
+      @current_comment_block.param_name = name
+      @current_arg = ASArg.new(name)
+      @current_arg.arg_type = resolve_type(type) if type
+      push_text_handler(CommentBlockTextHandler.new(@current_comment_block))
+    end
+    def end_param
+      unless @current_param_block.inlines.empty?
+        @current_method.comment ||= CommentData.new
+        @current_method.comment.add_block(@current_param_block)
+      end
+      @current_method.add_arg(@current_arg)
+      @current_arg = nil
+      @current_comment_block = nil
+      pop_text_handler
+    end
+    def start_return(type)
+      @current_comment_block = @current_return_block = ReturnBlockTag.new
+      @current_method.return_type = resolve_type(type) if type
+      push_text_handler(CommentBlockTextHandler.new(@current_comment_block))
+    end
+    def end_return
+      unless @current_return_block.inlines.empty?
+        @current_method.comment ||= CommentData.new
+        @current_method.comment.add_block(@current_return_block)
+      end
+      @current_comment_block = @current_return_block = nil
+      pop_text_handler
+    end
+    def start_field(name, type, visibility, static)
+      access = ASAccess.new(visibility, static)
+      @current_api_element = @current_field = ASExplicitField.new(@current_type, access, name)
+      @current_field.field_type = resolve_type(type) if type
+    end
+    def end_field
+      @current_type.add_field(@current_field)
+      @current_api_element = @current_field = nil
+    end
+    def start_exception(type)
+      @current_comment_block = @current_exception = ThrowsBlockTag.new
+      @current_exception.exception_type = resolve_type(type)
+      push_text_handler(CommentBlockTextHandler.new(@current_comment_block))
+    end
+    def end_exception
+      @current_method.comment.add_block(@current_exception)
+      pop_text_handler
+    end
+
+    private
+
+    def resolve_type(name)
+      @current_type.type_namespace.resolve(name)
+    end
+  end  # class DeserializerAPIListner
+
+end
+
+# vim:sw=2:sts=2
</pre></div>
<hr /><a name="file2" /><div class="file">
<span class="pathname" id="added">trunk/as2api/output/diff</span><br />
<div class="fileheader" id="added"><big><b>api_dump.rb</b></big> <small id="info">added at 310</small></div>
<pre class="diff"><small id="info">--- trunk/as2api/output/diff/api_dump.rb        2006-03-19 00:50:24 UTC (rev 309)
+++ trunk/as2api/output/diff/api_dump.rb        2006-03-19 21:53:32 UTC (rev 310)
@@ -0,0 +1,43 @@
</small></pre><pre class="diff" id="added">+# 
+# Part of as2api - http://www.badgers-in-foil.co.uk/projects/as2api/
+#
+# Copyright (c) 2006 David Holroyd, and contributors.
+#
+# See the file 'COPYING' for terms of use of this code.
+#
+
+
+require 'output/diff/api_serializer'
+require 'output/diff/api_deserializer'
+
+
+# Generates an XML dump of the data in the given GlobalTypeAggregator to
+# a file with a name based on settings from the given config structure
+def generate_api_dump(conf, type_aggregator)
+  File.open(api_dump_name(conf.api_name, conf.api_version), "w") do |io|
+    ser = APISerializer.new(io, conf.api_name, conf.api_version)
+    ser.serialize_api(type_aggregator)
+  end
+end
+
+# Given an the name and version of an API, work out what filename the API's
+# description should be stored in by downcasing, replacing any non-alphanumeric
+# characters in both strings with underscores, joining the name to the version
+# with a minushyphen ('-') and appending '.xml'.
+#
+#   api_dump_name('My Cool API!', "1.1a")  # =&gt; "my_cool_api_-1_1a.xml"
+#
+def api_dump_name(api_name, api_version)
+  api_name = api_name.gsub(/[^a-zA-Z0-9]/, "_").downcase
+  api_version = api_version.gsub(/[^a-zA-Z0-9]/, "_").downcase
+  "#{api_name}-#{api_version}.xml"
+end
+
+def load_api_dump(filename)
+  File.open(filename) do |io|
+    deser = APIDeserializer.new(io)
+    return deser.deserialize_api
+  end
+end
+
+# vim:sw=2:sts=2
</pre></div>
<hr /><a name="file3" /><div class="file">
<span class="pathname" id="added">trunk/as2api/output/diff</span><br />
<div class="fileheader" id="added"><big><b>api_serializer.rb</b></big> <small id="info">added at 310</small></div>
<pre class="diff"><small id="info">--- trunk/as2api/output/diff/api_serializer.rb        2006-03-19 00:50:24 UTC (rev 309)
+++ trunk/as2api/output/diff/api_serializer.rb        2006-03-19 21:53:32 UTC (rev 310)
@@ -0,0 +1,215 @@
</small></pre><pre class="diff" id="added">+# 
+# Part of as2api - http://www.badgers-in-foil.co.uk/projects/as2api/
+#
+# Copyright (c) 2006 David Holroyd, and contributors.
+#
+# See the file 'COPYING' for terms of use of this code.
+#
+
+
+require 'xmlwriter'
+require 'output/xml/xml_formatter'
+require 'output/diff/api_xml_writer'
+
+# The #serialize_api method of this class will write a description of the
+# types given, as XML, to the IO given to #new
+class APISerializer
+  include APIXMLWriter
+
+  # Creates an APISerializer which writes data to the given IO, and which will
+  # embed the given api_name and api_version into the resulting XML.
+  def initialize(io, api_name, api_version)
+    # APIWriter will use the @io attribute,
+    @io = XMLFormatter.new(XMLWriter.new(io))
+    @io.inlines(["see", "description", "param", "return", "link", "exception"])
+    @api_name = api_name
+    @api_version = api_version
+  end
+
+  # writes XML describing the given types to the IO passed to the constructor
+  def serialize_api(type_aggregator)
+    api_api("name"=&gt;@api_name, "version"=&gt;@api_version) {
+      type_aggregator.each_package do |aspackage|
+        serialize_package(aspackage)
+      end
+    }
+  end
+
+  private
+
+  def serialize_package(aspackage)
+    attrs = {}
+    attrs["name"] = aspackage.name if aspackage.name
+    api_package(attrs) {
+      aspackage.each_type do |astype|
+        serialize_type(astype)
+      end
+    }
+  end
+
+  def serialize_type(astype)
+    if astype.is_a?(ASClass)
+      serialize_class(astype)
+    else
+      serialize_interface(astype)
+    end
+  end
+
+  def serialize_class(asclass)
+    attrs = {"name"=&gt;asclass.unqualified_name}
+    if asclass.has_ancestor?
+      attrs["extends"] = asclass.extends.resolved_type.qualified_name
+    end
+    api_class(attrs) {
+      asclass.each_interface do |asinterface|
+        if asinterface.resolved?
+          api_implements("interface"=&gt;asinterface.resolved_type.qualified_name)
+        end
+      end
+      serialize_annotation(asclass.comment) if asclass.comment
+      serialize_all_fields(asclass)
+      serialize_all_methods(asclass)
+    }
+  end
+
+  def serialize_interface(asinterface)
+    attrs = {"name"=&gt;asinterface.unqualified_name}
+    if asinterface.has_ancestor?
+      attrs["extends"] = asinterface.extends.resolved_type.qualified_name
+    end
+    api_interface(attrs) {
+      serialize_annotation(asinterface.comment) if asinterface.comment
+      serialize_all_methods(asinterface)
+    }
+  end
+
+  def serialize_all_fields(asclass)
+    asclass.each_field do |asfield|
+      serialize_field(asfield)
+    end
+  end
+
+  def serialize_all_methods(astype)
+    astype.each_method do |asmethod|
+      serialize_method(asmethod)
+    end
+  end
+
+  def serialize_field(asfield)
<a name="task2" />+    # <span class="task">TODO</span>: some handling of explicit vs implict field stuff
+    attrs = {"name"=&gt;asfield.name}
+    attrs["visibility"] = asfield.access.visibility.to_s if asfield.access.visibility
+    attrs["static"] = "true" if asfield.access.static?
+    attrs["type"] = asfield.field_type.local_name if asfield.field_type
+    api_field(attrs) {
+      serialize_annotation(asfield.comment) if asfield.comment
+    }
+  end
+
+  def serialize_method(asmethod)
+    attrs = {"name"=&gt;asmethod.name}
+    attrs["visibility"] = asmethod.access.visibility.to_s if asmethod.access.visibility
+    attrs["static"] = "true" if asmethod.access.static?
+    api_method(attrs) {
+      serialize_annotation(asmethod.comment) if asmethod.comment
+      serialize_all_params(asmethod)
+      serialize_return(asmethod)
+      serialize_all_exceptions(asmethod) if asmethod.comment
+    }
+  end
+
+  def serialize_all_params(asmethod)
+    asmethod.arguments.each do |asarg|
+      serialize_param(asarg, asmethod.comment)
+    end
+  end
+
+  def serialize_param(asarg, comment)
+    attrs = {"name"=&gt;asarg.name}
+    attrs["type"] = asarg.arg_type.local_name if asarg.arg_type
+    api_param(attrs) {
+      if comment
+        param_doc = comment.find_param(asarg.name)
+        if param_doc
+          serialize_comment_inlines(param_doc)
+        end
+      end
+    }
+  end
+
+  def serialize_all_exceptions(asmethod)
+    asmethod.comment.each_exception do |throws_block|
+      api_exception("type"=&gt;throws_block.exception_type.local_name) {
+        serialize_comment_inlines(throws_block)
+      }
+    end
+  end
+
+  def serialize_return(asmethod)
+    attrs = {}
+    attrs["type"] = asmethod.return_type.local_name if asmethod.return_type
+    api_return(attrs) {
+      if asmethod.comment
+        return_doc = asmethod.comment.find_return
+        if return_doc
+          serialize_comment_inlines(return_doc)
+        end
+      end
+    }
+  end
+
+  def serialize_annotation(comment)
+    api_annotation {
+      if comment.description
+        serialize_description(comment.description)
+      end
+      comment.each_seealso do |seealso|
+        serialize_seealso(seealso)
+      end
+    }
+  end
+
+  def serialize_description(description)
+    api_description {
+      serialize_comment_inlines(description)
+    }
+  end
+
+  def serialize_seealso(seealso)
+    link = seealso.inlines.first
+    attrs = {}
+    attrs["type"]=link.target.local_name if link.target
+    attrs["member"]=link.member if link.member
+    if link.text &amp;&amp; link.text!=""
+      api_see(link.text, attrs)
+    else
+      api_see(attrs)
+    end
+  end
+
+  def serialize_comment_inlines(comment_block)
+    comment_block.each_inline do |inline|
+      case inline
+        when String
+          pcdata(inline)
+        when LinkTag
+          serialize_link_tag(inline)
+        else
+          raise "unhandled inline #{inline.inspect}"
+      end
+    end
+  end
+
+  def serialize_link_tag(link)
+    attrs = {}
+    attrs["type"]=link.target.local_name if link.target
+    attrs["member"]=link.member if link.member
+    if link.text &amp;&amp; link.text!=""
+      api_link(link.text, attrs)
+    else
+      api_link(attrs)
+    end
+  end
+end
+
+# vim:sw=2:sts=2
</pre></div>
<hr /><a name="file4" /><div class="file">
<span class="pathname" id="added">trunk/as2api/output/diff</span><br />
<div class="fileheader" id="added"><big><b>api_xml_reader.rb</b></big> <small id="info">added at 310</small></div>
<pre class="diff"><small id="info">--- trunk/as2api/output/diff/api_xml_reader.rb        2006-03-19 00:50:24 UTC (rev 309)
+++ trunk/as2api/output/diff/api_xml_reader.rb        2006-03-19 21:53:32 UTC (rev 310)
@@ -0,0 +1,125 @@
</small></pre><pre class="diff" id="added">+# 
+# Part of as2api - http://www.badgers-in-foil.co.uk/projects/as2api/
+#
+# Copyright (c) 2006 David Holroyd, and contributors.
+#
+# See the file 'COPYING' for terms of use of this code.
+#
+
+
+require 'rexml/document'
+require 'rexml/streamlistener'
+
+# Reads data from an XML stream and notifies an instance of APIListener about
+# the structures the stream contains.
+class APIXMLReader
+  include REXML::StreamListener
+
+  # Creates an instance that will call methods on the given APIListener as
+  # elements are seen during reading.
+  def initialize(api_listener)
+    @listener = api_listener
+  end
+
+  # parses XML data from the given stream, and sends the data to the
+  # APIListener that was given to the constructor.
+  def read(io)
+    REXML::Document.parse_stream(io, self)
+  end
+
+  def tag_start(name, attrs)
+    case name
+      when "api"
+        @listener.start_api(attrs["name"], attrs["version"])
+      when "package"
+        @listener.start_package(attrs["name"])
+      when "class"
+        @listener.start_class(attrs["name"], attrs["extends"])
+      when "interface"
+        @listener.start_interface(attrs["name"], attrs["extends"])
+      when "annotation"
+        @listener.start_annotation
+      when "description"
+        @listener.start_description
+      when "see"
+        @listener.start_see(attrs["type"], attrs["member"])
+      when "link"
+        @listener.start_link(attrs["type"], attrs["member"])
+      when "implements"
+        @listener.implements(attrs["interface"])
+      when "constructor"
+        @listener.start_constructor
+      when "method"
+        vis = attrs["visibility"] ? attrs["visibility"].to_sym : nil
+        @listener.start_method(attrs["name"], vis, attrs["static"]=="true")
+      when "param"
+        @listener.start_param(attrs["name"], attrs["type"])
+      when "return"
+        @listener.start_return(attrs["type"])
+      when "field"
+        vis = attrs["visibility"] ? attrs["visibility"].to_sym : nil
+        @listener.start_field(attrs["name"], attrs["type"], vis, attrs["static"]=="true")
+      when "exception"
+        @listener.start_exception(attrs["type"])
+      else
+        raise "unknown tag #{name.inspect}"
+    end
+  end
+
+  def tag_end(name)
+    @listener.send("end_#{name}") unless NOEND.include?(name)
+  end
+
+  def text(text)
+    @listener.text(text)
+  end
+
+  private
+
+  # elements which are expected to be empty tags (and so will not have an
+  # end-tag).
+  NOEND = [
+    "implements"
+  ]
+end
+
+
+# Module providing empty implementations of the methods called by APIXMLReader
+# as it encounters API decriptors in an XML stream.  If you want to parse an
+# API XML file, but aren't interested in all the events the parser produces,
+# include this module in your listner implementation.
+module APIListener
+  def text(text); end
+
+  def start_api(name); end
+  def end_api; end
+  def start_package(name); end
+  def end_package; end
+  def start_class(name, extends); end
+  def end_class; end
+  def start_interface(name, extends); end
+  def end_interface; end
+  def start_annotation; end
+  def end_annotation; end
+  def start_description; end
+  def end_description; end
+  def start_see(type, member); end
+  def end_see; end
+  def start_link(type, member); end
+  def end_link; end
+  def implements(interface); end
+  def start_constructor; end
+  def end_constructor; end
+  def start_method(name, visibility, static); end
+  def end_method; end
+  def start_param(name, type); end
+  def end_param; end
+  def start_return(type); end
+  def end_return; end
+  def start_field(name, type, visibility, static); end
+  def end_field; end
+  def start_exception(type); end
+  def end_exception; end
+end
+
+# vim:sw=2:sts=2
</pre></div>
<hr /><a name="file5" /><div class="file">
<span class="pathname" id="added">trunk/as2api/output/diff</span><br />
<div class="fileheader" id="added"><big><b>api_xml_writer.rb</b></big> <small id="info">added at 310</small></div>
<pre class="diff"><small id="info">--- trunk/as2api/output/diff/api_xml_writer.rb        2006-03-19 00:50:24 UTC (rev 309)
+++ trunk/as2api/output/diff/api_xml_writer.rb        2006-03-19 21:53:32 UTC (rev 310)
@@ -0,0 +1,97 @@
</small></pre><pre class="diff" id="added">+# 
+# Part of as2api - http://www.badgers-in-foil.co.uk/projects/as2api/
+#
+# Copyright (c) 2006 David Holroyd, and contributors.
+#
+# See the file 'COPYING' for terms of use of this code.
+#
+
+# Utility for generating API-describing XML data.
+#
+#   class MyWriter
+#     include APIXMLWriter
+#     
+#     def write
+#       File.open('api_data.xml') do |file|
+#         @io = XMLWriter.new(file)
+#         generate()
+#       end
+#     end
+#     
+#     def generate
+#       api_api("name"=&gt;"test", "version"=&gt;"1.0") {
+#         api_package("name"=&gt;"test.package") {
+#           ...
+#         }
+#       }
+#     end
+#   end
+module APIXMLWriter
+
+  private
+
+  # The names of elements supported by this file format
+  TAGS = [
+    "api",
+    "package",
+    "class",
+    "interface",
+    "annotation",
+    "description",
+    "see",
+    "implements",
+    "constructor",
+    "method",
+    "param",
+    "return",
+    "field",
+    "exception",
+    "link"
+  ]
+
+  TAGS.each do |name|
+    class_eval &lt;&lt;-HERE
+      def api_#{name}(*args)
+        if block_given?
+          @io.element("#{name}", *args) { yield }
+        else
+          if args.length == 0
+            @io.empty_tag("#{name}")
+          else
+            if args[0].instance_of?(String)
+              @io.simple_element("#{name}", *args)
+            else
+              @io.empty_tag("#{name}", *args)
+            end
+          end
+        end
+      end
+    HERE
+  end
+
+  public
+
+  def pcdata(text)
+    @io.pcdata(text)
+  end
+
+  def pi(text)
+    @io.pi(text)
+  end
+
+  def comment(text)
+    @io.comment(text)
+  end
+
+  def doctype(name, syspub, public_id, system_id)
+    @io.doctype(name, syspub, public_id, system_id)
+  end
+
+  def passthrough(text)
+    @io.passthrough(text)
+  end
+
+  def xml; @io end
+end
+
+# vim:sw=2
</pre></div>
<center><small><a href="http://www.badgers-in-foil.co.uk/projects/cvsspam/" title="commit -&gt; email">CVSspam</a> 0.2.11</small></center>
</body></html>