<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</tt></b><span id="info"> on MAIN</span></td></tr>
<tr><td><tt><a href="#file1">doc_comment.rb</a></tt></td><td align="right" id="added">+15</td><td></td><td nowrap="nowrap" align="center">283 -&gt; 284</td></tr>
<tr class="alt"><td><tt>localisation/xliff/<a href="#file2"><span id="added">driver.rb</span></a></tt></td><td align="right" id="added">+238</td><td></td><td nowrap="nowrap" align="right">added 284</td></tr>
<tr><td><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/<a href="#file3"><span id="added">translation_loader.rb</span></a></tt></td><td align="right" id="added">+183</td><td></td><td nowrap="nowrap" align="right">added 284</td></tr>
<tr class="alt"><td><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/<a href="#file4"><span id="added">xliff_doc_utils.rb</span></a></tt></td><td align="right" id="added">+72</td><td></td><td nowrap="nowrap" align="right">added 284</td></tr>
<tr><td><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/<a href="#file5"><span id="added">xliff_reader.rb</span></a></tt></td><td align="right" id="added">+158</td><td></td><td nowrap="nowrap" align="right">added 284</td></tr>
<tr class="alt"><td><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/<a href="#file6"><span id="added">xliff_writer.rb</span></a></tt></td><td align="right" id="added">+89</td><td></td><td nowrap="nowrap" align="right">added 284</td></tr>
<tr><td><tt>ui/<a href="#file7">cli.rb</a></tt></td><td align="right" id="added">+35</td><td align="right" id="removed">-3</td><td nowrap="nowrap" align="center">283 -&gt; 284</td></tr>
<tr><td></td><td align="right" id="added">+790</td><td align="right" id="removed">-3</td><td></td></tr>
</table>
<small id="info">5 added + 2 modified, total 7 files</small><br />
<div class="tasklist"><ul>
<li><a href="#task1">TODO: remove this hackery</a></li>
</ul></div>
<pre class="comment">
Initial (slightly hairy) cut at XLIFF import / export system for localising the human-readable portions of the API description
</pre>
<hr /><a name="file1" /><div class="file">
<span class="pathname">trunk/as2api</span><br />
<div class="fileheader"><big><b>doc_comment.rb</b></big> <small id="info">283 -&gt; 284</small></div>
<pre class="diff"><small id="info">--- trunk/as2api/doc_comment.rb        2006-02-26 16:07:25 UTC (rev 283)
+++ trunk/as2api/doc_comment.rb        2006-02-27 13:59:52 UTC (rev 284)
@@ -28,6 +28,10 @@
</small></pre><pre class="diff" id="context">     @blocks[i]
   end
 
</pre><pre class="diff" id="added">+  def description
+    @blocks[0]
+  end
+
</pre><pre class="diff" id="context">   def each_block_of_type(type)
     each_block do |block|
       yield block if block.is_a?(type)
</pre><pre class="diff"><small id="info">@@ -90,6 +94,13 @@
</small></pre><pre class="diff" id="context">     end
     return nil
   end
</pre><pre class="diff" id="added">+
+  def find_throws(exception_name)
+    each_exception do |block|
+      return block if block.exception_type.name == exception_name
+    end
+    nil
+  end
</pre><pre class="diff" id="context"> end
 
 class OurDocCommentHandler &lt; ActionScript::ParseDoc::DocCommentHandler
</pre><pre class="diff"><small id="info">@@ -239,6 +250,10 @@
</small></pre><pre class="diff" id="context">     @inlines
   end
 
</pre><pre class="diff" id="added">+  def clear
+    @inlines.clear
+  end
+
</pre><pre class="diff" id="context">   def ==(o)
     o.respond_to?(:inlines) &amp;&amp; inlines==o.inlines
   end
</pre></div>
<hr /><a name="file2" /><div class="file">
<span class="pathname" id="added">trunk/as2api/localisation/xliff</span><br />
<div class="fileheader" id="added"><big><b>driver.rb</b></big> <small id="info">added at 284</small></div>
<pre class="diff"><small id="info">--- trunk/as2api/localisation/xliff/driver.rb        2006-02-26 16:07:25 UTC (rev 283)
+++ trunk/as2api/localisation/xliff/driver.rb        2006-02-27 13:59:52 UTC (rev 284)
@@ -0,0 +1,238 @@
</small></pre><pre class="diff" id="added">+
+require 'xmlwriter'
+require 'localisation/xliff/xliff_writer'
+require 'localisation/xliff/xliff_doc_utils'
+require 'output/xml/xml_formatter'
+
+class XLIFFGenerator
+  include XLIFFWriter
+
+  def initialize(xml_writer, source_lang, target_lang)
+    @io = xml_writer
+    @source_lang = source_lang
+    @target_lang = target_lang
+  end
+
+
+  def generate_template_xliff(type_aggregator)
+    xliff_xliff("version"=&gt;"1.0") do
+      type_aggregator.each_type do |astype|
+        generate_file_template(astype);
+      end
+    end
+  end
+
+  def generate_file_template(astype)
+    xliff_file("original"=&gt;astype.input_filename,
+         "source-language"=&gt;@source_lang,
+         "target-language"=&gt;@target_lang,
+         "datatype"=&gt;"plaintext",
+         "xml:space"=&gt;"preserve") do
+      xliff_header do
+      end
+      xliff_body do
+        gen_type_trans_units(astype) if astype.comment
+        gen_member_trans_units(astype)
+      end
+    end
+  end
+
+  def trans_unit(id, *contexts)
+    xliff_trans_unit("id"=&gt;id) do
+      xliff_source do
+        yield
+      end
+      xliff_target do
+      end
+      unless contexts.empty?
+        simple_context_group("api", *contexts)
+      end
+    end
+  end
+
+  def simple_context_group(name, *contexts)
+    xliff_context_group("name"=&gt;name) do
+      until contexts.empty?
+        context_type, value, *contexts = contexts
+        xliff_context("context-type"=&gt;context_type) do
+          pcdata(value)
+        end
+      end
+    end
+  end
+
+  def gen_member_trans_units(astype)
+    if astype.respond_to?(:each_field)
+      astype.each_field do |asfield|
+        gen_field_comment(asfield) if asfield.comment
+      end
+    end
+    astype.each_method do |asmethod|
+      gen_method_comment(asmethod) if asmethod.comment
+    end
+  end
+
+  def gen_method_comment(asmethod)
+    @see_index = 0
+    asmethod.comment.each_block do |block|
+      send("gen_method_block_#{block.class.name}", asmethod, block)
+      @see_index += 1 if block.is_a?(SeeBlockTag)
+    end
+    @see_index = nil
+  end
+
+  def gen_field_comment(asfield)
+      asfield.comment.each_block do |block|
+        send("gen_field_block_#{block.class.name}", asfield, block)
+      end
+  end
+
+  def gen_type_trans_units(astype)
+    @see_index = 0
+    astype.comment.each_block do |block|
+      send("gen_type_block_#{block.class.name}", astype, block)
+      @see_index += 1 if block.is_a?(SeeBlockTag)
+    end
+    @see_index = nil
+  end
+
+  def gen_type_block_BlockTag(astype, block)
+    kind = if astype.is_a?(ASClass)
+      "class"
+    else
+      "interface"
+    end
+    trans_unit(XliffIds.id_for_type_description(astype),
+               "element", kind,
+               "type", astype.qualified_name) do
+      gen_inlines(block)
+    end
+  end
+
+  def gen_method_block_BlockTag(asmethod, block)
+    trans_unit(XliffIds.id_for_method_description(asmethod),
+               "element", "method",
+               "type", asmethod.containing_type.qualified_name,
+               "method", asmethod.name) do
+      gen_inlines(block)
+    end
+  end
+
+  def gen_field_block_BlockTag(asfield, block)
+    trans_unit(XliffIds.id_for_field_description(asfield),
+               "element", "field",
+               "type", asfield.containing_type.qualified_name,
+               "field", asfield.name) do
+      gen_inlines(block)
+    end
+  end
+
+  def gen_method_block_ParamBlockTag(asmethod, block)
+    trans_unit(XliffIds.id_for_parameter_description(asmethod,block.param_name),
+               "element", "parameter",
+               "type", asmethod.containing_type.qualified_name,
+               "method", asmethod.name,
+               "parameter", block.param_name) do
+      gen_inlines(block)
+    end
+  end
+
+  def gen_method_block_ReturnBlockTag(asmethod, block)
+    trans_unit(XliffIds.id_for_method_return(asmethod),
+               "element", "return",
+               "type", asmethod.containing_type.qualified_name,
+               "method", asmethod.name) do
+      gen_inlines(block)
+    end
+  end
+
+  def gen_method_block_ThrowsBlockTag(asmethod, block)
+    trans_unit(XliffIds.id_for_throws_description(asmethod, block.exception_type.resolved_type),
+               "element", "throws",
+               "type", asmethod.containing_type.qualified_name,
+               "method", asmethod.name,
+               "throws", block.exception_type.resolved_type.qualified_name) do
+      gen_inlines(block)
+    end
+  end
+
+  def gen_method_block_SeeBlockTag(asmethod, block)
+    trans_unit(XliffIds.id_for_method_see(asmethod, @see_index),
+               "element", "see",
+               "type", asmethod.containing_type.qualified_name,
+               "method", asmethod.name) do
+      gen_inlines(block)
+    end
+  end
+
+  def gen_type_block_SeeBlockTag(astype, block)
+    trans_unit(XliffIds.id_for_type_see(astype, @see_index),
+               "element", "see",
+               "type", astype.qualified_name) do
+      gen_inlines(block)
+    end
+  end
+
+  def gen_inlines(block)
+    index = 0
+    block.each_inline do |inline|
+      if index==0 &amp;&amp; inline.is_a?(String)
+        inline = inline.lstrip
+      end
+      if index==block.inlines.length-1 &amp;&amp; inline.is_a?(String)
+        inline = inline.rstrip
+      end
+      gen_inline(inline)
+      index += 1
+    end
+  end
+
+  def gen_inline(inline)
+    if inline.is_a?(String)
+      pcdata(inline)
+    else
+      send("gen_inline_#{inline.class.name}", inline)
+    end
+  end
+
+  def gen_inline_CodeTag(inline)
+    xliff_ph("id"=&gt;"code") do
+      pcdata(inline.text)
+    end
+  end
+
+  def gen_inline_LinkTag(inline)
+    xliff_ph("id"=&gt;"link") do
+      if inline.target
+        pcdata(inline.target.local_name)
+      end
+      if inline.member
+        pcdata("#")
+        pcdata(inline.member)
+      end
+      if inline.text &amp;&amp; inline.text != ""
+        xliff_sub("id"=&gt;"link-text") do
+          pcdata(inline.text)
+        end
+      end
+    end
+  end
+end
+
+def generate_xliff(conf, type_aggregator)
+  encoding = "UTF-8"
+  File.open(conf.xliff_export, "w") do |io|
+    xml = XMLWriter.new(io)
+    xml.pi("xml version=\"1.0\" encoding=\"#{encoding}\"")
+    xml.pcdata("\n")
+    xml.doctype("xliff", "PUBLIC",
+                "-//XLIFF//DTD XLIFF//EN",
+               "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd")
+    format = XMLFormatter.new(xml)
+    format.inlines [ "source", "target", "context", "ph", "sub", "it", "bpt", "ept", "g", "x", "bx", "ex", "mrk" ]
+    gen = XLIFFGenerator.new(format, conf.source_lang, conf.target_lang)
+    gen.generate_template_xliff(type_aggregator)
+  end
+end
+
+# vim:softtabstop=2:shiftwidth=2
</pre></div>
<hr /><a name="file3" /><div class="file">
<span class="pathname" id="added">trunk/as2api/localisation/xliff</span><br />
<div class="fileheader" id="added"><big><b>translation_loader.rb</b></big> <small id="info">added at 284</small></div>
<pre class="diff"><small id="info">--- trunk/as2api/localisation/xliff/translation_loader.rb        2006-02-26 16:07:25 UTC (rev 283)
+++ trunk/as2api/localisation/xliff/translation_loader.rb        2006-02-27 13:59:52 UTC (rev 284)
@@ -0,0 +1,183 @@
</small></pre><pre class="diff" id="added">+
+require 'localisation/xliff/xliff_reader'
+require 'localisation/xliff/xliff_doc_utils'
+
+class CodePhHandler
+  def initialize(block); @block = block; end
+  def start_ph; @text=""; end
+  def end_ph; @block.add_inline(CodeTag.new(0, @text)); end
+  def text(text); @text&lt;&lt;text end
+end
+
+class LinkPhHandler
+  def initialize(block, type_resolver)
+    @block = block
+    @type_resolver = type_resolver
+  end
+  def start_ph; @text=@ph_text=""; @sub_text="" end
+  def end_ph;
+    @ph_text =~ /^([^#\s]+)(?:#([^\s]+))?/
+    if $1
+      target = @type_resolver.resolve($1)
+    else
+      target = nil
+    end
+    member = $2
+    @block.add_inline(LinkTag.new(0, target, member, @sub_text)) 
+  end
+  def text(text); @text&lt;&lt;text end
+  def start_sub; @text=@sub_text; end
+  def end_sub; @text=@ph_text; end
+end
+
+class DocXliffHandler
+  include TranslationHandler
+
+  def initialize(filename_to_type, target_lang)
+    @filename_to_type = filename_to_type
+    @target_lang = target_lang
+    @current_type = nil
+  end
+
+  def start_file(file)
+    @current_type = @filename_to_type[file.original]
+    if @current_type.nil?
+      warn("no type matched #{file.original.inspect}")
+    end
+    if file.target_language == @target_lang
+      @process_this_file = true
+    else
+      @process_this_file = false
+      warn("target language #{file.target_language.inspect} for #{file.original.inspect} isn't #{@target_lang.inspect}; skipping translation data")
+    end
+  end
+  def end_file
+    @current_type = nil
+  end
+
+
+  def get_seeblock(comment, index)
+    i = 0
+    comment.each_seealso do |block|
+      return block if index == i
+      i += 1
+    end
+    return nil
+  end
+
+  def start_trans_unit(id)
+    return unless @process_this_file
+    parts = id.split(/-/)
+    element = parts.shift
+    unless element == "class" || element == "interface"
+      warn("expected id to start with 'class-' or 'interface-'")
+      return
+    end
+    type_name = XliffIds.from_id(parts.shift)
+    unless type_name == @current_type.qualified_name
+      warn("expected type didn't match type in id; #{@current_type.qualified_name.inspect}, #{type_name.inspect}")
+      return
+    end
+    element = parts.shift
+    case element
+      when "description"
+        @current_block = @current_type.comment.description
+      when "method"
+        method_name = XliffIds.from_id(parts.shift)
+        asmethod = @current_type.get_method_called(method_name)
+        element = parts.shift
+        case element
+          when "description"
+            @current_block = asmethod.comment.description
+          when "return"
+            @current_block = asmethod.comment.find_return
+          when "param"
+            param_name = XliffIds.from_id(parts.shift)
+            @current_block = asmethod.comment.find_param(param_name)
+          when "throws"
+            type_name = XliffIds.from_id(parts.shift)
+            @current_block = asmethod.comment.find_throws(type_name)
+          when "see"
+            see_num = parts.shift.to_i
+            @current_block = get_seeblock(asmethod.comment, see_num)
+          else
+            warn("unknown API element #{element.inspect} in id #{id.inspect}")
+            return
+        end
+      when "field"
+        field_name = XliffIds.from_id(parts.shift)
+        asfield = @current_type.get_field_called(field_name)
+        unless asfield
+          warn("no field #{field_name.inspect} in #{@current_type.qualified_name}")
+          return
+        end
+        element = parts.shift
+        case element
+          when "description"
+            @current_block = asfield.comment.description
+          when "see"
+            see_num = parts.shift.to_i
+            @current_block = get_seeblock(asfield.comment, see_num)
+          else
+            warn("unknown API element #{element.inspect} in id #{id.inspect}")
+            return
+        end
+      when "see"
+        see_num = parts.shift.to_i
+        @current_block = get_seeblock(@current_type.comment, see_num)
+      else
+        warn("unknown API element #{element.inspect} in id #{id.inspect}")
+        return
+    end
+  end
+
+  def end_trans_unit
+    return unless @process_this_file
+    @current_block = nil
+  end
+
+  def start_target
+    return unless @process_this_file
+    @current_block.clear if @current_block
+  end
+
+  def text(text)
+    return unless @process_this_file
+    @current_block.add_inline(text) if @current_block
+  end
+
+  def ph(id)
+    # Null-Object pattern,
+    return PhHandler.new unless @current_block &amp;&amp; @process_this_file
+
+    case id
+      when "code"
+        return CodePhHandler.new(@current_block)
+      when "link"
+        return LinkPhHandler.new(@current_block, @current_type.type_resolver)
+      else
+        raise "unhandled placeholder id #{id.inspect}"
+    end
+  end
+
+  private
+
+  def warn(text)
+    $stderr.puts("warn: #{text} (#{caller[0]})")
+  end
+end
+
+
+def update_docs(conf, type_aggregator)
+  filename_to_type = {}
+  type_aggregator.each_type do |astype|
+    filename_to_type[astype.input_filename] = astype
+  end
+  File.open(conf.xliff_import) do |io|
+    doc_handler = DocXliffHandler.new(filename_to_type, conf.target_lang)
+    XLIFFReader.new(io, doc_handler).parse
+  end
+end
+
+
+# vim:sw=2:sts=2
</pre></div>
<hr /><a name="file4" /><div class="file">
<span class="pathname" id="added">trunk/as2api/localisation/xliff</span><br />
<div class="fileheader" id="added"><big><b>xliff_doc_utils.rb</b></big> <small id="info">added at 284</small></div>
<pre class="diff"><small id="info">--- trunk/as2api/localisation/xliff/xliff_doc_utils.rb        2006-02-26 16:07:25 UTC (rev 283)
+++ trunk/as2api/localisation/xliff/xliff_doc_utils.rb        2006-02-27 13:59:52 UTC (rev 284)
@@ -0,0 +1,72 @@
</small></pre><pre class="diff" id="added">+
+# utilities pertaining to the use of JavaDoc in XLIFF
+
+
+module XliffIds
+
+  def self.id_prefix_for_type(astype)
+    "class-#{to_id(astype.qualified_name)}"
+  end
+
+  def self.id_prefix_for_method(asmethod)
+    "#{id_prefix_for_type(asmethod.containing_type)}-method-#{to_id(asmethod.name)}"
+  end
+
+  def self.id_prefix_for_field(asfield)
+    "#{id_prefix_for_type(asfield.containing_type)}-field-#{to_id(asfield.name)}"
+  end
+
+  def self.id_prefix_for_parameter(asmethod, param_name)
+    "#{id_prefix_for_method(asmethod)}-param-#{to_id(param_name)}"
+  end
+
+  def self.id_for_type_description(astype)
+    "#{id_prefix_for_type(astype)}-description"
+  end
+
+  def self.id_for_method_description(asmethod)
+    "#{id_prefix_for_method(asmethod)}-description"
+  end
+
+  def self.id_for_method_return(asmethod)
+    "#{id_prefix_for_method(asmethod)}-return"
+  end
+
+  def self.id_for_field_description(asfield)
+    "#{id_prefix_for_field(asfield)}-description"
+  end
+
+  def self.id_for_parameter_description(asmethod, param_name)
+    "#{id_prefix_for_parameter(asmethod, param_name)}-description"
+  end
+
+  def self.id_prefix_for_throws(asmethod, exception_type)
+    "#{id_prefix_for_method(asmethod)}-throws-#{to_id(exception_type.qualified_name)}"
+  end
+
+  def self.id_for_throws_description(asmethod, exception_type)
+    "#{id_prefix_for_throws(asmethod, exception_type)}-description"
+  end
+
+  def self.id_for_type_see(astype, index)
+    "#{id_prefix_for_type(astype)}-see-#{index}"
+  end
+
+  def self.id_for_method_see(asmethod, index)
+    "#{id_prefix_for_method(asmethod)}-see-#{index}"
+  end
+
+  def self.to_id(text)
+    text.gsub(/[^-.a-zA-Z0-9]/) do |match|
+      "_" + match[0].to_s(16)
+    end
+  end
+
+  def self.from_id(text)
+    text.gsub(/_([0-9a-fA-F]{2})/) do |match|
+      $1.to_s.to_i(16).chr
+    end
+  end
+end
+
+# vim:sw=2:sts=2
</pre></div>
<hr /><a name="file5" /><div class="file">
<span class="pathname" id="added">trunk/as2api/localisation/xliff</span><br />
<div class="fileheader" id="added"><big><b>xliff_reader.rb</b></big> <small id="info">added at 284</small></div>
<pre class="diff"><small id="info">--- trunk/as2api/localisation/xliff/xliff_reader.rb        2006-02-26 16:07:25 UTC (rev 283)
+++ trunk/as2api/localisation/xliff/xliff_reader.rb        2006-02-27 13:59:52 UTC (rev 284)
@@ -0,0 +1,158 @@
</small></pre><pre class="diff" id="added">+
+require 'rexml/document'
+
+# So, this code is all a bit of a mess, tbh.  Once I've got a working codebase
+# to look at, and I actually understand the indended use of the different
+# parts of the XLIFF spec, this could all do with a nice rewrite
+
+
+XLIFFFile = Struct.new(:original, :source_language, :target_language)
+
+
+module TranslationHandler
+  def start_file(file); end  
+  def end_file; end  
+
+  def start_trans_unit(id); end
+  def end_trans_unit; end
+
+  def start_source; end
+  def end_source; end
+  def start_target; end
+  def end_target; end
+
+  def text(text); end
+
+  def ph(id); PhHandler.new; end
+end
+
+class PhHandler
+  def start_ph; end
+  def end_ph; end
+
+  def start_sub; end
+  def end_sub; end
+
+  def text(text); end
+end
+
+
+class XLIFFReader
+
+  def initialize(io, translation_handler)
+    @doc = REXML::Document.new(io)
+    @handler = translation_handler
+  end
+
+  def parse
+    @doc.root.each_element("file") do |file_element|
+      file = XLIFFFile.new
+      file.original = file_element.attribute("original").value
+      file.source_language = file_element.attribute("source-language").value
+      file.target_language = file_element.attribute("target-language").value
+
+      @handler.start_file(file)
+      file_element.each_element("body/trans-unit") do |trans_unit_element|
+        parse_trans_unit(trans_unit_element)
+      end
+      @handler.end_file
+    end
+  end
+
+  def parse_trans_unit(trans_unit_element)
+    id = trans_unit_element.attribute("id")
+    @handler.start_trans_unit(id.value)
+    trans_unit_element.each_element("source") do |source_element|
+      parse_source(source_element)
+    end
+    trans_unit_element.each_element("target") do |target_element|
+      parse_target(target_element)
+    end
+    @handler.end_trans_unit
+  end
+
+  def parse_source(source_element)
+    return if source_element.size == 0
+
+    @handler.start_source
+    source_element.each_element do |element|
+    end
+    @handler.end_source
+  end
+
+  def parse_target(target_element)
+    return if target_element.size == 0
+
+    @handler.start_target
+    target_element.each_child do |element|
+      case element.node_type
+        when :text
+          @handler.text(element.value)
+        when :element
+          case element.name
+            when "ph"
+              parse_ph(element)
+            else
+              raise "unhandled element #{element.name.inspect}"
+          end
+        else
+          raise "unhandled node type #{element.inspect}"
+      end
+    end
+    @handler.end_target
+  end
+
+  def parse_ph(ph_element)
+    ph_handler = @handler.ph(ph_element.attribute("id").value)
+    ph_handler.start_ph
+    ph_element.each_child do |element|
+      case element.node_type
+        when :text
+          ph_handler.text(element.value)
+        when :element
+          case element.name
+            when "sub"
+              parse_sub(ph_handler, element)
+            else
+              raise "unhandled element #{element.name.inspect}"
+          end
+        else
+          raise "unhandled node type #{element.inspect}"
+      end
+    end
+    ph_handler.end_ph()
+  end
+
+  def parse_sub(ph_handler, sub_element)
+    ph_handler.start_sub
+    sub_element.each_child do |element|
+      case element.node_type
+        when :text
+          ph_handler.text(element.value)
+        else
+          raise "unhandled node type #{element.inspect}"
+      end
+    end
+    ph_handler.end_sub
+  end
+end
+
+if __FILE__ == $0
+
+  class MyListener
+    include TranslationHandler
+
+    def start_file(file); puts "file(#{file.original}),"; end
+    def start_trans_unit(id); puts "  unit(#{id}),"; end
+    def start_target; print "    target&lt;"; end
+    def end_target; puts "&gt;"; end
+    def text(text); print text; end
+  end
+
+  File.open(ARGV[0]) do |io|
+    XLIFFReader.new(io, MyListener.new).parse
+  end
+
+end
+
+# vim:sw=2:sts=2
</pre></div>
<hr /><a name="file6" /><div class="file">
<span class="pathname" id="added">trunk/as2api/localisation/xliff</span><br />
<div class="fileheader" id="added"><big><b>xliff_writer.rb</b></big> <small id="info">added at 284</small></div>
<pre class="diff"><small id="info">--- trunk/as2api/localisation/xliff/xliff_writer.rb        2006-02-26 16:07:25 UTC (rev 283)
+++ trunk/as2api/localisation/xliff/xliff_writer.rb        2006-02-27 13:59:52 UTC (rev 284)
@@ -0,0 +1,89 @@
</small></pre><pre class="diff" id="added">+
+module XLIFFWriter
+  private
+
+  TAGS = [
+    "alt-trans",
+    "bin-source",
+    "bin-target",
+    "bin-unit",
+    "body",
+    "bpt",
+    "bx",
+    "context",
+    "context-group",
+    "count",
+    "count-group",
+    "ept",
+    "ex",
+    "external-file",
+    "file",
+    "g",
+    "glossary",
+    "group",
+    "header",
+    "internal-file",
+    "it",
+    "mrk",
+    "note",
+    "ph",
+    "phase",
+    "phase-group",
+    "prop",
+    "prop-group",
+    "reference",
+    "skl",
+    "source",
+    "sub",
+    "target",
+    "tool",
+    "trans-unit",
+    "x",
+    "xliff"
+  ]
+
+
+  TAGS.each do |name|
+    class_eval &lt;&lt;-HERE
+      def xliff_#{name.gsub(/-/, "_")}(*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
</pre></div>
<hr /><a name="file7" /><div class="file">
<span class="pathname">trunk/as2api/ui</span><br />
<div class="fileheader"><big><b>cli.rb</b></big> <small id="info">283 -&gt; 284</small></div>
<pre class="diff"><small id="info">--- trunk/as2api/ui/cli.rb        2006-02-26 16:07:25 UTC (rev 283)
+++ trunk/as2api/ui/cli.rb        2006-02-27 13:59:52 UTC (rev 284)
@@ -14,6 +14,8 @@
</small></pre><pre class="diff" id="context"> require 'set'
 require 'output/html/driver'
 require 'output/html/diff'
</pre><pre class="diff" id="added">+require 'localisation/xliff/driver.rb'
+require 'localisation/xliff/translation_loader.rb'
</pre><pre class="diff" id="context"> 
 include GetText
 
</pre><pre class="diff"><small id="info">@@ -31,7 +33,9 @@
</small></pre><pre class="diff" id="context">                   :sources,
                   :format_html,
                   :source_lang,
</pre><pre class="diff" id="removed">-                  :target_lang<span id="removedchars">)</span>
</pre><pre class="diff" id="added">+                  :target_lang<span id="addedchars">,</span>
+                  :xliff_import,
+                  :xliff_export)
</pre><pre class="diff" id="context"> 
 SourceFile = Struct.new(:prefix, :suffix)
 
</pre><pre class="diff"><small id="info">@@ -115,7 +119,9 @@
</small></pre><pre class="diff" id="context">       [ "--sources",          GetoptLong::NO_ARGUMENT ],
       [ "--format-html",      GetoptLong::NO_ARGUMENT ],
       [ "--source-lang",      GetoptLong::REQUIRED_ARGUMENT ],
</pre><pre class="diff" id="removed">-      [ "--target-lang",      GetoptLong::REQUIRED_ARGUMENT ]
</pre><pre class="diff" id="added">+      [ "--target-lang",      GetoptLong::REQUIRED_ARGUMENT ]<span id="addedchars">,</span>
+      [ "--xliff-import",     GetoptLong::REQUIRED_ARGUMENT ],
+      [ "--xliff-export",     GetoptLong::REQUIRED_ARGUMENT ]
</pre><pre class="diff" id="context">     )
 
     conf = Conf.new
</pre><pre class="diff"><small id="info">@@ -154,12 +160,22 @@
</small></pre><pre class="diff" id="context">           conf.source_lang = arg
         when "--target-lang"
           conf.target_lang = arg
</pre><pre class="diff" id="added">+        when "--xliff-import"
+          conf.xliff_import = arg
+        when "--xliff-export"
+          conf.xliff_export = arg
</pre><pre class="diff" id="context">       end
     end
     if ARGV.empty?
       usage
       error(_("No packages specified"))
     end
</pre><pre class="diff" id="added">+    if conf.xliff_import &amp;&amp; conf.xliff_export
+      error(_("Options can't be used together: %s") % "--xliff-import --xliff-export")
+    end
+    if conf.xliff_export &amp;&amp; (conf.source_lang.nil? || conf.target_lang.nil?)
+      error(_("Both --source-lang and --target-lang must be provided with --xliff-export"))
+    end
</pre><pre class="diff" id="context">     ARGV.each do |package_spec|
       conf.package_filters &lt;&lt; to_filter(package_spec)
     end
</pre><pre class="diff"><small id="info">@@ -237,14 +253,30 @@
</small></pre><pre class="diff" id="context">     type_agregator
   end
 
</pre><pre class="diff" id="added">+  def xliff_import(type_aggregator)
+    update_docs(@conf, type_aggregator)
+  end
+
+  def xliff_export(type_aggregator)
+    generate_xliff(@conf, type_aggregator)
+  end
+
</pre><pre class="diff" id="context">   def main
     @conf = parse_opts
     files = find_sources(@conf.classpath)
     error(_("No source files matching specified packages")) if files.empty?
     type_agregator = parse_all(files, @conf.classpath)
</pre><pre class="diff" id="added">+    if @conf.xliff_import
+      xliff_import(type_agregator)
+    end
</pre><pre class="diff" id="context">     type_agregator.resolve_types
</pre><pre class="diff" id="removed">-    document_types(@conf, type_agregator)
</pre><pre class="diff" id="added">+    if @conf.xliff_export
+      xliff_export(type_agregator)
+    else
+      document_types(@conf, type_agregator)
+    end
</pre><pre class="diff" id="context"> 
</pre><pre class="diff" id="added"><a name="task1" />+    # <span class="task">TODO</span>: remove this hackery
</pre><pre class="diff" id="context">     unless @conf.oldrev_classpath.empty?
       old_files = find_sources(@conf.oldrev_classpath)
       error(_("No source files matching specified packages in oldrev-classpath")) if old_files.empty?
</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>