<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 < CommentBlockTextHandler
+ def text(text)
+        if @comment_block.inlines[0].text
+         # unlikely case, maybe a comment splitting the text?
+         @comment_block.inlines[0].text << 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 << 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") # => "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"=>@api_name, "version"=>@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"=>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"=>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"=>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"=>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"=>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"=>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"=>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 && 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 && 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"=>"test", "version"=>"1.0") {
+# api_package("name"=>"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 <<-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 -> email">CVSspam</a> 0.2.11</small></center>
</body></html>