Skip to content

Instantly share code, notes, and snippets.

@rscottm
Created December 19, 2010 05:14
Show Gist options
  • Select an option

  • Save rscottm/747119 to your computer and use it in GitHub Desktop.

Select an option

Save rscottm/747119 to your computer and use it in GitHub Desktop.
This is the code I used to build the android_api.xml file from the X.xml files (where X is 1-9) pulled from the ASOP project
#######################################################
#
# android_api_gen.rb (by Scott Moyer)
#
# This is the code I used to build the android_api.xml
# file from the X.xml files (where X is 1-9) pulled
# from the ASOP project.
#
# To get the individual versions (something like):
#
# Option 1: Clone the (large) repository
# git clone git://android.git.kernel.org/platform/frameworks/base.git
#
# Option 2: Browse to the repository and manually download
# http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree;f=api
#
#######################################################
require 'strscan'
###############################################################################
#
# Tag and CoreTag classes: These do most of the work for the children
#
class Tag
def self.children_types; @children_types ||= []; end
def self.children_classes; @children_classes ||= []; end
def self.child_tags *args; args.each{|i| child_tag i}; end
def self.default_values(hash); @default_value_hash = hash; end
def self.default_value_hash; @default_value_hash || {}; end
def self.child_tag text
children_types << text
children_classes << eval("#{text.capitalize}Tag")
define_method "add_#{text}" do |p|
children_of_tag(text) << p
instance_variable_set("@#{text}_hash", nil)
end
define_method("#{text}_list") {children_of_tag(text)}
define_method("#{text}_names") {children_of_tag(text).map{|i| i.identifier}}
define_method "#{text}_named" do |name|
unless instance_variable_get("@#{text}_hash")
h = {}
children_of_tag(text).each{|i| h[i.identifier] = i}
instance_variable_set("@#{text}_hash", h)
end
instance_variable_get("@#{text}_hash")[name]
end
end
def initialize(api, *args)
@children = []
self.class.children_types.each {|i| @children << []}
@values = args[1] || {}
@values["deprecated"] = (api || self).api_level if @values["deprecated"] == "deprecated"
self.class.default_value_hash.each{|k,v| @values.delete(k) if @values[k] == v}
api == self ? tag_start(*args) : tag_start(api, *args)
end
def method_missing name, *args
n = name.to_s
if n[-1..-1] == "?"
n = n[0..-2]
return @values[n] == "true" if @values
end
return @values[n] if @values
super(name, *args)
end
def children_of_tag name; @children[self.class.children_types.index(name)]; end
def identifier; name.to_s; end
def full_identifier(parent_identifier=nil)
parent_identifier ? "#{parent_identifier}.#{identifier}" : identifier
end
def tag_start(api, *args)
return if args[0] == self.class.name[0..-4].downcase
if (i=self.class.children_types.index(args[0]))
# puts "tag_start: #{args.map {|x| x.inspect}.join(', ')}"
@children[i] << (@current = self.class.children_classes[i].new(api || self, *args))
else
puts "***Error*** tag_start: #{args.map {|x| x.inspect}.join(', ')}" unless @current
@current.tag_start(api || self, *args)
end
end
def type
return @values["type"] if @values and @values["type"]
super
end
def add_value name, value, replace=false
@values ||= {}
@values[name] = value if @values[name].nil? or replace
end
def add_value_to_core(name, value, replace=false); end
def merge_with(other, api); end
def write_to(doc)
new_values = {}
@values.each{|k,v| new_values[k.to_s] = v.gsub("<", "&lt;").gsub(">", "&gt;").gsub('"', "&quot;")}
element = doc.add_element self.class.name[0..-4].downcase, new_values
@children.flatten.each{|i| i.write_to element}
end
end
class CoreTag < Tag
def add_value_to_core name, value, replace=false
add_value name, value, replace
@children.each{|i| i.each{|j| j.add_value_to_core(name, value,replace)}}
end
def merge_with other, api
add_value("deprecated", api.api_level, true) if other.deprecated and not deprecated
self.class.children_types.each do |tag_type|
(other.send("#{tag_type}_names") - self.send("#{tag_type}_names")).each do |i|
self.send("add_#{tag_type}", other.send("#{tag_type}_named", i))
end
(self.send("#{tag_type}_names") - other.send("#{tag_type}_names")).each do |i|
self.send("#{tag_type}_named", i).add_value_to_core "api_removed", api.api_level
end
(self.send("#{tag_type}_names") & other.send("#{tag_type}_names")).each do |i|
self.send("#{tag_type}_named", i).merge_with other.send("#{tag_type}_named", i), api
end
end
end
end
###############################################################################
#
# Tag children that represent the actual xml tags for the API
#
class ApiidTag < Tag
def identifier; @values["version"]; end
end
class ImplementsTag < Tag; end
class ParameterTag < Tag; end
class ExceptionTag < Tag; end
class FieldTag < CoreTag
default_values({
"transient" => "false",
"final" => "false",
"static" => "false",
"deprecated" => "not deprecated",
"volatile" => "false",
"visibility" => "public"
})
end
class ConstructorTag < CoreTag
child_tags "parameter", "exception"
default_values({
"final" => "false",
"static" => "false",
"deprecated" => "not deprecated",
"visibility" => "public"
})
end
class MethodTag < CoreTag
child_tags "parameter", "exception"
default_values({
"final" => "false",
"synchronized" => "false",
"native" => "false",
"abstract" => "false",
"static" => "false",
"deprecated" => "not deprecated",
"visibility" => "public",
"return" => "void"
})
# Identify a class by its name and parameters
def identifier
"#{name}(#{@children[0].map{|i| i.type}.join(",")})"
end
end
class ClassTag < CoreTag
child_tags "implements", "constructor", "field", "method"
default_values({
"final" => "false",
"abstract" => "false",
"static" => "false",
"deprecated" => "not deprecated",
"visibility" => "public"
})
end
class InterfaceTag < CoreTag
child_tags "implements", "field", "method"
default_values({
"final" => "false",
"abstract" => "false",
"static" => "false",
"deprecated" => "not deprecated",
"visibility" => "public"
})
end
class PackageTag < CoreTag
child_tags "class", "interface"
end
###############################################################################
#
# Represents a API level or builds a combined set of APIs
#
class ApiTag < CoreTag
attr_reader :number
child_tags "apiid", "package"
def self.compile_platforms(dir, platform_info)
first = nil
platform_info.each_with_index do |a, i|
current = self.new("api", {}).read_platform(dir, i+1, *a)
if first
current.api_stamp
first.merge_with current, current
end
first ||= current
end
first
end
def read_platform(dir, number, version, name=nil)
@number = number
# To switch back to using the rexml library
# REXML::Document.parse_stream(File.new("#{dir}/#{@number}.xml"), self)
doc = StringScanner.new(IO.read("#{dir}/#{@number}.xml"))
while not doc.eos?
doc.scan(/\s*</)
unless doc.scan(/\/\w+>\s*/)
name = doc.scan(/\w+/)
doc.scan(/\s+/)
values = {}
while not doc.scan(/[\/>]/)
key = doc.scan(/\w+/)
doc.scan(/="/)
value = doc.scan(/[^"]*/)
doc.scan(/"\s*/)
values[key] = value
doc.scan(/\s*/)
end
doc.scan(/>\s*/)
tag_start(name, values) # unless name == "api"
end
end
apiid_values = {"number" =>@number.to_s, "version" => version}
apiid_values["name"] = name if name
add_apiid(ApiidTag.new(self, "apiid", apiid_values))
self
end
def write_to file_name
require 'rexml/document'
d = REXML::Document.new
super d
d.write(File.open(file_name, "w"))
d
end
def api_stamp; add_value_to_core "api_added", @number.to_s; end
def api_level; @number.to_s; end
def identifier; "android-#{@number}"; end
def initialize(*args); super(self, *args); end
def tag_start(*args); super(self, *args); end
end
###############################################################################
#
# Build a single file combining all of the specified platforms
#
ApiTag.compile_platforms(".", [
["1.0", nil],
["1.1", nil],
["1.5", "cupcake"],
["1.6", "donut"],
["2.0", nil],
["2.0.1", nil],
["2.1", "eclair"],
["2.2", "froyo"],
["2.3", "gingerbread"],
]).write_to("./android_api.xml")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment