javaclass 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +188 -194
  3. data/Readme.txt +33 -29
  4. data/examples/generate_class_lists.rb +16 -9
  5. data/history.txt +15 -4
  6. data/javaclass.gemspec +10 -8
  7. data/lib/generated/examples/chart_class_dependencies.txt +35 -0
  8. data/lib/generated/examples/chart_module_dependencies.txt +43 -0
  9. data/lib/generated/examples/check_interface_names.txt +36 -0
  10. data/lib/generated/examples/count_classes_in_modules.txt +31 -0
  11. data/lib/generated/examples/cumulative_dependencies.txt +28 -0
  12. data/lib/generated/examples/find_all_imported_types.txt +38 -0
  13. data/lib/generated/examples/find_incoming_dependency_graph.txt +73 -0
  14. data/lib/generated/examples/find_layers_of_modules.txt +70 -0
  15. data/lib/generated/examples/find_referenced_modules.txt +41 -0
  16. data/lib/generated/examples/find_unreferenced_classes.txt +66 -0
  17. data/lib/generated/examples/generate_class_lists.txt +53 -0
  18. data/lib/generated/examples/show_jar_api.txt +64 -0
  19. data/lib/generated/examples/simple_usage.txt +38 -0
  20. data/lib/javaclass/classfile/access_flag_constants.rb +32 -5
  21. data/lib/javaclass/classfile/access_flags.rb +39 -20
  22. data/lib/javaclass/classfile/attributes/attributes.rb +134 -0
  23. data/lib/javaclass/classfile/class_access_flags.rb +37 -0
  24. data/lib/javaclass/classfile/class_file_attributes.rb +62 -0
  25. data/lib/javaclass/classfile/class_version.rb +0 -1
  26. data/lib/javaclass/classfile/constant_pool.rb +17 -11
  27. data/lib/javaclass/classfile/constants/single_reference.rb +53 -0
  28. data/lib/javaclass/classfile/fields.rb +37 -0
  29. data/lib/javaclass/classfile/java_class_header.rb +22 -13
  30. data/lib/javaclass/classfile/java_class_header_shortcuts.rb +6 -2
  31. data/lib/javaclass/classfile/methods.rb +37 -0
  32. data/lib/javaclass/classlist/jar_searcher.rb +38 -4
  33. data/lib/javaclass/classpath/temporary_unpacker.rb +1 -1
  34. data/lib/javaclass/string_hexdump.rb +1 -1
  35. data/license.txt +7 -7
  36. data/test/data/access_flags/{AccessFlagsTestInner$1.class → AccessFlagsTestAnonym$1.class} +0 -0
  37. data/test/data/access_flags/{AccessFlagsTestInner.class → AccessFlagsTestAnonym.class} +0 -0
  38. data/test/data/access_flags/AccessFlagsTestAnonym.java +9 -0
  39. data/test/data/access_flags/AccessFlagsTestPublic_javap.txt +1 -0
  40. data/test/data/constant_pool/Java8_JavaFX_Animation$1_Tag15.class +0 -0
  41. data/test/data/constant_pool/Java9_Activation_module-info_Tag20.class +0 -0
  42. data/test/data/jar_searcher/JarClassListTest.jar +0 -0
  43. data/test/data/jar_searcher/PublicClass.java +44 -1
  44. data/test/data/jar_searcher/make.bat +1 -2
  45. data/test/{test_access_flags.rb → test_class_access_flags.rb} +91 -93
  46. data/test/test_class_file_attributes.rb +57 -0
  47. data/test/test_constant_pool.rb +31 -0
  48. data/test/test_jar_searcher.rb +40 -7
  49. data/test/test_javaclass_api.rb +2 -2
  50. data/test/test_string_hexdump.rb +4 -1
  51. data/test/ts_all_tests.rb +4 -2
  52. metadata +100 -116
  53. data/test/data/access_flags/AccessFlagsTestInner$2.class +0 -0
  54. data/test/data/access_flags/AccessFlagsTestInner.java +0 -13
@@ -0,0 +1,41 @@
1
+ # Example usage of JavaClass::Classpath and JavaClass::Classpath::TrackingClasspath:
2
+ # Use the classes of one module and mark all their dependencies. Then find all modules
3
+ # which were not referenced. This list contains *potential* unused libraries (modules).
4
+ # Note that the libraries may still be used by reflection or internal from other libraries.
5
+ # Author:: Peter Kofler
6
+ # Copyright:: Copyright (c) 2009, Peter Kofler.
7
+ # License:: {BSD License}[link:/files/license_txt.html]
8
+ #
9
+ # === Steps
10
+ #
11
+ # require 'javaclass/dsl/mixin'
12
+ #
13
+ # 1) create a classpath of the main module(s) under test
14
+ # main_classpath = classpath("#{main_location};#{test_location}")
15
+ # puts "#{main_classpath.count} classes found in main classpath:"
16
+ # puts " #{main_classpath.elements.join("\n ")}"
17
+ #
18
+ # 2) mix in JavaClass::Classpath::TrackingClasspath before creating any new classpaths
19
+ # require 'javaclass/classpath/tracking_classpath'
20
+ #
21
+ # 3) create the (tracking) composite classpath of the given workspace
22
+ # cp = workspace(location)
23
+ # puts "#{cp.elements.size} classpaths found under the workspace #{location}"
24
+ # puts " #{cp.elements.join("\n ")}"
25
+ # puts "#{cp.count} classes found in classpaths"
26
+ #
27
+ # 4) mark all their referenced types as accessed in the workspace
28
+ # puts 'mapping referenced classes... (can take several minutes)'
29
+ # cp.reset_access
30
+ # main_classpath.external_types.each { |clazz| cp.mark_accessed(clazz) }
31
+ # puts 'referenced classes mapped'
32
+ #
33
+ # 5a) now find non accessed modules/libraries (i.e. classpath elements)
34
+ # unused = cp.elements.find_all { |clp| clp.jar? && clp.accessed == 0 }
35
+ # puts "\n#{unused.size} unused modules found:\n #{unused.join("\n ")}"
36
+ #
37
+ # 5b) or print the list of classpath elements (e.g. JARs) with their access
38
+ # puts "\nlibrary (module path): number of accessed classes"
39
+ # puts cp.elements.map { |clp| [clp.to_s, clp.accessed] }.
40
+ # sort { |a,b| a[1] <=> b[1] }.
41
+ # map { |e| " #{e[0]}: #{e[1]}" }
@@ -0,0 +1,66 @@
1
+ # Advanced example usage of JavaClass::Classpath::TrackingClasspath. Load all
2
+ # classes of an Eclipse workspace. Then mark all referenced classes. In the end
3
+ # report remaining classes as unreferenced. This lists *potential* unused classes.
4
+ # Note that the classes may still be used by reflection. Also this can be used to
5
+ # find classes that have a certain number of references to them, e.g. only used once.
6
+ # Author:: Peter Kofler
7
+ # Copyright:: Copyright (c) 2009, Peter Kofler.
8
+ # License:: {BSD License}[link:/files/license_txt.html]
9
+ #
10
+ # === Steps
11
+ #
12
+ # require 'javaclass/dsl/mixin'
13
+ # require 'javaclass/classpath/tracking_classpath'
14
+ #
15
+ # speed up loading by skipping non source file classpaths
16
+ # Eclipse.skip_lib
17
+ #
18
+ # 1) create the (tracking) composite classpath of the given workspace
19
+ # cp = workspace(location)
20
+ # puts "#{cp.elements.size} classpaths found under the workspace #{location}:"
21
+ # puts " #{cp.elements.join("\n ")}"
22
+ #
23
+ # create a filter to limit all operations to the classes of two base packages
24
+ # filter = Proc.new { |clazz| clazz.same_or_subpackage_of?(package1) ||
25
+ # clazz.same_or_subpackage_of?(package2) }
26
+ #
27
+ # 2) load all classes in the given packages
28
+ # puts 'loading all *filtered* classes... (can take several minutes)'
29
+ # classes = cp.values(&filter)
30
+ # puts "#{classes.size} classes loaded from classpaths"
31
+ #
32
+ # 3) mark all referenced types in the given packages as accessed
33
+ # cp.reset_access
34
+ # classes.map { |clazz| clazz.imported_types }.
35
+ # flatten.
36
+ # find_all(&filter).
37
+ # each { |classname| cp.mark_accessed(classname) }
38
+ # puts '*filtered* classes mapped'
39
+ #
40
+ # 4) also mark all classes referenced from config files (e.g. hardcoded class names)
41
+ # hardcoded_classnames = scan_config_for_3rd_party_class_names(location).find_all(&filter)
42
+ # puts "#{hardcoded_classnames.size} classes references found in configs"
43
+ # hardcoded_classnames.each { |classname| cp.mark_accessed(classname) }
44
+ # puts 'hardcoded classes mapped'
45
+ #
46
+ # 5) mark unit tests (all classes ending in Test) on the test projects (classpath elements ending in .test)
47
+ # test_projects = cp.elements.find_all { |cpe| cpe.to_s =~ /\.test\// }
48
+ # test_projects.each do |project|
49
+ # project.names { |classname| classname =~ /Test(?:Case|Suite|s)?\.class/ }.
50
+ # each { |classname| project.mark_accessed(classname) }
51
+ # end
52
+ # puts 'test classes mapped'
53
+ #
54
+ # 6) find non accessed classes in specific packages and report them
55
+ # unused_classes = classes.find_all { |clazz| cp.accessed(clazz) == 0 }
56
+ # report = unused_classes.map { |clazz| "#{clazz.to_classname}" }
57
+ # puts "#{report.size} unused classes found:\n #{report.join("\n ")}"
58
+ #
59
+ # 7) find only once accessed classes and report them
60
+ # once_used_classes = classes.find_all { |clazz| cp.accessed(clazz) == 1 }
61
+ # report = once_used_classes.map { |clazz| "#{clazz.to_classname}" }
62
+ # puts "#{report.size} once used classes found:\n #{report.join("\n ")}"
63
+ #
64
+ # twice_used_classes = classes.find_all { |clazz| cp.accessed(clazz) == 2 }
65
+ # report = twice_used_classes.map { |clazz| "#{clazz.to_classname}" }
66
+ # puts "#{report.size} twice used classes found:\n #{report.join("\n ")}"
@@ -0,0 +1,53 @@
1
+ # Generate a JavaClass::ClassList, which contains all class
2
+ # names with version numbers when introduced. The list is created
3
+ # iterating all JDKs, opening the RT.jars and loading all classes.
4
+ # Author:: Peter Kofler
5
+ # Copyright:: Copyright (c) 2009, Peter Kofler.
6
+ # License:: {BSD License}[link:/files/license_txt.html]
7
+ #
8
+ # === Usage
9
+ #
10
+ # require 'javaclass/classlist/jar_searcher'
11
+ # require 'javaclass/classpath/jar_classpath'
12
+ # require 'javaclass/classlist/list'
13
+ #
14
+ # Struct to keep configuration what kind of JDK classes should be searched and added.
15
+ # JDK_CONFIG = Struct.new(:version, :label, :paths)
16
+ #
17
+ # configuration for some JDKs
18
+ # JDKS = [ JDK_CONFIG.new(...), ... ]
19
+ #
20
+ # 1) create a JavaClass::ClassList::JarSearcher
21
+ # JavaClass.unpack_jars!(:unpack)
22
+ # searcher = JavaClass::ClassList::JarSearcher.new
23
+ # searcher.skip_inner_classes = false
24
+ # searcher.skip_package_classes = true
25
+ #
26
+ # 2) filter out unwanted classes, e.g. vendor classes
27
+ # searcher.filters = %w[sun/ sunw/ com/oracle/ com/sun/ netscape/ com/rsa/
28
+ # quicktime/ com/apple/mrj/macos/carbon/ org/jcp/xml/dsig/internal/
29
+ # oracle/jrockit/ jdk/internal/ jdk/nashorn/internal/
30
+ # jdk/dynalink/internal/ jdk/incubator/http.internal/ jdk/javadoc/internal/ jdk/tools/jlink/internal/ ]
31
+ #
32
+ # 3) create a new JavaClass::ClassList::List to contain the classes
33
+ # list = JavaClass::ClassList::List.new
34
+ #
35
+ #
36
+ # Work on all lists defined in +JDKS+, add to the list and write list files.
37
+ # JDKS.each do |conf|
38
+ # # 4) iterate JARs and compile a list
39
+ # conf.paths.each { |p| list = searcher.compile_list(conf.version, p, list) }
40
+ #
41
+ # # 5) save various kind of lists
42
+ # basename = "./ClassLists/jdk#{conf.label.gsub(/\./, '')}"
43
+ # # File.open("#{basename}_new_package_classes.txt", "w") { |f| f.print list.old_access_list.collect{|m| m.sub(/\s.*$/, '')} }
44
+ # # File.open("#{basename}_all_classes.txt", "w") { |f| f.print list.plain_class_list }
45
+ # File.open("#{basename}_all_packages.txt", "w") { |f| f.print list.to_s }
46
+ # File.open("#{basename}_all_public_classes.txt", "w") { |f| f.print list.plain_class_list { |c| c.public? }.join() }
47
+ # File.open("#{basename}_new_public_classes.txt", "w") { |f| f.print list.plain_class_list { |c| c.public? and c.version.first == conf.version }.join() }
48
+ #
49
+ # baseversion = conf.label.gsub(/\.|-.+$/, '')
50
+ # File.open("./fullClassList#{baseversion}.txt", "w") { |f| f.print list.full_class_list.join() }
51
+ #
52
+ # puts "processed #{conf.label}"
53
+ # end
@@ -0,0 +1,64 @@
1
+ # Generate a JavaClass::ClassList, which contains all class of a given
2
+ # JAR in the local Maven repository. Use JavaClass::Classpath::MavenArtefact
3
+ # to identify and (possibly download) the JAR under question.
4
+ # Author:: Peter Kofler
5
+ # Copyright:: Copyright (c) 2009, Peter Kofler.
6
+ # License:: {BSD License}[link:/files/license_txt.html]
7
+ #
8
+ # === Usage
9
+ #
10
+ # require 'javaclass/classlist/jar_searcher'
11
+ # require 'javaclass/classpath/maven_artefact'
12
+ # require 'javaclass/classlist/list'
13
+ #
14
+ # Define configuration for some Maven artefacts
15
+ # JARS = [ JavaClass::Classpath::MavenArtefact.new('commons-lang', 'commons-lang', '2.6'),, ... ]
16
+ #
17
+ # For all artefacts listed in +JARS+, load classes and write list files.
18
+ # JARS.each do |artefact|
19
+ #
20
+ # 1) create a new JavaClass::ClassList::List to contain the classes of this JAR
21
+ # list = JavaClass::ClassList::List.new
22
+ #
23
+ # 2) create a JavaClass::ClassList::JarSearcher
24
+ # searcher = JavaClass::ClassList::JarSearcher.new
25
+ # searcher.skip_package_classes = true
26
+ #
27
+ # 3) create the classpath of the artefact's JAR
28
+ # artefact.download_if_needed
29
+ # classpath = artefact.classpath
30
+ #
31
+ # 4) scan the JAR and add classes to the list
32
+ # searcher.add_list_from_classpath(artefact.version, classpath, list)
33
+ #
34
+ # 5) save the list to a file named "Commons Lang 2.6.txt" (after the artefact)
35
+ # File.open("#{artefact.title} #{artefact.version}.txt", "w") do |f|
36
+ # # print title
37
+ # f.print "*** #{artefact.title}\n"
38
+ #
39
+ # list.packages.each { |pkg|
40
+ # f.print "\n"
41
+ # # print package name
42
+ # f.print "* #{pkg.name}\n"
43
+ #
44
+ # # print class names
45
+ # pkg.classes.each { |c| f.print " #{c.name}\n" }
46
+ # }
47
+ # end
48
+ #
49
+ # puts "processed #{artefact.name}"
50
+ # end
51
+ #
52
+ # 6) the generated file looks like
53
+ # *** Commons Lang
54
+ #
55
+ # * org.apache.commons.lang
56
+ # ArrayUtils
57
+ # BitField
58
+ # ...
59
+ #
60
+ # * org.apache.commons.lang.builder
61
+ # CompareToBuilder
62
+ # EqualsBuilder
63
+ # HashCodeBuilder
64
+ # ...
@@ -0,0 +1,38 @@
1
+ # Example of basic usage of JavaClass to load and inspect class files.
2
+ # Author:: Peter Kofler
3
+ # Copyright:: Copyright (c) 2009, Peter Kofler.
4
+ # License:: {BSD License}[link:/files/license_txt.html]
5
+ #
6
+ # === Usage
7
+ #
8
+ # 1) require the basic module
9
+ # require 'javaclass'
10
+ #
11
+ # 2a) load a class directly from the file system
12
+ # clazz = JavaClass.load_fs('./test/data/access_flags/AccessFlagsTestPublic.class')
13
+ #
14
+ # 2b) or get a class from the system classpath which needs _JAVA_HOME_ to be set
15
+ # cp = JavaClass.environment_classpath
16
+ # puts cp.includes?('java/lang/String.class') # => 1 (true)
17
+ #
18
+ # 2c) or look up the class from some JavaClass::Classpath by its Java qualified name
19
+ # cp = JavaClass.classpath('./test/data/access_flags')
20
+ # puts cp.includes?('AccessFlagsTestPublic') # => 1 (true)
21
+ # clazz = JavaClass.load_cp('AccessFlagsTestPublic', cp)
22
+ #
23
+ # 3) then retrieve low level information about the class
24
+ # puts clazz.version # => "50.0"
25
+ # puts clazz.constant_pool.items[1] # => "packagename/AccessFlagsTestPublic"
26
+ # puts clazz.access_flags.public? # => true
27
+ # puts clazz.access_flags.final? # => false
28
+ # puts clazz.this_class # => "packagename/AccessFlagsTestPublic"
29
+ # puts clazz.super_class # => "java/lang/Object"
30
+ # puts clazz.super_class.to_classname # => "java.lang.Object"
31
+ # puts clazz.references.referenced_methods[0] # => "java/lang/Object.<init>:()V"
32
+ # puts clazz.interfaces # => []
33
+ #
34
+ # Returned class names are not just Strings, but JavaClass::JavaQualifiedName
35
+ # puts clazz.this_class.to_java_file # => "packagename/AccessFlagsTestPublic.java"
36
+ # puts clazz.this_class.full_name # => "packagename.AccessFlagsTestPublic"
37
+ # puts clazz.this_class.package # => "packagename"
38
+ # puts clazz.this_class.simple_name # => "AccessFlagsTestPublic"
@@ -5,19 +5,46 @@ module JavaClass
5
5
  # Author:: Peter Kofler
6
6
  module AccessFlagsConstants
7
7
 
8
+ # field
9
+ # ACC_PUBLIC = 0x0001
10
+ ACC_PRIVATE = 0x0002
11
+ ACC_PROTECTED = 0x0004
12
+ # TODO How were Java 1.0's "private protected" fields? set up? (see old JVM spec)
13
+ ACC_STATIC = 0x0008
14
+ # ACC_FINAL = 0x0010
15
+ ACC_VOLATILE = 0x0040
16
+ ACC_TRANSIENT = 0x0080
17
+ # ACC_SYNTHETIC = 0x1000
18
+ # ACC_ENUM = 0x4000
19
+
20
+ # method
21
+ # ACC_PUBLIC = 0x0001
22
+ # ACC_PRIVATE = 0x0002
23
+ # ACC_PROTECTED = 0x0004
24
+ # ACC_STATIC = 0x0008
25
+ # ACC_FINAL = 0x0010
26
+ ACC_SYNCHRONIZED = 0x0020
27
+ ACC_ACC_BRIDGE = 0x0040
28
+ ACC_VARARGS = 0x0080
29
+ ACC_NATIVE = 0x0100
30
+ # ACC_ABSTRACT = 0x0400
31
+ ACC_STRICT = 0x0800
32
+ # ACC_SYNTHETIC = 0x1000
33
+
34
+ # class
8
35
  ACC_PUBLIC = 0x0001
9
36
  ACC_FINAL = 0x0010
10
37
  ACC_SUPER = 0x0020 # old invokespecial instruction semantics
11
38
  ACC_INTERFACE = 0x0200
12
39
  ACC_ABSTRACT = 0x0400
13
- ACC_SYNTHETIC = 0x1000 # TODO Check what the synthetic flag means
40
+ ACC_SYNTHETIC = 0x1000 # may vary between different compilers
14
41
  ACC_ANNOTATION = 0x2000
15
42
  ACC_ENUM = 0x4000
16
- # TODO How were Java 1.0's "private protected" fields? set up? (see old JVM spec)
17
-
18
- # Bitmask for unknown/not supported flags.
19
- ACC_OTHER = 0xffff ^ ACC_PUBLIC ^ ACC_FINAL ^ ACC_SUPER ^ ACC_INTERFACE ^ ACC_ABSTRACT ^ ACC_SYNTHETIC ^ ACC_ENUM ^ ACC_ANNOTATION
43
+ ACC_MODULE = 0x8000
20
44
 
45
+ ACC_IMPLICIT = 0x8000 # e.g. default constructor
46
+ ACC_MANDATED = 0x8000 # formal parameter implicit declared
47
+
21
48
  end
22
49
 
23
50
  end
@@ -1,19 +1,18 @@
1
- require 'javaclass/string_ux'
2
1
  require 'javaclass/classfile/access_flag_constants'
3
2
  require 'javaclass/classfile/class_format_error'
4
3
 
5
4
  module JavaClass
6
5
  module ClassFile
7
6
 
8
- # The access flags of a class or interface.
7
+ # The general JVM access flags.
9
8
  # Author:: Peter Kofler
10
9
  class AccessFlags
11
10
  include AccessFlagsConstants
12
11
 
13
12
  attr_reader :flags
14
13
 
15
- def initialize(data, pos)
16
- @flags = data.u2(pos)
14
+ def initialize(flags)
15
+ @flags = flags
17
16
  correct_flags
18
17
  assert_flags
19
18
  end
@@ -27,44 +26,64 @@ module JavaClass
27
26
  private :correct_flags
28
27
 
29
28
  def assert_flags
30
- raise ClassFormatError, "inconsistent flags #{@flags} (abstract and final)" if abstract? && final?
31
- raise ClassFormatError, "inconsistent flags #{@flags} (interface not abstract)" if interface? && !abstract?
32
- raise ClassFormatError, "inconsistent flags #{@flags} (interface is final)" if interface? && final?
33
- raise ClassFormatError, "inconsistent flags #{@flags} (annotation not interface)" if annotation? && !interface?
34
- raise ClassFormatError, "inconsistent flags #{@flags} (other value #{@flags & ACC_OTHER})" if (@flags & ACC_OTHER) != 0
29
+ raise ClassFormatError, "inconsistent flags #{flags} (abstract and final)" if abstract? && final?
30
+ raise ClassFormatError, "inconsistent flags #{flags} (interface not abstract)" if interface? && !abstract?
31
+ raise ClassFormatError, "inconsistent flags #{flags} (interface is final)" if interface? && final?
32
+ raise ClassFormatError, "inconsistent flags #{flags} (annotation not interface)" if annotation? && !interface?
35
33
  end
36
34
  private :assert_flags
37
35
 
36
+ # Return +true+ if the bit _flag_ is set.
37
+ def is?(flag)
38
+ (@flags & flag) != 0
39
+ end
40
+
38
41
  # Return +true+ if the class is public.
39
42
  def public?
40
- (@flags & ACC_PUBLIC) != 0
43
+ is? ACC_PUBLIC
41
44
  end
42
45
  alias accessible? public?
43
46
 
44
- def final?
45
- (@flags & ACC_FINAL) != 0
47
+ def private?
48
+ is? ACC_PRIVATE
46
49
  end
47
50
 
51
+ def protected?
52
+ is? ACC_PROTECTED
53
+ end
54
+
55
+ def static?
56
+ is? ACC_STATIC
57
+ end
58
+
59
+ def final?
60
+ is? ACC_FINAL
61
+ end
62
+
48
63
  def abstract?
49
- (@flags & ACC_ABSTRACT) != 0
64
+ is? ACC_ABSTRACT
65
+ end
66
+
67
+ def synthetic?
68
+ is? ACC_SYNTHETIC
50
69
  end
51
70
 
52
71
  def interface?
53
- (@flags & ACC_INTERFACE) != 0
72
+ is? ACC_INTERFACE
54
73
  end
55
74
 
56
75
  def enum?
57
- (@flags & ACC_ENUM) != 0
76
+ is? ACC_ENUM
58
77
  end
59
78
 
60
79
  def annotation?
61
- (@flags & ACC_ANNOTATION) != 0
80
+ is? ACC_ANNOTATION
62
81
  end
63
82
 
64
- # def inner?
65
- # (@flags & ACC_INNER) != 0
66
- # end
67
-
83
+ def module?
84
+ is? ACC_MODULE
85
+ end
86
+
68
87
  # Return the hex word of the flag.
69
88
  def flags_hex
70
89
  format '%4.4X', @flags
@@ -0,0 +1,134 @@
1
+ module JavaClass
2
+ module ClassFile
3
+ module Attributes
4
+
5
+ # General container of the attributes.
6
+ # Author:: Peter Kofler
7
+ class Attributes
8
+
9
+ # Size of the whole attributes structure in bytes.
10
+ attr_reader :size
11
+
12
+ # Parse the attributes structure from the bytes _data_ beginning at position _start_.
13
+ def initialize(data, start, constant_pool)
14
+ creator = AttributesCreator.new(data, start, constant_pool)
15
+ creator.create!
16
+ @attributes = creator.attributes
17
+ @size = creator.size
18
+ end
19
+
20
+ # Find the attribute with the given _name_.
21
+ def with(name)
22
+ @attributes.find { |attr| attr.name == name }
23
+ end
24
+
25
+ end
26
+
27
+ class AttributesCreator # :nodoc:
28
+
29
+ attr_reader :attributes
30
+
31
+ def initialize(data, start, constant_pool)
32
+ @data = data
33
+ @start = start
34
+ @pos = start
35
+ @constant_pool = constant_pool
36
+ end
37
+
38
+ def size
39
+ @pos - @start
40
+ end
41
+
42
+ def create!
43
+ @attributes = []
44
+
45
+ count = @data.u2(@pos)
46
+ @pos += 2
47
+
48
+ (1..count).each do |i|
49
+ attribute_name_idx = @data.u2(@pos)
50
+ @pos += 2
51
+ attribute_name = @constant_pool[attribute_name_idx].string
52
+
53
+ attribute_length = @data.u4(@pos)
54
+ @pos += 4
55
+
56
+ if attribute_name == 'SourceFile'
57
+ @attributes << source_file(attribute_name)
58
+ elsif attribute_name == 'InnerClasses'
59
+ @attributes << inner_classes(attribute_name)
60
+ else # skip
61
+ @pos += attribute_length
62
+ end
63
+
64
+ end
65
+ end
66
+
67
+ def source_file(attribute_name)
68
+ sourcefile_idx = @data.u2(@pos)
69
+ @pos += 2
70
+ source_file = @constant_pool[sourcefile_idx].string
71
+ SourceFile.new(attribute_name, source_file)
72
+ end
73
+
74
+ def inner_classes(attribute_name)
75
+ number_of_classes = @data.u2(@pos)
76
+ @pos += 2
77
+ inner_classes = (1..number_of_classes).collect do |i|
78
+ inner_class_info_idx = @data.u2(@pos)
79
+ @pos += 2
80
+
81
+ @pos += 4
82
+ inner_class_access_flags = @data.u2(@pos)
83
+ @pos += 2
84
+
85
+ inner_class_info = @constant_pool[inner_class_info_idx]
86
+
87
+ InnerClass.new(inner_class_info.class_name, AccessFlags.new(inner_class_access_flags))
88
+ end
89
+ InnerClasses.new(attribute_name, inner_classes)
90
+ end
91
+
92
+ end
93
+
94
+ # Base class of the attributes
95
+ # Author:: Peter Kofler
96
+ class Attribute
97
+ attr_reader :name
98
+
99
+ def initialize(name)
100
+ @name = name
101
+ end
102
+ end
103
+
104
+ class SourceFile < Attribute
105
+ attr_reader :source_file
106
+
107
+ def initialize(name, source_file)
108
+ super(name)
109
+ @source_file = source_file
110
+ end
111
+ end
112
+
113
+ class InnerClasses < Attribute
114
+ attr_reader :inner_classes
115
+
116
+ def initialize(name, inner_classes)
117
+ super(name)
118
+ @inner_classes = inner_classes
119
+ end
120
+ end
121
+
122
+ class InnerClass
123
+ attr_reader :class_name
124
+ attr_reader :access_flags
125
+
126
+ def initialize(name, access_flags)
127
+ @class_name = name
128
+ @access_flags = access_flags
129
+ end
130
+ end
131
+
132
+ end
133
+ end
134
+ end