class Squash::Java::Namespace
A Ruby representation of the packages, classes, methods, and fields of a Java project, with their full and obfuscated names. The {RenameLog} class loads a Namespace
from a yGuard or ProGuard rename log file.
Note: Some of the finder methods of this class and its enclosed classes are find-or-create-type methods. Read the method documentation for each method carefully.
**Another note:** A distinction is made between full and partial names (e.g., “MyClass” vs. “com.mycompany.MyClass”), and cleartext and obfuscated names (e.g., “com.mycompany.MyClass” vs. “com.A.B”). Note that there are four possible combinations of these states. Method docs will state what naming format is expected for each parameter.
Constants
- METHOD_REGEX
@private
Public Class Methods
Creates a new empty Namespace
.
# File lib/squash/java/namespace.rb, line 39 def initialize @package_roots = Set.new end
Public Instance Methods
Associates a full class name with an obfuscated name.
@param [String] name The full class name (e.g., “com.foo.bar.Baz”). @param [String] obfuscation The obfuscated name for just the last segment of
the full name (e.g., "A").
@return [Squash::Java::Class] The newly created class object.
# File lib/squash/java/namespace.rb, line 114 def add_class_alias(name, obfuscation) cl = klass(name) cl.obfuscation = obfuscation return cl end
Associates a method name with an obfuscated alias.
@param [String, Squash::Java::Class] class_or_name A full class name (e.g.,
"com.foo.bar.Baz"), or a class object.
@param [String] method_name A method name, with return value and argument
types (e.g., "com.foo.Type1 methodName(com.foo.Type2, int[])").
@return [Squash::Java::Method] The newly created method object.
# File lib/squash/java/namespace.rb, line 128 def add_method_alias(class_or_name, method_name, obfuscation) cl = (class_or_name.kind_of?(Squash::Java::Class) ? class_or_name : klass(class_or_name)) meth = java_method(cl, method_name) meth.obfuscation = obfuscation return meth end
Associates a full package name with an obfuscated name.
@param [String] name The full package name (e.g., “com.foo.bar”). @param [String] obfuscation The obfuscated name for just the last segment of
the full name (e.g., "A").
@return [Squash::Java::Package] The newly created package object.
# File lib/squash/java/namespace.rb, line 101 def add_package_alias(name, obfuscation) pkg = package(name) pkg.obfuscation = obfuscation return pkg end
Creates a new Argument for a given type descriptor. This can be a primitive (e.g., “float”) or a full class name (e.g., “com.foo.Bar”), and can be a scalar or an array (e.g., “float[]” or “com.foo.Bar[]”). **Finds or creates** the {Squash::Java::Type Type}, and **always creates** a new Argument.
@param [String] type_descriptor The type description. @return [Squash::Java::Argument] The argument object, unbound to any
{Squash::Java::Method Method}.
# File lib/squash/java/namespace.rb, line 261 def argument(type_descriptor) dimensionality = type_descriptor.scan(/\[\]/).size type_name = type_descriptor.gsub(/\[\]/, '') Squash::Java::Argument.new type(type_name), dimensionality end
@overload find_files
(root)
Attempts to locate the paths to the {Squash::Java::Class Classes} defined in this Namespace. Classes must be defined in files named after the class, organized into folders structured after the classes' packages. For example, the source of the `com.foo.bar.Baz` class should be defined in a file located at "com/foo/bar/Baz.java", somewhere inside your project root. This is a "best guess" attempt and will not work every time. Once this method is complete, any Class objects under this namespace that were successfully matched will have their {Squash::Java::Class#path path} attributes set. @param [String] root The project root. All source directories must be under this root.
# File lib/squash/java/namespace.rb, line 58 def find_files(root, package_or_class=nil) case package_or_class when nil root.sub! /\/$/, '' @package_roots.each { |pkg| find_files root, pkg } when Squash::Java::Package package_or_class.classes.each { |cl| find_files root, cl } package_or_class.children.each { |pkg| find_files root, pkg } when Squash::Java::Class class_subpath = package_or_class.subpath Find.find(root) do |project_path| if project_path[0, root.length + 2] == root + '/.' Find.prune next end if project_path[-class_subpath.length, class_subpath.length] == class_subpath package_or_class.path = project_path.sub(/^#{Regexp.escape root}\//, '') end end end end
**Finds or creates** a method by its name and parent class. Polymorphism is supported: Two methods can share the same name so long as their argument count or types are different.
@param [Squash::Java::Class] klass The class containing the method. @param [String] name The method name, with return type and arguments as
full, unobfuscated types (e.g., "com.foo.Bar myMethod(com.foo.Baz, int[])".
@return [Squash::Java::Method] The corresponding method.
# File lib/squash/java/namespace.rb, line 222 def java_method(klass, name) matches = name.match(METHOD_REGEX) or raise "Invalid method name #{name.inspect}" return_type = argument(matches[1]) method_name = matches[2] args = matches[3].split(/,\s*/).map { |arg| argument(arg) } args = [] if matches[3].empty? klass.java_methods.detect { |meth| meth.name == method_name && meth.arguments == args } || Squash::Java::Method.new(klass, method_name, return_type, *args) end
Creates a new Argument for a given type descriptor, which can be fully or partially obfuscated. This can be an unobfuscated primitive (e.g., “float”) or a possibly-obfuscated full class name (e.g., “com.foo.A”), and can be a scalar or an array (e.g., “float[]” or “com.foo.A[]”). Finds the {Squash::Java::Type Type}, and **always creates** a new Argument. Returns `nil` for unknown types.
@param [String] type_descriptor The type description. @return [Squash::Java::Argument, nil] The argument object, unbound to any
{Squash::Java::Method Method}, or `nil` if the type is not recognized.
# File lib/squash/java/namespace.rb, line 278 def obfuscated_argument(type_descriptor) dimensionality = type_descriptor.scan(/\[\]/).size type_name = type_descriptor.gsub(/\[\]/, '') type = obfuscated_type(type_name) return nil unless type Squash::Java::Argument.new type, dimensionality end
Finds a class by its obfuscated (or partially obfuscated) full name. (Technically it also works as a find-only variant of {#klass} since all some, or none of the name need be obfuscated.)
@param [String] identifier An obfuscated full class name (e.g.,
"com.foo.A.B").
@return [Squash::Java::Class, nil] The class with that obfuscated name.
# File lib/squash/java/namespace.rb, line 184 def obfuscated_class(identifier) parts = identifier.split('.') class_name = parts.pop pkg = obfuscated_package(parts.join('.')) return nil unless pkg pkg.classes.detect { |cl| cl.obfuscation == class_name || cl.name == class_name } end
Finds a method by its obfuscated name and parent class. Polymorphism is supported: Two methods can share the same name so long as their argument count or types are different.
@param [Squash::Java::Class] klass The class containing the method. @param [String] name The obfuscated method name, with return type and
arguments as full, obfuscated types (e.g., "com.foo.A myMethod(com.foo.B, int[])".
@return [Squash::Java::Method, nil] The corresponding method.
# File lib/squash/java/namespace.rb, line 242 def obfuscated_method(klass, name) matches = name.match(METHOD_REGEX) or raise "Invalid method name #{name.inspect}" return_type = obfuscated_type(matches[1]) method_name = matches[2] args = matches[3].split(/,\s*/).map { |arg| obfuscated_argument(arg) } args = [] if matches[3].empty? klass.java_methods.detect { |m| m.obfuscation == method_name && m.arguments == args } end
Finds a package by its obfuscated (or partially obfuscated) full name. (Technically it also works as a find-only variant of {#package} since all, some, or none of the name need be obfuscated.)
@param [String] identifier An obfuscated full package name (e.g.,
"com.foo.A").
@return [Squash::Java::Package, nil] The package with that obfuscated name.
# File lib/squash/java/namespace.rb, line 165 def obfuscated_package(identifier) parts = identifier.split('.') root_name = parts.shift root = @package_roots.detect { |pkg| pkg.name == root_name } if parts.empty? root else root ? root.find_obfuscated(parts.join('.')) : nil end end
Finds a class or primitive type by its obfuscated name. Primitives are never obfuscated.
@param [String] name The obfuscated full class name (e.g.,
"com.squareup.A.B") or full primitive name
@return [Squash::Java::Type, nil] The type of that name, if found.
# File lib/squash/java/namespace.rb, line 209 def obfuscated_type(name) Squash::Java::PRIMITIVES.detect { |prim| prim.name == name } || obfuscated_class(name) end
**Finds or creates** a package by its full name.
@param [String] identifier A full package name (e.g., “com.foo.bar”). @return [Squash::Java::Package] The package with that name.
# File lib/squash/java/namespace.rb, line 140 def package(identifier) parts = identifier.split('.') root_name = parts.shift root = @package_roots.detect { |pkg| pkg.name == root_name } || begin pkg = Squash::Java::Package.new(root_name) @package_roots << pkg pkg end if parts.empty? root else root.find_or_create(parts.join('.')) end end
Returns the path to a {Squash::Java::Class Class}'s source .java file, relative to the project root, if a) the class exists in the namespace, and b) the class has a known path.
@param [String] klass The full class name (parts of which can be
obfuscated), e.g., "com.foo.A.Baz".
@return [String, nil] The path to the class's source file, if known. @see find_files
# File lib/squash/java/namespace.rb, line 89 def path_for_class(klass) cl = obfuscated_class(klass) cl ? cl.path : nil end
**Finds or creates** a primitive or class type by its name.
@param [String] name The type name (e.g., “int” or “FooClass”). @return [Squash::Java::Type] The type object.
# File lib/squash/java/namespace.rb, line 198 def type(name) Squash::Java::PRIMITIVES.detect { |prim| prim.name == name } || klass(name) end