require 'rexml/document'

$file_types = {}

# Returns true if the given file is an XML file
def xml_file?(file)
  `file "#{file}"`.include? "XML document text"
end

# Adds a mapping between the given syntax file and the given file extension to the hashtable
def add_file_for_extension(file, extension)
  extension = extension.strip
  file = File.basename(file).gsub(".plist", "")
  if $file_types[extension].nil?
    $file_types[extension] = [file]
  else
    $file_types[extension] << file
  end
end

# Parses the given syntax file and adds its list of file type extensions to a hashtable
def process_syntax_file(file)
  if xml_file?(file)
    begin
      xml = REXML::Document.new(File.open(file))
      xml.elements.each("plist/dict/key") do |entry|
        if entry.text == "fileTypes"
          entry.next_element.each_element { |extension| add_file_for_extension(file, extension.text) }
          break
        end
      end  
    rescue REXML::ParseException
      puts "Error: Could not parse #{File.basename(file)}"
    end
  else
    # Not an XML (plist) file. Find a line of the form "fileTypes = (a, b, c, ...)"
    IO.foreach(file) do |line|
      if line =~ /fileTypes\s?=\s?\((.*)\)/
        Regexp.last_match(1).split(',').each { |extension| add_file_for_extension(file, extension) }
        break
      end
    end
  end
end

# Searches the basedir for files of the form *tmbundle/Syntaxes/*plist and calls processSyntaxFile on them
def find_syntax_files(basedir)
  if File.exists?(basedir) and File.directory?(basedir)
  	Dir.foreach(basedir) do |file|
  		dir = "#{basedir}/#{file}"
  		if dir =~ /tmbundle$/
  		  syntaxDir = "#{dir}/Syntaxes/"
  		  if File.exists?(syntaxDir)
    		  Dir.foreach(syntaxDir) do |file|
    		    process_syntax_file("#{dir}/Syntaxes/#{file}") if file =~ /plist$/
    		  end
    		end
  		end
  	end
  end
end

# Walks through the list of file extension mappings and print out any that are mapped to more than one syntax
def print_collisions
  collisions_found = false
  $file_types.each_key do |key|
    val = $file_types[key]
    if val.length > 1
      collisions_found = true
      printf "%8s : ", key
      i = 0
      val.each do |type|
        print "#{type}"
        print ", " unless i+1 == val.length
        i += 1
      end
      puts
    end
  end
  puts "No collisions found." unless collisions_found
end

puts "Checking syntax files for extension collisions..."
find_syntax_files("/Library/Application Support/TextMate/Bundles")
print_collisions
