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.
- data/Rakefile +18 -71
- data/Readme.txt +13 -22
- data/{example_task.rb → dev/example_task.rb} +1 -1
- data/dev/saikuro_task.rb +120 -0
- data/examples/chart_class_dependencies.rb +43 -0
- data/examples/chart_module_dependencies.rb +64 -0
- data/examples/check_interface_names.rb +3 -2
- data/examples/corpus.rb +52 -12
- data/examples/count_classes_in_modules.rb +2 -1
- data/examples/cumulative_dependencies.rb +4 -3
- data/examples/find_all_imported_types.rb +11 -7
- data/examples/find_incoming_dependency_graph.rb +81 -0
- data/examples/find_layers_of_modules.rb +79 -0
- data/examples/find_referenced_modules.rb +4 -6
- data/examples/find_unreferenced_classes.rb +14 -4
- data/examples/generate_class_lists.rb +1 -0
- data/examples/show_jar_api.rb +93 -0
- data/examples/test_corpus.rb +32 -0
- data/history.txt +17 -5
- data/javaclass.gemspec +9 -8
- data/lib/javaclass/analyse/dependencies.rb +1 -1
- data/lib/javaclass/analyse/transitive_dependencies.rb +2 -2
- data/lib/javaclass/classfile/class_magic.rb +1 -1
- data/lib/javaclass/classfile/constant_pool.rb +1 -0
- data/lib/javaclass/classfile/java_class_header_as_java_name.rb +4 -0
- data/lib/javaclass/classfile/java_class_header_shortcuts.rb +2 -0
- data/lib/javaclass/classlist/jar_searcher.rb +11 -5
- data/lib/javaclass/classlist/list.rb +27 -14
- data/lib/javaclass/classpath/any_classpath.rb +1 -1
- data/lib/javaclass/classpath/eclipse_classpath.rb +45 -25
- data/lib/javaclass/classpath/factory.rb +23 -13
- data/lib/javaclass/classpath/maven_artefact.rb +62 -0
- data/lib/javaclass/classpath/tracking_classpath.rb +3 -1
- data/lib/javaclass/dependencies/class_node.rb +36 -0
- data/lib/javaclass/dependencies/classpath_node.rb +37 -0
- data/lib/javaclass/dependencies/edge.rb +41 -0
- data/lib/javaclass/dependencies/graph.rb +53 -0
- data/lib/javaclass/dependencies/graphml_serializer.rb +102 -0
- data/lib/javaclass/dependencies/node.rb +82 -0
- data/lib/javaclass/dependencies/yaml_serializer.rb +103 -0
- data/lib/javaclass/dsl/java_name_factory.rb +2 -2
- data/lib/javaclass/gems/zip_file.rb +41 -33
- data/lib/javaclass/java_name.rb +1 -1
- data/lib/javaclass/java_name_scanner.rb +9 -7
- data/lib/javaclass/string_20.rb +40 -0
- data/lib/javaclass/string_hexdump.rb +18 -2
- data/lib/javaclass/string_ux.rb +3 -7
- data/planned.txt +50 -4
- data/rake_analysis.rb +46 -0
- data/test/data/api/packagename/Broken.class +0 -0
- data/test/logging_folder_classpath.rb +2 -2
- data/test/{test_adder_tree.rb → test_adder_tree_node.rb} +1 -1
- data/test/test_class_magic.rb +1 -1
- data/test/test_eclipse_classpath.rb +1 -1
- data/test/test_edge.rb +60 -0
- data/test/test_factory.rb +1 -1
- data/test/test_graph.rb +28 -0
- data/test/test_java_name.rb +13 -1
- data/test/test_javaclass_api.rb +18 -3
- data/test/test_maven_artefact.rb +37 -0
- data/test/test_node.rb +56 -0
- data/test/test_yaml_serializer.rb +105 -0
- data/test/ts_all_tests.rb +16 -3
- metadata +46 -35
- data/examples/profiler_scratchpad.rb +0 -33
- data/lib/javaclass/analyse/ideas.txt +0 -15
- data/lib/javaclass/classpath/classpaths.txt +0 -2
- 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
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
data/lib/javaclass/java_name.rb
CHANGED
@@ -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
|
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
|
-
|
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.
|
89
|
+
@data.strip_non_printable
|
74
90
|
end
|
75
91
|
|
76
92
|
end
|
data/lib/javaclass/string_ux.rb
CHANGED
@@ -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
|
-
|
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
|
-
===
|
1
|
+
=== Defect
|
2
2
|
|
3
|
-
*
|
4
|
-
|
5
|
-
|
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
|
Binary file
|
@@ -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)
|
data/test/test_class_magic.rb
CHANGED