Created
December 19, 2010 05:14
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ####################################################### | |
| # | |
| # 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("<", "<").gsub(">", ">").gsub('"', """)} | |
| 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