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
@@ -20,7 +20,7 @@ module JavaClass
20
20
  #
21
21
  module JavaNameFactory
22
22
 
23
- alias :__top_level_method_missing__ :method_missing # :nodoc:
23
+ alias :__top_level_method_missing__ :method_missing unless method_defined?('__top_level_method_missing__') # :nodoc:
24
24
 
25
25
  # Convert the beginning of a full qualified Java classname starting with 'java' to a JavaQualifiedName instance.
26
26
  def java
@@ -52,7 +52,7 @@ module JavaClass
52
52
  @context = fail
53
53
  end
54
54
 
55
- alias :__unused_method_missing__ :method_missing
55
+ alias :__unused_method_missing__ :method_missing unless method_defined?('__unused_method_missing__')
56
56
 
57
57
  def method_missing(method_id, *args)
58
58
  str = method_id.id2name
@@ -1,4 +1,7 @@
1
- require 'zip/zipfilesystem'
1
+ begin
2
+ # 0.x name
3
+ require 'zip/zipfilesystem'
4
+ FILESYSTEM = Zip::ZipFile
2
5
 
3
6
  # Patch the zip for invalid Linux file system flags found in some JARs.
4
7
  module Zip # :nodoc:all
@@ -58,47 +61,53 @@ module Zip # :nodoc:all
58
61
  end
59
62
 
60
63
  case @fstype
61
- when FSTYPE_UNIX
62
- @unix_perms = (@externalFileAttributes >> 16) & 07777
63
-
64
- case (@externalFileAttributes >> 28)
65
- when 04
66
- @ftype = :directory
67
- when 010
68
- @ftype = :file
69
- when 012
70
- @ftype = :link
71
- else
72
- # raise ZipInternalError, "unknown file type #{'0%o' % (@externalFileAttributes >> 28)}"
73
-
74
- # PKZIP format see http://www.pkware.com/documents/APPNOTE/APPNOTE-6.3.0.TXT
75
- # external file attributes: (4 bytes)
76
- # The mapping of the external attributes is host-system dependent (see 'version made by').
77
- # For MS-DOS, the low order byte is the MS-DOS directory attribute byte. If input came
78
- # from standard input, this field is set to zero.
79
- # for the meaning of these flags see http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute
80
-
81
- # unknown flag, fall back to name detection
82
- if name_is_directory?
83
- @ftype = :directory
84
- else
85
- @ftype = :file
86
- end
87
-
88
- end
89
-
64
+ when FSTYPE_UNIX
65
+ @unix_perms = (@externalFileAttributes >> 16) & 07777
66
+
67
+ case (@externalFileAttributes >> 28)
68
+ when 04
69
+ @ftype = :directory
70
+ when 010
71
+ @ftype = :file
72
+ when 012
73
+ @ftype = :link
90
74
  else
75
+ # raise ZipInternalError, "unknown file type #{'0%o' % (@externalFileAttributes >> 28)}"
76
+
77
+ # PKZIP format see http://www.pkware.com/documents/APPNOTE/APPNOTE-6.3.0.TXT
78
+ # external file attributes: (4 bytes)
79
+ # The mapping of the external attributes is host-system dependent (see 'version made by').
80
+ # For MS-DOS, the low order byte is the MS-DOS directory attribute byte. If input came
81
+ # from standard input, this field is set to zero.
82
+ # for the meaning of these flags see http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute
83
+
84
+ # unknown flag, fall back to name detection
91
85
  if name_is_directory?
92
86
  @ftype = :directory
93
87
  else
94
88
  @ftype = :file
95
89
  end
90
+
96
91
  end
92
+
93
+ else
94
+ if name_is_directory?
95
+ @ftype = :directory
96
+ else
97
+ @ftype = :file
98
+ end
99
+ end
97
100
  end
98
101
 
99
102
  end
100
103
  end
101
104
 
105
+ rescue LoadError
106
+ # 1.x name
107
+ require 'zip/filesystem'
108
+ FILESYSTEM = Zip::File
109
+ end
110
+
102
111
  module JavaClass
103
112
 
104
113
  # Module for wrappers around used gems to avoid direct dependencies to gems.
@@ -116,7 +125,7 @@ module JavaClass
116
125
  # Read the _file_ from archive.
117
126
  def read(file)
118
127
  begin
119
- Zip::ZipFile.open(@archive) { |zipfile| zipfile.file.read(file) }
128
+ FILESYSTEM.open(@archive) { |zipfile| zipfile.file.read(file) }
120
129
  rescue
121
130
  nil
122
131
  end
@@ -124,7 +133,7 @@ module JavaClass
124
133
 
125
134
  # List the entries of this zip for the block given.
126
135
  def entries(&block)
127
- Zip::ZipFile.foreach(@archive) do |entry|
136
+ FILESYSTEM.foreach(@archive) do |entry|
128
137
  block.call(ZipEntry.new(entry))
129
138
  end
130
139
  end
@@ -151,4 +160,3 @@ module JavaClass
151
160
 
152
161
  end
153
162
  end
154
-
@@ -56,7 +56,7 @@ module JavaClass
56
56
  # Split the simple name at the camel case boundary _pos_ and return two parts. _pos_ may be < 0 for counting backwards.
57
57
  def split_simple_name(pos)
58
58
  parts = @simple_name.scan(/([A-Z][^A-Z]+)/).flatten
59
- pos = parts.size + pos +1 if pos < 0
59
+ pos = parts.size + pos + 1 if pos < 0
60
60
  return ['', @simple_name] if pos <= 0
61
61
  return [@simple_name, ''] if pos >= parts.size
62
62
  [parts[0...pos].join, parts[pos..-1].join]
@@ -17,17 +17,17 @@ module JavaClass
17
17
  # Author:: Peter Kofler
18
18
  module JavaNameScanner
19
19
 
20
- def scan_config_for_3rd_party_class_names(path)
21
- scan_config_for_class_names(path).reject { |name| name.in_jdk? }
20
+ def scan_config_for_3rd_party_class_names(path, ignorepattern=/^\./)
21
+ scan_config_for_class_names(path, ignorepattern).reject { |name| name.in_jdk? }
22
22
  # need abstraction for - .reject { |name| name.in_jdk? } - have it three times
23
23
  # maybe a mixin for enumerables containing JavaTypes
24
24
  #JavaNameEnumerable with - .reject { |name| name.in_jdk? } - und anderen? reject_in_jdk = 3rd Party
25
25
  # and find_all { |n| n.same_or_subpackage_of?(x) } -- also very often = in_package()
26
26
  end
27
27
 
28
- # Find all possible class names in all XML and property files under _path_
29
- def scan_config_for_class_names(path)
30
- return unless File.exist? path
28
+ # Find all possible class names in all XML and property files under _path_ and optionally ignore _ignorepattern_
29
+ def scan_config_for_class_names(path, ignorepattern=/^\./)
30
+ return [] unless File.exist? path
31
31
  if File.file?(path)
32
32
  if path =~ /\.xml$|\.properties$|MANIFEST.MF$/
33
33
  scan_text_for_class_names(IO.readlines(path).join)
@@ -35,12 +35,14 @@ module JavaClass
35
35
  []
36
36
  end
37
37
  else
38
- Dir.entries(path).reject { |n| n=~/^\./ }.map { |n| scan_config_for_class_names(File.join(path, n)) }.flatten.sort
38
+ Dir.entries(path).reject { |n| n =~ /^\./ or n=~ ignorepattern }.
39
+ map { |n| scan_config_for_class_names(File.join(path, n), ignorepattern) }.
40
+ flatten.sort
39
41
  end
40
42
  end
41
43
 
42
44
  TEXT_REGEX = /
43
- (?:^|>|"|'|=|:)
45
+ (?:^|>|"|'|=|:|!)
44
46
  \s*
45
47
  ( (?:#{JavaLanguage::IDENTIFIER_REGEX}#{JavaLanguage::SEPARATOR_REGEX})+#{JavaLanguage::TYPE_REGEX} )
46
48
  \s*
@@ -0,0 +1,40 @@
1
+ # Compatibility methods to work with Ruby 1.8, 1.9 and 2.0 Strings.
2
+ # Author:: Peter Kofler
3
+ class String
4
+
5
+ RUBY19 = ''.respond_to? :codepoints
6
+
7
+ # Return the _index_'th element as byte.
8
+ def byte_at(index=0)
9
+ if RUBY19
10
+ self[index..index].unpack('C')[0]
11
+ else
12
+ self[index]
13
+ end
14
+ end
15
+
16
+ def same_bytes_as?(other)
17
+ if RUBY19
18
+ self.unpack('C*') == other.unpack('C*')
19
+ else
20
+ self == other
21
+ end
22
+ end
23
+
24
+ def number_bytes
25
+ if RUBY19
26
+ self.bytesize
27
+ else
28
+ self.length
29
+ end
30
+ end
31
+
32
+ def strip_non_printable
33
+ if RUBY19
34
+ self.unpack('C*').map { |c| if c < 32 or c > 127 then 46 else c end }.pack('C*')
35
+ else
36
+ self.gsub(Regexp.new("[^ -\x7f]", nil, 'n'), '.')
37
+ end
38
+ end
39
+
40
+ end
@@ -1,3 +1,5 @@
1
+ require 'javaclass/string_20'
2
+
1
3
  # Add some +hexdump+ helper method to dump the data contained in this String.
2
4
  # Author:: Peter Kofler
3
5
  class String
@@ -7,9 +9,11 @@ class String
7
9
  return StringLineHexdumper.empty(columns).format if size == 0
8
10
 
9
11
  input = [0, []]
10
- output = scan(/[\x00-\xff]{1,#{columns}}/).inject(input) { |result, part|
12
+ lines = 1..number_hexdump_lines(columns)
13
+ output = lines.inject(input) { |result, line_index|
11
14
  offset, previous_lines = *result
12
15
 
16
+ part = hexdump_line(line_index, columns)
13
17
  line = StringLineHexdumper.new(offset, columns, part).format
14
18
 
15
19
  [ offset + columns, previous_lines + [line] ]
@@ -17,6 +21,18 @@ class String
17
21
  offset, lines = *output
18
22
  lines.join
19
23
  end
24
+
25
+ private
26
+
27
+ def number_hexdump_lines(columns=16)
28
+ (self.number_bytes + columns - 1) / columns
29
+ end
30
+
31
+ def hexdump_line(index, columns=16)
32
+ from = (index-1) * columns
33
+ to = index * columns - 1
34
+ self[from..to]
35
+ end
20
36
 
21
37
  end
22
38
 
@@ -70,7 +86,7 @@ class StringLineHexdumper
70
86
  end
71
87
 
72
88
  def strip_non_printable
73
- @data.gsub(/[^ -\x7f]/, '.')
89
+ @data.strip_non_printable
74
90
  end
75
91
 
76
92
  end
@@ -1,16 +1,12 @@
1
+ require 'javaclass/string_20'
2
+
1
3
  # Add some +unpack+ helper methods for HI-LO byte order (network byte order) contained in this String.
2
4
  # Author:: Peter Kofler
3
5
  class String
4
6
 
5
- RUBY19 = ''.respond_to? :codepoints
6
-
7
7
  # Return the _index_'th element as byte.
8
8
  def u1(index=0)
9
- if RUBY19
10
- self[index..index].unpack('C')[0]
11
- else
12
- self[index]
13
- end
9
+ self.byte_at(index)
14
10
  end
15
11
 
16
12
  # Return the _index_'th and the next element as unsigned word.
data/planned.txt CHANGED
@@ -1,13 +1,59 @@
1
- === Planned
1
+ === Defect
2
2
 
3
- * add collecting of incoming references (mark my interfaces, mark my referenced classes)
4
- * can calculate stability
5
- * find interfaces with only 1 implementation (smell?)
3
+ * also find as dependency a class inside an annotation. This is likely a String, still could find it
4
+ @IdClass(ChangeHistoryDetailPK.class)
5
+ class ...
6
+
7
+ === Tools
8
+
9
+ * find boundaries of my own code, i.e. find leafs of my dependency hull
10
+ * find all without dependency on my code
11
+ * find all with one dependency to these
12
+ * etc.
13
+
14
+
15
+ === Classpath (JavaClass::Classpath)
16
+
17
+ * WARs and EARs contain JARs and a folder
18
+
19
+
20
+ === Class File Format (JavaClass::ClassFile)
21
+
22
+ * {understand synthetic class}[http://stackoverflow.com/questions/8540768/when-is-the-jvm-bytecode-access-modifier-flag-0x1000-hex-synthetic-set]
6
23
 
7
24
  * implement byte code awareness
8
25
  * add the byte code sequences to the methods so it can be analysed later (see JVM spec)
9
26
  * scanning of byte code for numbers, which methods loaded, which fields loaded
10
27
 
28
+ * see what {similar project by unageanu}[http://github.com/unageanu/javaclass] has to offer ;-)
29
+
30
+
31
+ === Class Analysis, on a single class (JavaClass::ClassScanner)
32
+
33
+ * count outgoing references (usage) per method, similar to general imported types
34
+ * display graph of fields and methods per class and which methods use/call which (as seen in "Code Blindness")
35
+
36
+
37
+ === Metrics/Analysis across a whole classpath (JavaClass::Analyse)
38
+
39
+ * add collecting of incoming references (mark my interfaces, mark my referenced classes)
40
+ * can calculate stability
41
+ * find interfaces with only 1 implementation (smell?)
42
+
43
+ * "Greed Detector" = "metric" if all referencing classes of a class is another package, propose moving it there.
44
+ * count outgoing references (usage) per method
45
+ * which signatures are called how often
46
+ * maybe move current method there?
47
+
48
+ * {Chidamber and Kemerer Java Metrics (ckjm)}[http://www.spinellis.gr/sw/ckjm/]
49
+ * {The CRSS Metric for Package Design Quality}[http://crpit.com/confpapers/CRPITV62Melton2.pdf]
50
+ * {Evolutionary architecture and emergent design: Emergent design through metrics}[http://www.ibm.com/developerworks/java/library/j-eaed6/]
51
+
52
+
53
+ === Test
54
+
11
55
  * test with a large set of classes
12
56
  * use all classes of all JDKs against javap (compileTheWorld)
13
57
  * use the Quality Corpus
58
+
59
+ * improve test coverage
data/rake_analysis.rb ADDED
@@ -0,0 +1,46 @@
1
+ require 'rake'
2
+ require 'rake/clean' # for clean/clobber
3
+
4
+ # Static analysis functions.
5
+ # Author:: Peter Kofler
6
+
7
+ gemspec = eval(IO.readlines('javaclass.gemspec').join)
8
+
9
+ begin
10
+ require File.dirname(__FILE__) + '/dev/saikuro_task'
11
+
12
+ # :complexity, :clobber_complexity, :recomplexity
13
+ Rake::SaikuroTask.new do |saikuro|
14
+ saikuro.files.include "#{gemspec.require_path}/**/*.rb"
15
+ end
16
+
17
+ rescue LoadError
18
+ # skip if not installed
19
+ warn("saikuro not installed. complexity not available. #{$!}")
20
+ end
21
+
22
+ # Helper method to grep all the sources for some _pattern_ words.
23
+ def egrep(pattern)
24
+ Dir['**/*'].
25
+ find_all { |fn| FileTest.file?(fn) }.
26
+ reject { |fn| File.basename(fn) =~ /^\./ }.
27
+ reject { |fn| fn =~ /^hosting\// }.
28
+ each do |fn|
29
+ line_count = 0
30
+ open(fn) do |f|
31
+ while line = f.gets
32
+ line_count += 1
33
+ if line =~ pattern
34
+ puts "#{fn}:#{line_count}:#{line.strip}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ desc 'Look for TODO and FIXME tags'
42
+ task :todo do
43
+ egrep(/#.*(FI[X]ME|TO[D]O|T[B]D|H[A]CK)/i) # use 'odd' brackets to not find myself (and not have Eclipse markers)
44
+ end
45
+
46
+ task :default => :complexity
@@ -6,8 +6,8 @@ module JavaClass
6
6
 
7
7
  # Add a field +was_called+ to see if the load_binary was called.
8
8
  class FolderClasspath
9
- alias :__old_load_binary :load_binary
10
-
9
+ alias :__old_load_binary :load_binary unless method_defined?('__old_load_binary')
10
+
11
11
  def load_binary(classname)
12
12
  @was_called = true
13
13
  __old_load_binary(classname)
@@ -1,7 +1,7 @@
1
1
  require File.dirname(__FILE__) + '/setup'
2
2
  require 'javaclass/adder_tree'
3
3
 
4
- class TestAdderTree < Test::Unit::TestCase
4
+ class TestAdderTreeNode < Test::Unit::TestCase
5
5
 
6
6
  def setup
7
7
  @tree = AdderTree.new(0)
@@ -17,7 +17,7 @@ module TestJavaClass
17
17
  end
18
18
 
19
19
  def test_bytes
20
- assert_equal("\xCA\xFE\xBA\xBE", @magic.bytes)
20
+ assert("\xCA\xFE\xBA\xBE".same_bytes_as?(@magic.bytes))
21
21
  end
22
22
 
23
23
  def test_check
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/setup'
2
2
  require 'javaclass/classpath/eclipse_classpath'
3
- require 'test/dot_classpath'
3
+ require File.dirname(__FILE__) + '/dot_classpath'
4
4
 
5
5
  module TestJavaClass
6
6
  module TestClasspath