vivo/legacy/utilities/languageSupport/i18nChecker/properties_file_checker.rb
2015-11-19 23:47:41 +00:00

220 lines
5.7 KiB
Ruby

=begin
--------------------------------------------------------------------------------
Read and interpret a properties file. Accept a default version, if appropriate.
Warn if a specialized file has no default version.
Warn about duplicate keys, keys with empty values.
Warn about file reference values with no corresponding file
Warn about keys that do not appear in the default version.
If the "complete" flag is set,
Warn if the default version is not found.
Warn about missing keys, compared to the default version.
--------------------------------------------------------------------------------
=end
class Warning
attr_reader :line
attr_reader :message
def initialize(line, message)
@line = line
@message = message
end
end
class Property
attr_reader :line
attr_reader :key
attr_reader :value
def initialize(line, key, value)
@line = line
@key = key
@value = value
end
end
class PropertiesFileChecker
# ------------------------------------------------------------------------------------
private
# ------------------------------------------------------------------------------------
#
# What path would represent the default version of this property file?
#
def figure_rootpath()
name = File.basename(path)
dirname = File.dirname(path)
extname = File.extname(path)
raise("Invalid property file name: '#{path}': too many underscores.") if name.count("_") > 2
first_underscore = name.index('_')
if first_underscore
@rootpath = File.join(dirname, name[0, first_underscore] + extname)
else
@rootpath = path
end
end
#
# Is this a default version or a locale-specific version?
#
def is_root?()
@rootpath == @path
end
def check_for_faux_continuations(lines)
ln = 0
lines.map do |line|
ln += 1
if /(\\) +$/.match(line)
@warnings << Warning.new(ln, "On a continuation line, the \\ must not be followed by spaces.")
$` + $1
else
line
end
end
end
def join_continuation_lines(lines)
(lines.size()-1).downto(0) do |i|
if /(.*)\\$/.match(lines[i])
lines[i] = $1 + lines[i+1].lstrip()
lines[i+1] = ''
end
end
return lines
end
def read_properties(lines)
ln = 0
lines.each do |line|
ln += 1
line.strip!
# ignore blank lines, and lines starting with '#' or '!'.
next if line.length == 0 || line[0] == ?# || line[0] == ?!
if line =~ /(.*?)\s*[=:]\s*(.*)/
# key and value are separated by '=' or ':' and optional whitespace.
key = $1.strip
value = $2
else
# No '=' or ':' means that the value is empty.
key = line;
value = ''
end
if dupe = @properties[key]
@warnings << Warning.new(ln, "Key '#{key}' is duplicated on line #{dupe.line}")
else
@properties[key] = Property.new(ln, key, value)
end
end
end
def check_for_root_file()
@warnings << Warning.new(0, "Found no root file '#{File.basename(@rootpath)}'") if !is_root?() && !@root
end
def scan_for_empty_values()
@properties.values.each do |p|
@warnings << Warning.new(p.line, "Value for '#{p.key}' is empty.") if p.value.empty?
end
end
def scan_for_invalid_file_references()
@properties.values.each do |p|
if /@@file\s+(.*)/.match(p.value)
file_reference = $1.strip
dir = File.dirname(@path)
path = File.join(dir, file_reference)
unless File.file?(path)
@warnings << Warning.new(p.line, "Invalid file reference '#{p.value}': file not found")
end
end
end
end
def scan_for_properties_not_present_in_root()
root_name = File.basename(@root.path)
extra_keys = @properties.keys - @root.properties.keys
extra_keys.each do |key|
p = @properties[key]
@warnings << Warning.new(p.line, "Property '#{key}' is not present in root file '#{root_name}'")
end
end
def scan_for_properties_not_present_in_derived
root_name = File.basename(@root.path)
next_line = @properties.values.max {|a, b| a.line <=> b.line}.line + 1
missing_keys = @root.properties.keys - @properties.keys
missing_keys.sort.each do |key|
@warnings << Warning.new(next_line, "No value to override '#{key}' in the root file '#{root_name}'")
end
end
# ------------------------------------------------------------------------------------
public
# ------------------------------------------------------------------------------------
def initialize(path)
@path = path
@root = nil
@rootpath = nil
@warnings = []
@properties = {}
figure_rootpath()
lines = IO.readlines(@path)
lines = check_for_faux_continuations(lines)
lines = join_continuation_lines(lines)
read_properties(lines)
end
def try_to_set_root(root)
if !is_root?
if root.path == @rootpath
@root = root
end
end
end
def path()
@path
end
def properties()
@properties
end
def warnings()
@warnings
end
#
# Analyze the properties, and say what we found.
#
def report(complete, summary)
check_for_root_file() if complete
scan_for_empty_values()
scan_for_invalid_file_references()
scan_for_properties_not_present_in_root() if @root
scan_for_properties_not_present_in_derived() if complete && @root
puts " Properties file '#{@path}', #{@properties.size()} properties"
if !@warnings.empty?
if summary
puts " #{@warnings.size} warnings."
else
@warnings.sort! {|a, b| a.line <=> b.line}
@warnings.each do |w|
puts " line #{w.line}: #{w.message}"
end
end
end
end
end