# # File:: xml2vsxml.rb # # Pipeline:: takes a generic xml file and converts it to an xml format # that visual studio understands. this is because visual studio # thinks it needs to 'upgrade' perfectly well formatted xml data. # muppets # # # Author:: Greg Smith # Date:: 28 January 2010 # # #----------------------------------------------------------------------------- # Uses #----------------------------------------------------------------------------- require "rexml/document" #----------------------------------------------------------------------------- # Implementation #----------------------------------------------------------------------------- module Pipeline class VsXml < REXML::Formatters::Default @@vs2010_format = false def VsXml.convert_to_vsxml( input_filename, output_filename, vs2010_format = false ) @@vs2010_format = vs2010_format file = File.open(input_filename,"r") doc = REXML::Document.new(file) file.close() begin file = File.open(output_filename,"w") file.truncate(0) formatter = VsXml.new() formatter.write( doc, file ) rescue Exception => ex print "Unable to write #{output_filename}!\n" print "Exception: #{ex.message}\n" print ex.backtrace.join( "\n" ) return false ensure file.close() end return true end # If compact is set to true, then the formatter will attempt to use as # little space as possible attr_accessor :compact # The width of a page. Used for formatting text attr_accessor :width # Create a new pretty printer. # # output:: # An object implementing '<<(String)', to which the output will be written. # indentation:: # An integer greater than 0. The indentation of each level will be # this number of spaces. If this is < 1, the behavior of this object # is undefined. Defaults to 2. # ie_hack:: # If true, the printer will insert whitespace before closing empty # tags, thereby allowing Internet Explorer's feeble XML parser to # function. Defaults to false. def initialize( indentation=1, ie_hack=false ) @indentation = indentation @level = 0 @ie_hack = ie_hack @width = 80 end def write( node, output ) case node when Document if node.xml_decl.encoding != "UTF-8" && !output.kind_of?(Output) output = Output.new( output, node.xml_decl.encoding ) end write_document( node, output ) when Element write_element( node, output ) when Declaration, ElementDecl, NotationDecl, ExternalEntity, Entity, Attribute, AttlistDecl node.write( output,-1 ) when Instruction write_instruction( node, output ) when DocType node.write( output ) when XMLDecl if @@vs2010_format == false output << "" else output << "" end when Comment write_comment( node, output ) when CData write_cdata( node, output ) when Text write_text( node, output ) else raise Exception.new("XML FORMATTING ERROR") end end protected def write_element(node, output) output << "\t"*@level output << "<#{node.expanded_name}" node.attributes.each_attribute do |attr| if @@vs2010_format == false output << "\n" output << "\t"*(@level + 1) else output << " " #Adds a space between attribute on each line. end out_val = attr.value.gsub("\"",""") out_val = out_val.gsub("\r\n"," ") output << ( "#{attr.name}=\"#{out_val}\"" ) end unless node.attributes.empty? if node.children.empty? if @ie_hack output << " " end if @@vs2010_format == false output << "\n" output << "\t"*@level end output << "/" else output << ">" # If compact and all children are text, and if the formatted output # is less than the specified width, then try to print everything on # one line skip = false if compact if node.children.inject(true) {|s,c| s & c.kind_of?(Text)} string = "" old_level = @level @level = 0 node.children.each { |child| write( child, string ) } @level = old_level if string.length < @width output << string skip = true end end end unless skip if @@vs2010_format == true output << "\n" if node.children.size > 1 else output << "\n" end @level += @indentation node.children.each { |child| next if child.kind_of?(Text) and child.to_s.strip.length == 0 write( child, output ) output << "\n" if @@vs2010_format == false } @level -= @indentation output << "\t"*@level if @@vs2010_format == false end if @@vs2010_format == true output << "\t"*@level if node.children.size > 1 end output << "" output << "\n" if @@vs2010_format == true end def write_text( node, output ) s = node.to_s() s.gsub!(/\s/,' ') s.squeeze!(" ") #Do not automatically wrap the contents of the text node in Visual Studio 2010. #This results in multiple-line build steps being broken up with incorrect line breaks. if @@vs2010_format == false s = wrap(s, 80-@level) end s.gsub!("\"",""") s.gsub!("&#x0D;&#x0A;", "\r\n") if @@vs2010_format == false s = indent_text(s, @level, " ", true) output << (' '*@level + s) end output << s end def write_comment( node, output) output << "\t" * @level super end def write_cdata( node, output) output << "\t" * @level super end def write_document( node, output ) # Ok, this is a bit odd. All XML documents have an XML declaration, # but it may not write itself if the user didn't specifically add it, # either through the API or in the input document. If it doesn't write # itself, then we don't need a carriage return... which makes this # logic more complex. node.children.each { |child| next if child == node.children[-1] and child.instance_of?(Text) unless child == node.children[0] or child.instance_of?(Text) or (child == node.children[1] and !node.children[0].writethis) output << "\n" end write( child, output ) } end private def indent_text(string, level=1, style="\t", indentfirstline=true) return string if level < 0 string.gsub(/\n/, "\n#{style*level}") end def wrap(string, width) # Recursivly wrap string at width. return string if string.length <= width place = string.rindex(' ', width) # Position in string with last ' ' before cutoff return string[0,place] + "\n" + wrap(string[place+1..-1], width) if place != nil return string # Can not wrap this string. end end end