javaclass 0.0.4 → 0.4.1

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 (68) hide show
  1. data/Rakefile +18 -71
  2. data/Readme.txt +13 -22
  3. data/{example_task.rb → dev/example_task.rb} +1 -1
  4. data/dev/saikuro_task.rb +120 -0
  5. data/examples/chart_class_dependencies.rb +43 -0
  6. data/examples/chart_module_dependencies.rb +64 -0
  7. data/examples/check_interface_names.rb +3 -2
  8. data/examples/corpus.rb +52 -12
  9. data/examples/count_classes_in_modules.rb +2 -1
  10. data/examples/cumulative_dependencies.rb +4 -3
  11. data/examples/find_all_imported_types.rb +11 -7
  12. data/examples/find_incoming_dependency_graph.rb +81 -0
  13. data/examples/find_layers_of_modules.rb +79 -0
  14. data/examples/find_referenced_modules.rb +4 -6
  15. data/examples/find_unreferenced_classes.rb +14 -4
  16. data/examples/generate_class_lists.rb +1 -0
  17. data/examples/show_jar_api.rb +93 -0
  18. data/examples/test_corpus.rb +32 -0
  19. data/history.txt +17 -5
  20. data/javaclass.gemspec +9 -8
  21. data/lib/javaclass/analyse/dependencies.rb +1 -1
  22. data/lib/javaclass/analyse/transitive_dependencies.rb +2 -2
  23. data/lib/javaclass/classfile/class_magic.rb +1 -1
  24. data/lib/javaclass/classfile/constant_pool.rb +1 -0
  25. data/lib/javaclass/classfile/java_class_header_as_java_name.rb +4 -0
  26. data/lib/javaclass/classfile/java_class_header_shortcuts.rb +2 -0
  27. data/lib/javaclass/classlist/jar_searcher.rb +11 -5
  28. data/lib/javaclass/classlist/list.rb +27 -14
  29. data/lib/javaclass/classpath/any_classpath.rb +1 -1
  30. data/lib/javaclass/classpath/eclipse_classpath.rb +45 -25
  31. data/lib/javaclass/classpath/factory.rb +23 -13
  32. data/lib/javaclass/classpath/maven_artefact.rb +62 -0
  33. data/lib/javaclass/classpath/tracking_classpath.rb +3 -1
  34. data/lib/javaclass/dependencies/class_node.rb +36 -0
  35. data/lib/javaclass/dependencies/classpath_node.rb +37 -0
  36. data/lib/javaclass/dependencies/edge.rb +41 -0
  37. data/lib/javaclass/dependencies/graph.rb +53 -0
  38. data/lib/javaclass/dependencies/graphml_serializer.rb +102 -0
  39. data/lib/javaclass/dependencies/node.rb +82 -0
  40. data/lib/javaclass/dependencies/yaml_serializer.rb +103 -0
  41. data/lib/javaclass/dsl/java_name_factory.rb +2 -2
  42. data/lib/javaclass/gems/zip_file.rb +41 -33
  43. data/lib/javaclass/java_name.rb +1 -1
  44. data/lib/javaclass/java_name_scanner.rb +9 -7
  45. data/lib/javaclass/string_20.rb +40 -0
  46. data/lib/javaclass/string_hexdump.rb +18 -2
  47. data/lib/javaclass/string_ux.rb +3 -7
  48. data/planned.txt +50 -4
  49. data/rake_analysis.rb +46 -0
  50. data/test/data/api/packagename/Broken.class +0 -0
  51. data/test/logging_folder_classpath.rb +2 -2
  52. data/test/{test_adder_tree.rb → test_adder_tree_node.rb} +1 -1
  53. data/test/test_class_magic.rb +1 -1
  54. data/test/test_eclipse_classpath.rb +1 -1
  55. data/test/test_edge.rb +60 -0
  56. data/test/test_factory.rb +1 -1
  57. data/test/test_graph.rb +28 -0
  58. data/test/test_java_name.rb +13 -1
  59. data/test/test_javaclass_api.rb +18 -3
  60. data/test/test_maven_artefact.rb +37 -0
  61. data/test/test_node.rb +56 -0
  62. data/test/test_yaml_serializer.rb +105 -0
  63. data/test/ts_all_tests.rb +16 -3
  64. metadata +46 -35
  65. data/examples/profiler_scratchpad.rb +0 -33
  66. data/lib/javaclass/analyse/ideas.txt +0 -15
  67. data/lib/javaclass/classpath/classpaths.txt +0 -2
  68. data/lib/javaclass/classscanner/ideas.txt +0 -3
@@ -0,0 +1,32 @@
1
+ # :nodoc:
2
+
3
+ require 'test/unit'
4
+ require 'test/unit/testcase'
5
+ require File.join(File.dirname(__FILE__), '..', 'examples', 'corpus')
6
+
7
+ class TestCorpus < Test::Unit::TestCase
8
+
9
+ def test_regular_corpus
10
+ c = Corpus[:HBD]
11
+ assert_equal('E:\OfficeDateien\Corpus/Java6_Web(HBD_Online)', c.location)
12
+ assert_equal('E:\OfficeDateien\Corpus/Java6_Web(HBD_Online)/classes.zip', c.classes)
13
+ assert_equal('E:\OfficeDateien\Corpus/Java6_Web(HBD_Online)/test-classes.zip', c.testClasses)
14
+ assert_equal(['at.herold'], c.packages)
15
+ assert_equal('at.herold', c.package)
16
+ end
17
+
18
+ def test_regular_corpus_without_tests
19
+ c = Corpus[:WF]
20
+ assert_equal('E:\OfficeDateien\Corpus/Java2_Swing(WF_iMagine)', c.location)
21
+ assert_equal('E:\OfficeDateien\Corpus/Java2_Swing(WF_iMagine)/classes.zip', c.classes)
22
+ assert_nil(c.testClasses)
23
+ assert_equal(['at.workforce'], c.packages)
24
+ end
25
+
26
+ def test_regular_corpus_without_packages
27
+ c = Corpus[:Harmony15]
28
+ assert_equal([], c.packages)
29
+ end
30
+
31
+ end
32
+
data/history.txt CHANGED
@@ -1,4 +1,16 @@
1
- === 0.0.4 19/12/2011
1
+ === 0.4.1 30/05/2015
2
+
3
+ * make compatible with Ruby 2.0
4
+ * upgrade rubyzip to 1.x (still supports 0.x as well)
5
+ * moved code to Bitbucket, moved API doc to code-cop.org
6
+ * Cloudbees dropped support for Ruby 1.8.6 and 1.8.7 - not testet there anymore
7
+ * add plain class Node for dependency graph
8
+ * add dependency graph and classpath (module/component/plugin) implementation
9
+ * add Maven artefact to generate classpath from group/name/version
10
+ * change version number from micro to minor which more proper reflects code
11
+ * fix some warnings shown by Ruby 1.9.1
12
+
13
+ === 0.4 named "0.0.4" 19/12/2011
2
14
 
3
15
  * convert examples to RDOC using example_task
4
16
  * added transitive dependency trees from references
@@ -14,13 +26,13 @@
14
26
  * added usage of system 7za/unzip (if available) which unpack much faster
15
27
  * added option to unpack JARs for faster access later
16
28
  * added support for Java Enum and Annotations
17
- * added DSL like shortcuts for java.* and javax.* package and class names.
29
+ * added DSL like shortcuts for java.* and javax.* package and class names
18
30
  * added interfaces implemented by the class
19
31
  * fixed string ux problem in Ruby 1.9
20
32
  * replaced leading 0 in exponent of float/double because of Linux printf
21
33
  * split Gemspec from Rakefile as proposed by Yeguda Katz
22
34
 
23
- === 0.0.3 02/10/2010
35
+ === 0.3 named "0.0.3" 02/10/2010
24
36
 
25
37
  * integrated ClassList from another project as first analyser
26
38
  * added abstraction of Java names to all class names that are returned
@@ -31,14 +43,14 @@
31
43
  * added references to used classes
32
44
  * added classpath abstraction
33
45
 
34
- === 0.0.2 08/04/2009
46
+ === 0.2 named "0.0.2" 08/04/2009
35
47
 
36
48
  * refactored code to smaller objects for version and constant pool
37
49
  * fixed float and double constant pool items
38
50
  * added tests
39
51
  * added implementation with class name and references of a class file
40
52
 
41
- === 0.0.1 01/03/2009
53
+ === 0.1 named "0.0.1" 01/03/2009
42
54
 
43
55
  * extracted initial version of javaclass-rb from ClassList project
44
56
  * reads the class version and package/public flag of a class
data/javaclass.gemspec CHANGED
@@ -1,27 +1,28 @@
1
1
  GEM_NAME = 'javaclass'
2
- GOOGLE_PROJECT = "#{GEM_NAME}-rb"
2
+ HG_PROJECT = "#{GEM_NAME}-rb"
3
3
 
4
4
  Gem::Specification.new do |s|
5
- s.version = '0.0.4'
5
+ s.version = '0.4.1'
6
6
  s.name = GEM_NAME
7
7
  s.rubyforge_project = 'javaclass' # old, just redirects
8
- s.summary = 'A parser and disassembler for Java class files'
8
+ s.summary = 'Java class files parser and disassembler for Ruby'
9
9
  s.description = 'Provides access to the package, protected, and public fields and methods of the classes passed to it together with a list of all outgoing references.'
10
10
  s.license = 'BSD'
11
- s.homepage = "http://code.google.com/p/#{GOOGLE_PROJECT}/"
11
+ s.homepage = "https://bitbucket.org/pkofler/#{HG_PROJECT}"
12
12
  s.author = 'Peter Kofler'
13
13
  s.email = 'peter dot kofler at code minus cop dot org'
14
- s.date = Time::gm(2011, 12, 19)
14
+ s.date = Time::gm(2015, 5, 30) # set current date for release
15
15
 
16
- s.files = Dir.glob('*.txt') + Dir.glob('{lib,test,examples}/**/*') + ['javaclass.gemspec', 'Rakefile', 'example_task.rb']
16
+ s.files = Dir.glob('*.txt') + Dir.glob('{lib,test,examples}/**/*') + ['javaclass.gemspec', 'Rakefile'] + Dir.glob('rake_*.rb') + Dir.glob('dev/*_task.rb')
17
17
  s.test_files = Dir.glob('test/**/test_*.rb')
18
18
  s.require_path = 'lib'
19
19
  s.add_dependency('rubyzip', '>= 0.9.1')
20
20
  s.required_ruby_version = '>= 1.8.6'
21
21
  s.required_rubygems_version = nil if s.respond_to? :required_rubygems_version=
22
22
  s.platform = Gem::Platform::RUBY
23
- s.add_development_dependency('rake', '>= 0.8.4')
24
- s.add_development_dependency('rcov', '>= 0.8.1.2')
23
+ s.add_development_dependency('rake', '>= 0.8.3')
24
+ # s.add_development_dependency('rcov', '>= 0.8.1.2')
25
+ # s.add_development_dependency('saikuro', '>= 1.2.1')
25
26
  # s.add_development_dependency('ZenTest', '>= 4.4.0')
26
27
 
27
28
  s.has_rdoc = true
@@ -1,7 +1,7 @@
1
1
  module JavaClass
2
2
 
3
3
  # The module Analyse is for separating namespaces. It contains various methods
4
- # to analyse classes accross a whole whole code bases (Classpath). The functionality
4
+ # to analyse classes accross a whole code bases (Classpath). The functionality
5
5
  # of all analysers is collected in Dsl::ClasspathAnalysers.
6
6
  # Author:: Peter Kofler
7
7
  module Analyse
@@ -24,8 +24,8 @@ module JavaClass
24
24
  end
25
25
 
26
26
  if includes?(classname)
27
- load(classname).imported_3rd_party_types.each do |classname|
28
- transitive_dependency_tree(classname, tree, &filter)
27
+ load(classname).imported_3rd_party_types.each do |type|
28
+ transitive_dependency_tree(type, tree, &filter)
29
29
  end
30
30
  end
31
31
 
@@ -16,7 +16,7 @@ module JavaClass
16
16
 
17
17
  # Return +true+ if the data was valid, i.e. if the class started with +CAFEBABE+.
18
18
  def valid?
19
- @bytes == CAFE_BABE
19
+ @bytes.same_bytes_as?(CAFE_BABE)
20
20
  end
21
21
 
22
22
  # Return the value of the magic in this class.
@@ -25,6 +25,7 @@ module JavaClass
25
25
  NAME_AND_TYPE_TAG = 12 => Constants::ConstantNameAndType,
26
26
  ASCIZ_TAG = 1 => Constants::ConstantAsciz,
27
27
  }
28
+ # TODO Java7/Java8 JDK, implement 3 new tags, see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4
28
29
 
29
30
  # Size of the whole constant pool in bytes.
30
31
  attr_reader :size
@@ -27,6 +27,10 @@ module JavaClass
27
27
  delegate :split_simple_name, :this_class
28
28
  delegate :in_jdk?, :this_class
29
29
 
30
+ def to_s
31
+ to_classname
32
+ end
33
+
30
34
  end
31
35
 
32
36
  end
@@ -15,5 +15,7 @@ module JavaClass
15
15
 
16
16
  end
17
17
 
18
+ # TODO add tests for this
19
+
18
20
  end
19
21
  end
@@ -62,16 +62,22 @@ module JavaClass
62
62
  # _list_ must provide a <code>add_class(entry, is_public, version)</code> method.
63
63
  def compile_list(version, path, list)
64
64
  cpe = Classpath::AnyClasspath.new(path)
65
- filter_classes(cpe.names).each do |entry|
66
- is_public = public?(cpe, entry)
65
+ add_list_from_classpath(version, cpe, list)
66
+ list
67
+ end
68
+
69
+ # Compile the class list for the given _classpath_ . This searches the _path_ for zips and JARs
70
+ # and adds them to the given _list_ of found classes. _version_ is a number >= 0, e.g. 2 for JDK 1.2.
71
+ # _list_ must provide a <code>add_class(entry, is_public, version)</code> method.
72
+ def add_list_from_classpath(version, classpath, list)
73
+ filter_classes(classpath.names).each do |entry|
74
+ is_public = public?(classpath, entry)
67
75
  next if @skip_package_classes && !is_public
68
76
  list.add_class(entry, is_public, version) if list
69
77
  yield(entry, is_public, version) if block_given?
70
78
  end
71
-
72
- list
73
79
  end
74
-
80
+
75
81
  end
76
82
 
77
83
  end
@@ -36,20 +36,7 @@ module JavaClass
36
36
  is_public = (versions !~ /p/)
37
37
  versions = versions.gsub(/p/, '')
38
38
 
39
- # \d, \d-\d, only \d, -\d, oder leer
40
- if versions =~ /^(\d)$/
41
- first_vers = $1.to_i
42
- elsif versions =~ /^(\d)-(\d)$/
43
- first_vers = $1.to_i
44
- last_vers = $2.to_i
45
- elsif versions =~ /^(?:bis\s|-)(\d)$/
46
- last_vers = $1.to_i
47
- elsif versions =~ /^only (\d)$/
48
- first_vers = $1.to_i
49
- last_vers = $1.to_i
50
- else
51
- raise "can't match version number #{versions} in line #{line.chomp}" unless versions == ''
52
- end
39
+ first_vers, last_vers = *split_version(versions, first_vers, last_vers)
53
40
  end
54
41
 
55
42
  first_vers.upto(last_vers) do |v|
@@ -59,6 +46,32 @@ module JavaClass
59
46
  raise "#{$!} in line #{line.chomp}: class_name=#{class_name}, versions=#{versions}, first_vers=#{first_vers}, last_vers=#{last_vers}, is_public=#{is_public}"
60
47
  end
61
48
 
49
+ private
50
+
51
+ # Split _versions_ String which is \d, \d-\d, only \d, -\d, or empty.
52
+ def split_version(versions, prior_first_vers, prior_last_vers)
53
+ if versions =~ /^(\d)$/
54
+ first_vers = $1.to_i
55
+ [first_vers, prior_last_vers]
56
+ elsif versions =~ /^(\d)-(\d)$/
57
+ first_vers = $1.to_i
58
+ last_vers = $2.to_i
59
+ [first_vers, last_vers]
60
+ elsif versions =~ /^(?:bis\s|-)(\d)$/
61
+ last_vers = $1.to_i
62
+ [prior_first_vers, last_vers]
63
+ elsif versions =~ /^only (\d)$/
64
+ first_vers = $1.to_i
65
+ last_vers = $1.to_i
66
+ [first_vers, last_vers]
67
+ else
68
+ raise "can't match version number #{versions}" unless versions == ''
69
+ [prior_first_vers, prior_last_vers]
70
+ end
71
+ end
72
+
73
+ public
74
+
62
75
  def packages
63
76
  @packages.values.sort
64
77
  end
@@ -29,7 +29,7 @@ module JavaClass
29
29
  begin
30
30
  Dir.chdir File.expand_path(path)
31
31
 
32
- Dir['*'].collect do |name|
32
+ Dir['*'].sort.collect do |name|
33
33
  if FileTest.directory?(name)
34
34
  find_jars(name)
35
35
  elsif name =~ /\.jar$|\.zip$/
@@ -31,41 +31,61 @@ module JavaClass
31
31
  unless EclipseClasspath::valid_location?(folder)
32
32
  raise IOError, "folder #{folder} not an Eclipse project"
33
33
  end
34
- dot_classpath = File.join(folder, DOT_CLASSPATH)
34
+ @folder = folder
35
+ dot_classpath = File.join(@folder, DOT_CLASSPATH)
35
36
  super(dot_classpath)
36
- classpath = IO.readlines(dot_classpath)
37
37
 
38
- classpath.find_all { |line| line =~ /kind\s*=\s*"output"/ }.each do |line|
38
+ add_entries_from(dot_classpath)
39
+ end
40
+
41
+ private
42
+
43
+ def add_entries_from(dot_classpath)
44
+ dot_classpath_lines = IO.readlines(dot_classpath)
45
+
46
+ add_output_folder(dot_classpath_lines)
47
+ add_source_output_folder(dot_classpath_lines)
48
+
49
+ @@skip_lib ||= false
50
+ unless @@skip_lib
51
+ add_local_libraries(dot_classpath_lines)
52
+
53
+ @@variables ||= Hash.new
54
+ add_variables(dot_classpath_lines)
55
+ end
56
+ end
57
+
58
+ def add_output_folder(dot_classpath_lines)
59
+ dot_classpath_lines.find_all { |line| line =~ /kind\s*=\s*"output"/ }.each do |line|
39
60
  if line =~ /path\s*=\s*"([^"]+)"/
40
- add_file_name(File.join(folder, $1))
61
+ add_file_name(File.join(@folder, $1))
41
62
  end
42
63
  end
43
-
44
- classpath.find_all { |line| line =~ /output\s*=\s*"/ }.each do |line|
64
+ end
65
+
66
+ def add_source_output_folder(dot_classpath_lines)
67
+ dot_classpath_lines.find_all { |line| line =~ /output\s*=\s*"/ }.each do |line|
45
68
  if line =~ /output\s*=\s*"([^"]+)"/
46
- add_file_name(File.join(folder, $1))
69
+ add_file_name(File.join(@folder, $1))
47
70
  end
48
71
  end
49
-
50
- @@skip_lib ||= false
51
- unless @@skip_lib
52
-
53
- classpath.find_all { |line| line =~ /kind\s*=\s*"lib"/ }.each do |line|
54
- if line =~ /path\s*=\s*"([^"]+)"/
55
- add_file_name(File.join(folder, $1))
56
- end
57
- end
58
-
59
- @@variables ||= Hash.new
60
- classpath.find_all { |line| line =~ /kind\s*=\s*"var"/ }.each do |line|
61
- if line =~ /path\s*=\s*"([^\/]+)\/([^"]+)"/
62
- path = @@variables[$1]
63
- add_file_name(File.join(path, $2)) if path
64
- end
72
+ end
73
+
74
+ def add_local_libraries(dot_classpath_lines)
75
+ dot_classpath_lines.find_all { |line| line =~ /kind\s*=\s*"lib"/ }.each do |line|
76
+ if line =~ /path\s*=\s*"([^"]+)"/
77
+ add_file_name(File.join(@folder, $1))
78
+ end
79
+ end
80
+ end
81
+
82
+ def add_variables(dot_classpath_lines)
83
+ dot_classpath_lines.find_all { |line| line =~ /kind\s*=\s*"var"/ }.each do |line|
84
+ if line =~ /path\s*=\s*"([^\/]+)\/([^"]+)"/
85
+ path = @@variables[$1]
86
+ add_file_name(File.join(path, $2)) if path
65
87
  end
66
-
67
88
  end
68
-
69
89
  end
70
90
 
71
91
  end
@@ -36,7 +36,8 @@ module JavaClass
36
36
 
37
37
  # Create a classpath from a workspace _basepath_ which contains Eclipse or Maven projects.
38
38
  def workspace(basepath, cp=CompositeClasspath.new)
39
- # check for a valid project in this folder
39
+
40
+ # check for a valid project in this basepath
40
41
  Classpath_types.each do |classpath_type|
41
42
  if classpath_type.valid_location?(basepath)
42
43
  cp.add_element(classpath_type.new(basepath))
@@ -44,21 +45,30 @@ module JavaClass
44
45
  end
45
46
  end
46
47
 
47
- # check the children as regular workspace
48
- Dir.entries(basepath).each do |entry|
49
- next if entry == '.' || entry == '..'
50
- file = File.join(basepath, entry)
51
-
52
- Classpath_types.each do |classpath_type|
53
- if classpath_type.valid_location?(file)
54
- cp.add_element(classpath_type.new(file))
55
- break
48
+ root_folder(basepath, cp)
49
+ end
50
+
51
+ # Create a classpath from a project root directory _basepath_ by looking in the children folder for regular workspaces.
52
+ def root_folder(basepath, cp=CompositeClasspath.new)
53
+ if FileTest.directory? basepath
54
+
55
+ Dir.entries(basepath).each do |entry|
56
+ next if entry == '.' || entry == '..'
57
+ file = File.join(basepath, entry)
58
+
59
+ Classpath_types.each do |classpath_type|
60
+ if classpath_type.valid_location?(file)
61
+ cp.add_element(classpath_type.new(file))
62
+ break
63
+ end
56
64
  end
57
65
  end
58
- end if FileTest.directory? basepath
59
- cp
66
+
67
+ end
68
+
69
+ cp
60
70
  end
61
-
71
+
62
72
  end
63
73
 
64
74
  end
@@ -0,0 +1,62 @@
1
+ require 'javaclass/classpath/composite_classpath'
2
+
3
+ module JavaClass
4
+ module Classpath
5
+
6
+ # A reference to a Maven artefact. This is a group/artefact/version tuple that points to a single Jar in the Maven repository.
7
+ # Author:: Peter Kofler
8
+ class MavenArtefact
9
+
10
+ attr_reader :group, :name, :version, :title
11
+
12
+ # Create a Maven artefact with given _group_ , _name_ and _version_ .
13
+ def initialize(group, name, version, title=nil)
14
+ @group = group
15
+ @name = name
16
+ @version = version
17
+ @title = title || make_title
18
+ end
19
+
20
+ # Kind of hack function to call Maven to download the current artefact if it does not exist.
21
+ def download_if_needed
22
+ unless File.exist? repository_path
23
+ puts `#{download_command}`
24
+ end
25
+ end
26
+
27
+ # Return this Maven artefact's JavaClass::Classpath. This is a single Jar in the Maven repository.
28
+ def classpath
29
+ cp = CompositeClasspath.new(basename)
30
+ cp.add_file_name(repository_path)
31
+ cp
32
+ end
33
+
34
+ # Return the Jar's file path of this artefact inside ~/.m2/repository
35
+ def repository_path
36
+ File.join(ENV['HOME'], '.m2', 'repository', @group.gsub(/\./, '/'), @name, @version, "#{basename}.jar" )
37
+ end
38
+
39
+ private
40
+
41
+ def make_title
42
+ @name.sub(/\d$/, '').split(/[\s-]/).map { |p|
43
+ if p.size < 4
44
+ p.upcase
45
+ else
46
+ p.capitalize
47
+ end
48
+ }.join(' ')
49
+ end
50
+
51
+ def basename
52
+ "#{@name}-#{@version}"
53
+ end
54
+
55
+ def download_command
56
+ "mvn org.apache.maven.plugins:maven-dependency-plugin:2.5:get -Dartifact=#{@group}:#{@name}:#{@version}"
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ end