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

new() click to toggle source

Creates a new empty Namespace.

# File lib/squash/java/namespace.rb, line 39
def initialize
  @package_roots = Set.new
end

Public Instance Methods

add_class_alias(name, obfuscation) click to toggle source

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
add_method_alias(class_or_name, method_name, obfuscation) click to toggle source

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
add_package_alias(name, obfuscation) click to toggle source

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
argument(type_descriptor) click to toggle source

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
find_files(root, package_or_class=nil) click to toggle source

@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
java_method(klass, name) click to toggle source

**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
klass(identifier)
Alias for: package
obfuscated_argument(type_descriptor) click to toggle source

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
obfuscated_class(identifier) click to toggle source

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
obfuscated_method(klass, name) click to toggle source

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
obfuscated_package(identifier) click to toggle source

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
obfuscated_type(name) click to toggle source

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
package(identifier) click to toggle source

**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
Also aliased as: klass
path_for_class(klass) click to toggle source

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
type(name) click to toggle source

**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