javaclass 0.0.3 → 0.0.4
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 +67 -56
- data/Readme.txt +42 -39
- data/example_task.rb +172 -0
- data/examples/check_interface_names.rb +44 -0
- data/examples/corpus.rb +19 -0
- data/examples/count_classes_in_modules.rb +38 -0
- data/examples/cumulative_dependencies.rb +39 -0
- data/examples/find_all_imported_types.rb +44 -0
- data/examples/find_referenced_modules.rb +53 -0
- data/examples/find_unreferenced_classes.rb +65 -0
- data/examples/generate_class_lists.rb +67 -43
- data/examples/profiler_scratchpad.rb +33 -0
- data/examples/simple_usage.rb +42 -0
- data/history.txt +29 -7
- data/javaclass.gemspec +31 -0
- data/lib/javaclass/adder_tree.rb +92 -0
- data/lib/javaclass/analyse/dependencies.rb +52 -0
- data/lib/javaclass/{metric/metrics.txt → analyse/ideas.txt} +2 -2
- data/lib/javaclass/analyse/transitive_dependencies.rb +52 -0
- data/lib/javaclass/classfile/access_flag_constants.rb +24 -0
- data/lib/javaclass/classfile/access_flags.rb +49 -26
- data/lib/javaclass/classfile/class_format_error.rb +37 -0
- data/lib/javaclass/classfile/class_magic.rb +16 -8
- data/lib/javaclass/classfile/class_version.rb +19 -25
- data/lib/javaclass/classfile/constant_pool.rb +110 -45
- data/lib/javaclass/classfile/constants/base.rb +33 -12
- data/lib/javaclass/classfile/constants/double_reference.rb +55 -41
- data/lib/javaclass/classfile/constants/single_reference.rb +29 -21
- data/lib/javaclass/classfile/constants/value.rb +43 -33
- data/lib/javaclass/classfile/java_class_header.rb +72 -46
- data/lib/javaclass/classfile/java_class_header_as_java_name.rb +33 -0
- data/lib/javaclass/classfile/java_class_header_shortcuts.rb +19 -0
- data/lib/javaclass/classfile/references.rb +21 -19
- data/lib/javaclass/classlist/class_entry.rb +26 -27
- data/lib/javaclass/classlist/jar_searcher.rb +34 -25
- data/lib/javaclass/classlist/list.rb +31 -31
- data/lib/javaclass/classlist/package_entry.rb +25 -24
- data/lib/javaclass/classpath/any_classpath.rb +48 -0
- data/lib/javaclass/classpath/class_not_found_error.rb +20 -0
- data/lib/javaclass/classpath/classpaths.txt +2 -2
- data/lib/javaclass/classpath/composite_classpath.rb +56 -54
- data/lib/javaclass/classpath/convention_classpath.rb +38 -0
- data/lib/javaclass/classpath/eclipse_classpath.rb +74 -0
- data/lib/javaclass/classpath/factory.rb +65 -0
- data/lib/javaclass/classpath/file_classpath.rb +47 -0
- data/lib/javaclass/classpath/folder_classpath.rb +42 -44
- data/lib/javaclass/classpath/jar_classpath.rb +91 -52
- data/lib/javaclass/classpath/java_home_classpath.rb +24 -13
- data/lib/javaclass/classpath/maven_classpath.rb +44 -0
- data/lib/javaclass/classpath/temporary_unpacker.rb +111 -0
- data/lib/javaclass/classpath/tracking_classpath.rb +144 -0
- data/lib/javaclass/classscanner/ideas.txt +3 -0
- data/lib/javaclass/classscanner/imported_types.rb +29 -0
- data/lib/javaclass/classscanner/scanners.rb +29 -0
- data/lib/javaclass/delegate_directive.rb +15 -0
- data/lib/javaclass/dsl/caching_classpath.rb +38 -0
- data/lib/javaclass/dsl/classpath_analysers.rb +27 -0
- data/lib/javaclass/dsl/java_name_factory.rb +79 -0
- data/lib/javaclass/dsl/loader.rb +42 -0
- data/lib/javaclass/dsl/loading_classpath.rb +53 -0
- data/lib/javaclass/dsl/mixin.rb +54 -0
- data/lib/javaclass/gems/zip_file.rb +154 -0
- data/lib/javaclass/java_language.rb +50 -0
- data/lib/javaclass/java_name.rb +329 -55
- data/lib/javaclass/java_name_scanner.rb +95 -0
- data/lib/javaclass/resources/iso_3166_countries.txt +240 -0
- data/lib/javaclass/resources/jdk0_packages.txt +6 -0
- data/lib/javaclass/resources/jdk1_packages.txt +6 -0
- data/lib/javaclass/resources/jdk2_packages.txt +4 -0
- data/lib/javaclass/resources/jdk3_packages.txt +6 -0
- data/lib/javaclass/resources/jdk4_packages.txt +22 -0
- data/lib/javaclass/resources/jdk5_packages.txt +5 -0
- data/lib/javaclass/resources/jdk6_packages.txt +7 -0
- data/lib/javaclass/resources/jdk7_packages.txt +0 -0
- data/lib/javaclass/resources/jdk_packages.txt +53 -0
- data/lib/javaclass/resources/reserved_words.txt +50 -0
- data/lib/javaclass/string_hexdump.rb +76 -0
- data/lib/javaclass/string_ux.rb +21 -10
- data/lib/javaclass.rb +16 -41
- data/license.txt +28 -0
- data/planned.txt +13 -0
- data/test/data/Object_102.class +0 -0
- data/test/data/Runnable_102.class +0 -0
- data/test/data/access_flags/AccessFlagsTestAnnotation.class +0 -0
- data/test/data/access_flags/AccessFlagsTestAnnotation.java +3 -0
- data/test/data/access_flags/AccessFlagsTestEnum$1.class +0 -0
- data/test/data/access_flags/AccessFlagsTestEnum.class +0 -0
- data/test/data/access_flags/AccessFlagsTestEnum.java +6 -0
- data/test/data/access_flags/AccessFlagsTestInner$1.class +0 -0
- data/test/data/access_flags/AccessFlagsTestInner$2.class +0 -0
- data/test/data/access_flags/AccessFlagsTestInner.class +0 -0
- data/test/data/access_flags/AccessFlagsTestInner.java +13 -0
- data/test/data/access_flags/AccessFlagsTestPackage.class +0 -0
- data/test/data/access_flags/AccessFlagsTestPackage.java +1 -1
- data/test/data/api/packagename/AccessFlagsTestPublic.class +0 -0
- data/test/data/class_version/ClassVersionTest17.class +0 -0
- data/test/data/class_version/make.bat +6 -2
- data/test/data/eclipse_classpath/classes/ClassVersionTest12.class +0 -0
- data/test/data/eclipse_classpath/lib/JarClasspathTest.jar +0 -0
- data/test/data/eclipse_classpath/test-classes/ClassVersionTest13.class +0 -0
- data/test/data/folder_classpath/{JarClasspathTestFolder → classes}/ClassVersionTest10.class +0 -0
- data/test/data/folder_classpath/{JarClasspathTestFolder → classes}/package/ClassVersionTest11.class +0 -0
- data/test/data/jar_classpath/JarClasspathTest.jar +0 -0
- data/test/data/jar_classpath/JarClasspathTest.zip +0 -0
- data/test/data/jar_classpath/JarClasspathTestManifest.jar +0 -0
- data/test/data/jar_classpath/JarClasspathTestMultiManifest.jar +0 -0
- data/test/data/jar_classpath/make.bat +6 -2
- data/test/data/jar_searcher/BrokenRunnable_102.class +0 -0
- data/test/data/java_home_classpath/jdk118/lib/classes.zip +0 -0
- data/test/data/java_name_scanner/META-INF/MANIFEST.MF +12 -0
- data/test/data/java_name_scanner/plugin.xml +18 -0
- data/test/data/maven_classpath/module/pom.xml +8 -0
- data/test/data/maven_classpath/module/target/classes/ClassVersionTest12.class +0 -0
- data/test/data/maven_classpath/pom.xml +8 -0
- data/test/data/maven_classpath/target/classes/ClassVersionTest10.class +0 -0
- data/test/data/maven_classpath/target/test-classes/ClassVersionTest11.class +0 -0
- data/test/data/transitive_dependencies/A.class +0 -0
- data/test/data/transitive_dependencies/A.java +5 -0
- data/test/data/transitive_dependencies/B.class +0 -0
- data/test/data/transitive_dependencies/B.java +3 -0
- data/test/data/transitive_dependencies/C.class +0 -0
- data/test/data/transitive_dependencies/C.java +3 -0
- data/test/data/transitive_dependencies/Start.class +0 -0
- data/test/data/transitive_dependencies/Start.java +4 -0
- data/test/data/transitive_dependencies/make.bat +3 -0
- data/test/data/zip_file/commons-math-2.2-broken.zip +0 -0
- data/test/data/zip_file/regenerated-with-7zip.zip +0 -0
- data/test/data/zip_file/regenerated-with-jar.zip +0 -0
- data/test/dot_classpath.rb +33 -0
- data/test/logging_folder_classpath.rb +19 -0
- data/test/setup.rb +1 -1
- data/test/test_access_flags.rb +58 -32
- data/test/test_adder_tree.rb +78 -0
- data/test/test_any_classpath.rb +39 -0
- data/test/test_base.rb +9 -7
- data/test/test_caching_classpath.rb +41 -0
- data/test/test_class_entry.rb +60 -60
- data/test/test_class_magic.rb +31 -0
- data/test/test_class_version.rb +25 -25
- data/test/test_composite_classpath.rb +22 -23
- data/test/test_constant_pool.rb +37 -13
- data/test/test_convention_classpath.rb +39 -0
- data/test/test_eclipse_classpath.rb +73 -0
- data/test/test_factory.rb +61 -0
- data/test/test_folder_classpath.rb +26 -10
- data/test/test_imported_types.rb +34 -0
- data/test/test_jar_classpath.rb +29 -14
- data/test/test_jar_searcher.rb +27 -14
- data/test/test_java_class_header.rb +22 -10
- data/test/test_java_class_header_as_java_name.rb +41 -0
- data/test/test_java_home_classpath.rb +17 -11
- data/test/test_java_name.rb +204 -64
- data/test/test_java_name_factory.rb +52 -0
- data/test/test_java_name_scanner.rb +24 -0
- data/test/test_javaclass_api.rb +43 -0
- data/test/test_list.rb +58 -44
- data/test/test_load_directive.rb +34 -0
- data/test/test_maven_classpath.rb +46 -0
- data/test/test_package_entry.rb +27 -22
- data/test/test_references.rb +14 -14
- data/test/test_string_hexdump.rb +24 -0
- data/test/test_string_ux.rb +18 -106
- data/test/test_tracking_classpath.rb +112 -0
- data/test/test_transitive_dependencies.rb +31 -0
- data/test/test_unpacking_jar_classpath.rb +43 -0
- data/test/test_zip_file.rb +33 -0
- data/test/ts_all_tests.rb +80 -18
- data/thanks.txt +2 -0
- metadata +151 -22
- data/lib/javaclass/classpath/port_ClassPathEntry.java +0 -202
- data/lib/javaclass/classpath/port_ClassPathEntryFactory.java +0 -311
- data/lib/javaclass/classpath/port_DirectoryRepository.java +0 -24
- data/lib/javaclass/metric/ccd.rb +0 -68
- data/lib/javaclass/metric/class_usage.rb +0 -41
- data/test/test_javaclass.rb +0 -22
@@ -1,32 +1,51 @@
|
|
1
|
-
require '
|
1
|
+
require 'fileutils'
|
2
|
+
require 'javaclass/gems/zip_file'
|
3
|
+
require 'javaclass/classpath/temporary_unpacker'
|
4
|
+
require 'javaclass/classpath/file_classpath'
|
5
|
+
require 'javaclass/java_language'
|
2
6
|
require 'javaclass/java_name'
|
3
7
|
|
4
8
|
module JavaClass
|
5
|
-
|
6
|
-
|
7
|
-
|
9
|
+
|
10
|
+
# Activate temporary unpacking of all JARs. This speeds up loading of classes later.
|
11
|
+
def self.unpack_jars!(flag=:unpack)
|
12
|
+
@@unpack_jars = flag
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return +true+ if JARs should be temporarily unpacked
|
16
|
+
def self.unpack_jars?
|
17
|
+
defined?(@@unpack_jars) && @@unpack_jars
|
18
|
+
end
|
19
|
+
|
20
|
+
module Classpath
|
21
|
+
|
22
|
+
# Abstraction of a ZIP or JAR on the CLASSPATH. May return additional classpath
|
23
|
+
# elements for referenced libs. This is a leaf in the classpath tree.
|
8
24
|
# Author:: Peter Kofler
|
9
|
-
class JarClasspath
|
25
|
+
class JarClasspath < FileClasspath
|
26
|
+
|
27
|
+
# Check if the _file_ is a valid location for a jar classpath.
|
28
|
+
def self.valid_location?(file)
|
29
|
+
FileTest.exist?(file) && FileTest.file?(file) && FileTest.size(file) > 0 && file =~ /\.jar$|\.zip$/
|
30
|
+
end
|
10
31
|
|
11
|
-
#
|
32
|
+
# Create a classpath with this _jarfile_ .
|
12
33
|
def initialize(jarfile)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@classes = list_classes.collect { |cl| cl.to_javaname }
|
17
|
-
@manifest =
|
18
|
-
begin
|
19
|
-
Zip::ZipFile.open(@jarfile) { |zipfile| zipfile.file.read("META-INF/MANIFEST.MF") }
|
20
|
-
rescue
|
21
|
-
nil
|
34
|
+
super(jarfile)
|
35
|
+
unless JarClasspath::valid_location?(jarfile)
|
36
|
+
raise IOError, "jarfile #{jarfile} not found/no file"
|
22
37
|
end
|
38
|
+
@jarfile = jarfile
|
39
|
+
init_classes
|
40
|
+
@manifest = JavaClass::Gems::ZipFile.new(@jarfile).read('META-INF/MANIFEST.MF')
|
41
|
+
setup_cache if JavaClass.unpack_jars?
|
23
42
|
end
|
24
43
|
|
25
|
-
# Return
|
44
|
+
# Return +true+ as this classpath element is a jar. Zip files return +false+ as well.
|
26
45
|
def jar?
|
27
46
|
@manifest != nil
|
28
47
|
end
|
29
|
-
|
48
|
+
|
30
49
|
# Return list of additional classpath elements defined in the manifest of this jarfile.
|
31
50
|
def additional_classpath
|
32
51
|
if @manifest
|
@@ -40,61 +59,81 @@ module JavaClass
|
|
40
59
|
[]
|
41
60
|
end
|
42
61
|
end
|
43
|
-
|
44
|
-
# Return the list of class names found in this jar.
|
45
|
-
def names
|
46
|
-
|
62
|
+
|
63
|
+
# Return the list of class names found in this jar. An additional block is used as _filter_ on class names.
|
64
|
+
def names(&filter)
|
65
|
+
if block_given?
|
66
|
+
@class_names.find_all { |n| filter.call(n) }
|
67
|
+
else
|
68
|
+
@class_names.dup
|
69
|
+
end
|
47
70
|
end
|
48
|
-
|
71
|
+
|
49
72
|
# Return if _classname_ is included in this jar.
|
50
73
|
def includes?(classname)
|
51
|
-
@
|
74
|
+
@class_lookup[to_key(classname).file_name]
|
52
75
|
end
|
53
|
-
|
76
|
+
|
54
77
|
# Load the binary data of the file name or class name _classname_ from this jar.
|
55
78
|
def load_binary(classname)
|
56
|
-
|
57
|
-
|
58
|
-
|
79
|
+
key = to_key(classname)
|
80
|
+
if JavaClass.unpack_jars?
|
81
|
+
@delegate.load_binary(key)
|
82
|
+
else
|
83
|
+
unless includes?(key)
|
84
|
+
raise ClassNotFoundError.new(key, @jarfile)
|
85
|
+
end
|
86
|
+
JavaClass::Gems::ZipFile.new(@jarfile).read(key).freeze
|
59
87
|
end
|
60
88
|
end
|
61
|
-
|
89
|
+
|
62
90
|
# Return the number of classes in this jar.
|
63
91
|
def count
|
64
|
-
@
|
65
|
-
end
|
66
|
-
|
67
|
-
def to_s
|
68
|
-
@jarfile
|
69
|
-
end
|
70
|
-
|
71
|
-
def ==(other)
|
72
|
-
other.class == JarClasspath && other.to_s == self.to_s
|
92
|
+
@class_names.size
|
73
93
|
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
# Return the list of classnames (in fact file names) found in this jarfile.
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# Return the list of classnames (in fact file names) found in this jarfile.
|
78
98
|
def list_classes
|
79
99
|
list = []
|
80
|
-
|
100
|
+
JavaClass::Gems::ZipFile.new(@jarfile).entries do |entry|
|
81
101
|
name = entry.name
|
82
|
-
next unless entry.file? and name =~
|
83
|
-
list << name
|
102
|
+
next unless entry.file? and name =~ JavaLanguage::CLASS_REGEX # class file
|
103
|
+
list << name
|
84
104
|
end
|
85
|
-
list
|
105
|
+
list
|
106
|
+
end
|
107
|
+
|
108
|
+
# Set up the class names.
|
109
|
+
def init_classes
|
110
|
+
@class_names = list_classes.
|
111
|
+
reject { |n| n =~ /package-info\.class$/ }.
|
112
|
+
find_all { |n| valid_java_name?(n) }.
|
113
|
+
sort.
|
114
|
+
collect { |cl| JavaClassFileName.new(cl) }
|
115
|
+
pairs = @class_names.map { |name| [name.file_name, 1] }.flatten
|
116
|
+
@class_lookup = Hash[ *pairs ] # file_name (String) => anything
|
86
117
|
end
|
87
118
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
classname.gsub(/\./,'/') + '.class'
|
119
|
+
def valid_java_name?(name)
|
120
|
+
if JavaClassFileName.valid?(name)
|
121
|
+
true
|
92
122
|
else
|
93
|
-
|
123
|
+
warn("skipping invalid class file name #{name} in classpath #{@jarfile}")
|
124
|
+
false
|
94
125
|
end
|
95
126
|
end
|
96
127
|
|
128
|
+
# Set up the temporary unpacking. This sets the delegate field for future use.
|
129
|
+
def setup_cache
|
130
|
+
unpacker = TemporaryUnpacker.new(@jarfile)
|
131
|
+
unpacker.create_temporary_folder
|
132
|
+
unpacker.unpack!
|
133
|
+
@delegate = FolderClasspath.new(unpacker.folder)
|
134
|
+
end
|
135
|
+
|
97
136
|
end
|
98
|
-
|
137
|
+
|
99
138
|
end
|
100
|
-
end
|
139
|
+
end
|
@@ -1,40 +1,51 @@
|
|
1
1
|
require 'javaclass/classpath/jar_classpath'
|
2
2
|
|
3
3
|
module JavaClass
|
4
|
-
module Classpath
|
5
|
-
|
6
|
-
# Abstraction of the Java boot CLASSPATH. May return additional classpath
|
4
|
+
module Classpath
|
5
|
+
|
6
|
+
# Abstraction of the Java boot CLASSPATH. May return additional classpath
|
7
|
+
# elements for endorsed libs. This is a leaf in the classpath tree.
|
7
8
|
# Author:: Peter Kofler
|
8
9
|
class JavaHomeClasspath < JarClasspath
|
10
|
+
|
11
|
+
RT_JAR = 'rt.jar'
|
9
12
|
|
10
|
-
#
|
13
|
+
# Create a classpath from this _javahome_ directory.
|
11
14
|
def initialize(javahome)
|
12
|
-
if
|
15
|
+
if exist?(rtjar=File.join(javahome, 'lib', RT_JAR))
|
16
|
+
super(rtjar)
|
17
|
+
elsif exist?(rtjar=File.join(javahome, 'jre', 'lib', RT_JAR))
|
13
18
|
super(rtjar)
|
14
|
-
elsif
|
19
|
+
elsif exist?(rtjar=File.join(javahome, 'lib', 'classes.zip')) # Java 1.1 home with lib/classes.zip
|
15
20
|
super(rtjar)
|
16
21
|
else
|
17
|
-
raise IOError, "
|
22
|
+
raise IOError, "#{RT_JAR} not found in java home #{javahome}"
|
18
23
|
end
|
19
24
|
@lib = File.dirname(rtjar)
|
20
25
|
end
|
21
|
-
|
26
|
+
|
22
27
|
# Return list of additional classpath elements, e.g. endorsed libs found in this Java Home.
|
23
28
|
def additional_classpath
|
24
29
|
list = super
|
25
|
-
|
26
|
-
if FileTest.exist? ext=File.join(@lib, 'ext')
|
30
|
+
|
31
|
+
if FileTest.exist? ext=File.join(@lib, 'ext')
|
27
32
|
current = Dir.getwd
|
28
33
|
Dir.chdir ext
|
29
|
-
|
34
|
+
|
30
35
|
list += Dir['*.jar'].collect { |jar| File.join(ext, jar) }
|
31
|
-
|
36
|
+
|
32
37
|
Dir.chdir current
|
33
38
|
end
|
34
39
|
list
|
35
40
|
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def exist?(file)
|
45
|
+
FileTest.exist?(file) && FileTest.file?(file)
|
46
|
+
end
|
36
47
|
|
37
48
|
end
|
38
|
-
|
49
|
+
|
39
50
|
end
|
40
51
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'javaclass/classpath/composite_classpath'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
module Classpath
|
5
|
+
|
6
|
+
# A Maven folder structure aware classpath. Maven submodules are supported.
|
7
|
+
# Author:: Peter Kofler
|
8
|
+
class MavenClasspath < CompositeClasspath
|
9
|
+
|
10
|
+
POM_XML = 'pom.xml'
|
11
|
+
|
12
|
+
# Check if the _file_ is a valid location for a Maven classpath.
|
13
|
+
def self.valid_location?(file)
|
14
|
+
FileTest.exist?(file) && FileTest.directory?(file) && FileTest.exist?(File.join(file, POM_XML))
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create a classpath for a Maven base project _folder_
|
18
|
+
def initialize(folder)
|
19
|
+
unless MavenClasspath::valid_location?(folder)
|
20
|
+
raise IOError, "folder #{folder} not a Maven project"
|
21
|
+
end
|
22
|
+
pom = File.join(folder, POM_XML)
|
23
|
+
super(pom)
|
24
|
+
add_if_exist(File.join(folder, 'target/classes'))
|
25
|
+
add_if_exist(File.join(folder, 'target/test-classes'))
|
26
|
+
|
27
|
+
# look for submodules
|
28
|
+
Dir.entries(folder).each do |dir|
|
29
|
+
next if dir =~ /^(?:\.|\.\.|src|target|pom.xml|\.settings)$/
|
30
|
+
folder = File.join(folder, dir)
|
31
|
+
add_element(MavenClasspath.new(folder)) if MavenClasspath::valid_location?(folder)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def add_if_exist(folder)
|
38
|
+
add_file_name(folder) if FileTest.exist?(folder) && FileTest.directory?(folder)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'javaclass/gems/zip_file'
|
3
|
+
require 'javaclass/java_language'
|
4
|
+
|
5
|
+
module JavaClass
|
6
|
+
module Classpath
|
7
|
+
|
8
|
+
# Unpack a JAR (ZIP) into a temporary folder.
|
9
|
+
# Author:: Peter Kofler
|
10
|
+
class TemporaryUnpacker
|
11
|
+
|
12
|
+
# Command templates for external too like 7zip or zip.
|
13
|
+
COMMANDS = [
|
14
|
+
# 7zip 9.20
|
15
|
+
'7za x -bd -o<folder> -y <jar> 2>&1',
|
16
|
+
# Unzip 5.42
|
17
|
+
'unzip -o -qq <jar> -d <folder> 2>&1',
|
18
|
+
# WinZip 8.1
|
19
|
+
'WinZip32.exe -min -e -o <jar> <folder>',
|
20
|
+
]
|
21
|
+
|
22
|
+
# The temporary folder. This folder will be deleted after Ruby shuts down.
|
23
|
+
attr_reader :folder
|
24
|
+
|
25
|
+
# Set the given _jarfile_ to unpack.
|
26
|
+
def initialize(jarfile)
|
27
|
+
@jarfile = jarfile
|
28
|
+
|
29
|
+
if !defined?(@@unpack_strategies)
|
30
|
+
# use unzip first, fallback by hand
|
31
|
+
@@unpack_strategies = COMMANDS.map{ |c| Proc.new{ |jar, folder| TemporaryUnpacker::unpack_shell(c, jar, folder) } } +
|
32
|
+
[ Proc.new{ |jar, folder| TemporaryUnpacker::unpack_ruby(jar, folder) } ]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Create the temporary folder where it will be unpacked to.
|
37
|
+
def create_temporary_folder
|
38
|
+
folder = File.join(find_temp_folder, "temp_#{File.basename(@jarfile)}_#{Time.now.to_i.to_s}")
|
39
|
+
FileUtils.mkdir_p(folder)
|
40
|
+
at_exit { FileUtils.rm_r(folder) }
|
41
|
+
@folder = folder
|
42
|
+
end
|
43
|
+
|
44
|
+
# Unpack the given jar file.
|
45
|
+
def unpack!
|
46
|
+
unless defined?(@folder) && @folder
|
47
|
+
raise IOError, 'no temporary folder created'
|
48
|
+
end
|
49
|
+
|
50
|
+
# Find the first working strategy and keep it
|
51
|
+
if ! @@unpack_strategies.first.call(@jarfile, @folder)
|
52
|
+
warn("Dropping unpacker for #{@jarfile}. Install 7zip or unzip!")
|
53
|
+
@@unpack_strategies.delete_at(0)
|
54
|
+
if @@unpack_strategies.empty?
|
55
|
+
raise 'no suitable unpack strategy found'
|
56
|
+
end
|
57
|
+
unpack!
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return the temp folder if a variable is set, else returm /tmp.
|
62
|
+
def find_temp_folder
|
63
|
+
TemporaryUnpacker::escape_folder(
|
64
|
+
if ENV['TEMP']
|
65
|
+
ENV['TEMP'] # Windows
|
66
|
+
elsif ENV['TMP']
|
67
|
+
ENV['TMP']
|
68
|
+
else
|
69
|
+
'/tmp'
|
70
|
+
end
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Escape _folder_ if it contains blanks.
|
77
|
+
def self.escape_folder(folder)
|
78
|
+
if folder =~ / / then "\"#{folder}\"" else folder end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Unpack _jarfile_ into _folder_ using external executeable using the _command_ string. Return +true+ for success.
|
82
|
+
def self.unpack_shell(command, jarfile, folder)
|
83
|
+
begin
|
84
|
+
`#{command.gsub(/<folder>/, escape_folder(folder)).gsub(/<jar>/, escape_folder(jarfile))}`
|
85
|
+
$?.to_i == 0
|
86
|
+
rescue
|
87
|
+
false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Unpack _jarfile_ into _folder_ using Ruby's Rubyzip gem. This is very slow. Return +true+ for success.
|
92
|
+
def self.unpack_ruby(jarfile, folder)
|
93
|
+
# warn('unpacking with slow ruby unpacker')
|
94
|
+
zip_file = JavaClass::Gems::ZipFile.new(jarfile)
|
95
|
+
zip_file.entries do |entry|
|
96
|
+
name = entry.name
|
97
|
+
next unless entry.file? and name =~ JavaLanguage::CLASS_REGEX # class file
|
98
|
+
|
99
|
+
f_path = File.join(folder, entry.name)
|
100
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
101
|
+
unless File.exist?(f_path)
|
102
|
+
File.open(f_path, 'wb') { |file| file.write(zip_file.read(name)) }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'javaclass/classpath/composite_classpath'
|
3
|
+
|
4
|
+
module JavaClass
|
5
|
+
module Classpath
|
6
|
+
|
7
|
+
# A delegator classpath that tracks which classes have been accessed. For an example see
|
8
|
+
# {how to find (un)referenced JARs}[link:/files/lib/generated/examples/find_referenced_modules_txt.html].
|
9
|
+
# Author:: Peter Kofler
|
10
|
+
class TrackingClasspath < SimpleDelegator
|
11
|
+
|
12
|
+
# Create a tracked instance of the _classpath_ .
|
13
|
+
def initialize(classpath)
|
14
|
+
unless classpath.respond_to?(:load_binary) || classpath.respond_to?(:load)
|
15
|
+
raise ArgumentError, "wrong type of delegatee #{classpath.class}"
|
16
|
+
end
|
17
|
+
@classpath = classpath
|
18
|
+
reset_access
|
19
|
+
super(classpath)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the wrapped classpath element (+self+) of this decorated classpath.
|
23
|
+
def elements
|
24
|
+
if [FolderClasspath, JarClasspath].include?(@classpath.class)
|
25
|
+
[self]
|
26
|
+
else
|
27
|
+
@classpath.elements
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Reset all prior marked access.
|
32
|
+
def reset_access
|
33
|
+
@accessed = Hash.new(0) # class_file (JavaClassFileName) => cnt
|
34
|
+
end
|
35
|
+
|
36
|
+
# Load the binary and mark the _classname_ as accessed.
|
37
|
+
def load_binary(classname)
|
38
|
+
key = to_key(classname)
|
39
|
+
mark_accessed(key)
|
40
|
+
@classpath.load_binary(key)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Read and disassemble the given class _classname_ and mark as accessed.
|
44
|
+
def load(classname)
|
45
|
+
key = to_key(classname)
|
46
|
+
mark_accessed(key)
|
47
|
+
@classpath.load(key)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Mark the _classname_ as accessed. Return the number of accesses so far.
|
51
|
+
def mark_accessed(classname)
|
52
|
+
key = to_key(classname)
|
53
|
+
|
54
|
+
if @classpath.includes?(key)
|
55
|
+
|
56
|
+
# hash keys need to be frozen to keep state
|
57
|
+
if !@accessed.include?(key)
|
58
|
+
key = key.freeze
|
59
|
+
end
|
60
|
+
|
61
|
+
@accessed[key] += 1
|
62
|
+
else
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Was the _classname_ accessed then return the count? If _classname_ is +nil+ then check if any class was accessed.
|
68
|
+
def accessed(classname=nil)
|
69
|
+
if classname
|
70
|
+
key = to_key(classname)
|
71
|
+
@accessed[key]
|
72
|
+
else
|
73
|
+
@accessed.values.inject(0) {|s,e| s + e }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return the classnames of all accessed classes. This is a list of frozen JavaClassFileName.
|
78
|
+
def all_accessed
|
79
|
+
@accessed.keys.sort
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def to_key(classname)
|
85
|
+
classname.to_javaname.to_class_file
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
class CompositeClasspath
|
91
|
+
|
92
|
+
# Wrap the _elem_ classpath with a new TrackingClasspath and add it to the list of elements.
|
93
|
+
alias __old__add_element__ add_element
|
94
|
+
|
95
|
+
def add_element(elem)
|
96
|
+
unless @elements.find { |cpe| cpe == elem }
|
97
|
+
if [FolderClasspath, JarClasspath].include?(elem.class)
|
98
|
+
__old__add_element__(TrackingClasspath.new(elem))
|
99
|
+
else
|
100
|
+
__old__add_element__(elem)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Reset all prior marked access in child elements. (See TrackingClasspath)
|
106
|
+
def reset_access
|
107
|
+
@elements.each { |e| e.reset_access }
|
108
|
+
end
|
109
|
+
|
110
|
+
# Mark the _classname_ as accessed. Return the number of accesses so far. (See TrackingClasspath)
|
111
|
+
def mark_accessed(classname)
|
112
|
+
key = to_key(classname)
|
113
|
+
found = find_element_for(key)
|
114
|
+
if found
|
115
|
+
found.mark_accessed(key)
|
116
|
+
else
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Was the _classname_ accessed then return the count? If _classname_ is +nil+
|
122
|
+
# then check if any class was accessed. (See TrackingClasspath)
|
123
|
+
def accessed(classname=nil)
|
124
|
+
if classname
|
125
|
+
key = to_key(classname)
|
126
|
+
found = find_element_for(key)
|
127
|
+
if found then found.accessed(key) else 0 end
|
128
|
+
else
|
129
|
+
@elements.inject(0) do |s,e|
|
130
|
+
accessed = e.accessed
|
131
|
+
if accessed then s + accessed else s end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Return the classnames of all accessed classes in child elements. (See TrackingClasspath)
|
137
|
+
def all_accessed
|
138
|
+
@elements.map { |cp| cp.all_accessed }.flatten.sort
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
module ClassScanner
|
5
|
+
|
6
|
+
# Add analysis for imported types to ClassFile::JavaClassHeader.
|
7
|
+
# Author:: Peter Kofler
|
8
|
+
class ImportedTypes < SimpleDelegator
|
9
|
+
|
10
|
+
# Decorate JavaClassHeader _header_ to add imported types lazy scanner.
|
11
|
+
def initialize(header)
|
12
|
+
super(header)
|
13
|
+
@imported_types = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
# Determine the imported types of this class and return their names. This does not contain the name if this class itself.
|
17
|
+
def imported_types
|
18
|
+
@imported_types ||= references.used_classes.collect { |c| c.class_name.to_classname }.sort
|
19
|
+
end
|
20
|
+
|
21
|
+
# Determine the imported types of this class which are not from the JDK. This are all imported_types - all jdk types.
|
22
|
+
def imported_3rd_party_types
|
23
|
+
imported_types.reject { |name| name.in_jdk? }
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'javaclass/classscanner/imported_types'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
|
5
|
+
# The module ClassScanner is for separating namespaces. It contains various
|
6
|
+
# decorators that scan and analyse single class files and provide additional
|
7
|
+
# information about the class, e.g. Cyclomatic Complexity. For analysis across
|
8
|
+
# whole code bases ("classpaths") see module Analyse.
|
9
|
+
# Author:: Peter Kofler
|
10
|
+
module ClassScanner
|
11
|
+
|
12
|
+
# ClassScanner factory methods to create different kind of scanner decorators.
|
13
|
+
# Author:: Peter Kofler
|
14
|
+
module Scanners
|
15
|
+
|
16
|
+
# Scan parsed _header_ for (selected) _features_ . This ties together all scanners.
|
17
|
+
def analyse(header, features=:all)
|
18
|
+
# later add feature selection if needed, argument determines delegators, :none, :some, :all
|
19
|
+
ImportedTypes.new(
|
20
|
+
header
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
alias :decorate analyse
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Delegation directives.
|
2
|
+
# Author:: Peter Kofler
|
3
|
+
module DelegateDirective # :nodoc:
|
4
|
+
|
5
|
+
# Directive to create a delegating method _method_name_ to the result of my own method _delegate_ without arguments.
|
6
|
+
def delegate(method_name, delegate)
|
7
|
+
self.module_eval("def #{method_name}(*obj) #{delegate}.#{method_name}(*obj) end")
|
8
|
+
end
|
9
|
+
|
10
|
+
# Directive to create a delegating method _method_name_ to the field _field_name_ .
|
11
|
+
def delegate_field(method_name, field_name)
|
12
|
+
self.module_eval("def #{method_name}(*obj) @#{field_name}.#{method_name}(*obj) end")
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module JavaClass
|
4
|
+
module Dsl
|
5
|
+
|
6
|
+
# A delegator classpath that caches loaded class files in a map by full qualified class names.
|
7
|
+
# Author:: Peter Kofler
|
8
|
+
class CachingClasspath < SimpleDelegator
|
9
|
+
|
10
|
+
# Create a cached instance of the _classpath_ (a LoadingClasspath).
|
11
|
+
def initialize(classpath)
|
12
|
+
unless classpath.respond_to? :load
|
13
|
+
raise ArgumentError, "wrong type of delegatee #{classpath.class}"
|
14
|
+
end
|
15
|
+
@classpath = classpath
|
16
|
+
@cache = Hash.new # full_name (String) => ClassEntryHeader
|
17
|
+
super(classpath)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Ask the cache for the _classname_ and return it. Else delegate loading.
|
21
|
+
def load(classname)
|
22
|
+
key = classname.to_javaname.full_name
|
23
|
+
if !@cache.include?(key)
|
24
|
+
@cache[key] = @classpath.load(classname)
|
25
|
+
end
|
26
|
+
@cache[key]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Load _listed_ or all classes. Duplicate method to use the cache of decorator.
|
30
|
+
def values(listed=nil, &filter)
|
31
|
+
listed ||= names(&filter)
|
32
|
+
listed.collect { |name| load(name) }
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|