javaclass 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. data/Rakefile +208 -23
  2. data/Readme.txt +56 -19
  3. data/examples/generate_class_lists.rb +61 -0
  4. data/history.txt +13 -19
  5. data/lib/javaclass/classfile/access_flags.rb +52 -0
  6. data/lib/javaclass/classfile/class_magic.rb +29 -0
  7. data/lib/javaclass/classfile/class_version.rb +67 -0
  8. data/lib/javaclass/classfile/constant_pool.rb +91 -0
  9. data/lib/javaclass/classfile/constants/base.rb +33 -0
  10. data/lib/javaclass/classfile/constants/double_reference.rb +99 -0
  11. data/lib/javaclass/classfile/constants/single_reference.rb +65 -0
  12. data/lib/javaclass/classfile/constants/value.rb +116 -0
  13. data/lib/javaclass/classfile/java_class_header.rb +102 -0
  14. data/lib/javaclass/classfile/references.rb +43 -0
  15. data/lib/javaclass/classlist/class_entry.rb +89 -0
  16. data/lib/javaclass/classlist/jar_searcher.rb +69 -0
  17. data/lib/javaclass/classlist/list.rb +126 -0
  18. data/lib/javaclass/classlist/package_entry.rb +87 -0
  19. data/lib/javaclass/classpath/classpaths.txt +2 -0
  20. data/lib/javaclass/classpath/composite_classpath.rb +99 -0
  21. data/lib/javaclass/classpath/folder_classpath.rb +83 -0
  22. data/lib/javaclass/classpath/jar_classpath.rb +100 -0
  23. data/lib/javaclass/classpath/java_home_classpath.rb +40 -0
  24. data/lib/javaclass/classpath/port_ClassPathEntry.java +202 -0
  25. data/lib/javaclass/classpath/port_ClassPathEntryFactory.java +311 -0
  26. data/lib/javaclass/classpath/port_DirectoryRepository.java +24 -0
  27. data/lib/javaclass/java_name.rb +90 -0
  28. data/lib/javaclass/metric/ccd.rb +68 -0
  29. data/lib/javaclass/metric/class_usage.rb +41 -0
  30. data/lib/javaclass/metric/metrics.txt +15 -0
  31. data/lib/javaclass.rb +44 -11
  32. data/test/data/ShortClientData$1-4000Wert.class +0 -0
  33. data/test/data/{AccessFlagsTestAbstract.class → access_flags/AccessFlagsTestAbstract.class} +0 -0
  34. data/test/data/{AccessFlagsTestAbstract.java → access_flags/AccessFlagsTestAbstract.java} +0 -0
  35. data/test/data/{AccessFlagsTestFinal.class → access_flags/AccessFlagsTestFinal.class} +0 -0
  36. data/test/data/{AccessFlagsTestFinal.java → access_flags/AccessFlagsTestFinal.java} +0 -0
  37. data/test/data/{AccessFlagsTestInterface.class → access_flags/AccessFlagsTestInterface.class} +0 -0
  38. data/test/data/{AccessFlagsTestInterface.java → access_flags/AccessFlagsTestInterface.java} +0 -0
  39. data/test/data/{AccessFlagsTestPackage.class → access_flags/AccessFlagsTestPackage.class} +0 -0
  40. data/test/data/{AccessFlagsTestPackage.java → access_flags/AccessFlagsTestPackage.java} +0 -0
  41. data/test/data/access_flags/AccessFlagsTestPublic$Inner.class +0 -0
  42. data/test/data/access_flags/AccessFlagsTestPublic$InnerInterface.class +0 -0
  43. data/test/data/access_flags/AccessFlagsTestPublic$StaticInner.class +0 -0
  44. data/test/data/access_flags/AccessFlagsTestPublic.class +0 -0
  45. data/test/data/access_flags/AccessFlagsTestPublic.java +11 -0
  46. data/test/data/access_flags/AccessFlagsTestPublic_javap.txt +29 -0
  47. data/test/data/{makeAccessFlagsTest.bat → access_flags/make.bat} +0 -0
  48. data/test/data/{ClassVersionTest.java → class_version/ClassVersionTest.java} +0 -0
  49. data/test/data/{ClassVersionTest10.class → class_version/ClassVersionTest10.class} +0 -0
  50. data/test/data/{ClassVersionTest11.class → class_version/ClassVersionTest11.class} +0 -0
  51. data/test/data/{ClassVersionTest12.class → class_version/ClassVersionTest12.class} +0 -0
  52. data/test/data/{ClassVersionTest13.class → class_version/ClassVersionTest13.class} +0 -0
  53. data/test/data/{ClassVersionTest14.class → class_version/ClassVersionTest14.class} +0 -0
  54. data/test/data/{ClassVersionTest15.class → class_version/ClassVersionTest15.class} +0 -0
  55. data/test/data/{ClassVersionTest16.class → class_version/ClassVersionTest16.class} +0 -0
  56. data/test/data/{makeClassVersionTest.bat → class_version/make.bat} +0 -0
  57. data/test/data/{ConstantPoolTest.class → constant_pool/ConstantPoolTest.class} +0 -0
  58. data/test/data/{ConstantPoolTest.java → constant_pool/ConstantPoolTest.java} +0 -0
  59. data/test/data/{makeConstantPoolTest.bat → constant_pool/make.bat} +0 -0
  60. data/test/data/folder_classpath/JarClasspathTestFolder/ClassVersionTest10.class +0 -0
  61. data/test/data/folder_classpath/JarClasspathTestFolder/package/ClassVersionTest11.class +0 -0
  62. data/test/data/jar_classpath/JarClasspathTest.jar +0 -0
  63. data/test/data/jar_classpath/JarClasspathTest.zip +0 -0
  64. data/test/data/jar_classpath/JarClasspathTestManifest.jar +0 -0
  65. data/test/data/jar_classpath/JarClasspathTestMultiManifest.jar +0 -0
  66. data/test/data/jar_classpath/make.bat +24 -0
  67. data/test/data/jar_searcher/JarClassListTest.jar +0 -0
  68. data/test/data/jar_searcher/PublicClass.java +12 -0
  69. data/test/data/jar_searcher/PublicInterface.java +5 -0
  70. data/test/data/jar_searcher/make.bat +13 -0
  71. data/test/data/java_home_classpath/jdk/jre/lib/rt.jar +0 -0
  72. data/test/data/java_home_classpath/jre/lib/rt.jar +0 -0
  73. data/test/data/java_home_classpath/jre-ext/lib/ext/ext.jar +0 -0
  74. data/test/data/java_home_classpath/jre-ext/lib/rt.jar +0 -0
  75. data/test/data/java_home_classpath/make.bat +13 -0
  76. data/test/data/references/ReferencesTest.class +0 -0
  77. data/test/data/references/ReferencesTest.java +12 -0
  78. data/test/data/references/make.bat +5 -0
  79. data/test/setup.rb +6 -5
  80. data/test/test_access_flags.rb +59 -44
  81. data/test/test_base.rb +20 -15
  82. data/test/test_class_entry.rb +198 -0
  83. data/test/test_class_version.rb +48 -46
  84. data/test/test_composite_classpath.rb +68 -0
  85. data/test/test_constant_pool.rb +49 -41
  86. data/test/test_folder_classpath.rb +45 -0
  87. data/test/test_jar_classpath.rb +57 -0
  88. data/test/test_jar_searcher.rb +93 -0
  89. data/test/test_java_class_header.rb +34 -22
  90. data/test/test_java_home_classpath.rb +50 -0
  91. data/test/test_java_name.rb +110 -0
  92. data/test/test_javaclass.rb +22 -0
  93. data/test/test_list.rb +123 -0
  94. data/test/test_package_entry.rb +91 -0
  95. data/test/test_references.rb +38 -20
  96. data/test/test_string_ux.rb +99 -2
  97. data/test/ts_all_tests.rb +42 -21
  98. metadata +171 -50
  99. data/lib/javaclass/access_flags.rb +0 -44
  100. data/lib/javaclass/class_magic.rb +0 -27
  101. data/lib/javaclass/class_version.rb +0 -65
  102. data/lib/javaclass/constant_pool.rb +0 -85
  103. data/lib/javaclass/constants/base.rb +0 -31
  104. data/lib/javaclass/constants/double_reference.rb +0 -81
  105. data/lib/javaclass/constants/single_reference.rb +0 -58
  106. data/lib/javaclass/constants/value.rb +0 -114
  107. data/lib/javaclass/java_class_header.rb +0 -88
  108. data/lib/javaclass/references.rb +0 -31
  109. data/test/data/AccessFlagsTestPublic$InnerClass.class +0 -0
  110. data/test/data/AccessFlagsTestPublic.class +0 -0
  111. data/test/data/AccessFlagsTestPublic.java +0 -7
@@ -0,0 +1,69 @@
1
+ require 'javaclass/classpath/composite_classpath'
2
+ require 'javaclass/classfile/java_class_header'
3
+
4
+ module JavaClass
5
+ module ClassList # :nodoc:
6
+
7
+ # Search in zip or jar files for Java class files, check for package access or inner classes and
8
+ # call back a list of all these.
9
+ # Author:: Peter Kofler
10
+ class JarSearcher
11
+
12
+ attr_accessor :skip_inner_classes
13
+ attr_accessor :skip_package_classes
14
+
15
+ # Create a new searcher.
16
+ def initialize
17
+ @skip_inner_classes = true
18
+ @skip_package_classes = false
19
+ @package_filters = []
20
+ end
21
+
22
+ # The given _filters_ will be dropped during searching.
23
+ # _filters_ contain the beginning of package paths, i.e. <code>com/sun/</code>.
24
+ def filters=(filters)
25
+ @package_filters = filters.collect{ |f| /^#{f}/ }
26
+ end
27
+
28
+ # Return the list of classnames of this list of _classes_ .
29
+ # Skips inner classes if +skip_inner_classes+ is true.
30
+ # Skips classes that are in the filtered packages.
31
+ def filter_classes(classes)
32
+ classes.find_all do |name|
33
+ !(@skip_inner_classes && name =~ /\$/) && (@package_filters.find { |filter| name =~ filter } == nil)
34
+ end
35
+ end
36
+
37
+ # Return true if the _classfile_ in the given _classpath_ is public. This is expensive because
38
+ # the jarfile is opened and the _classfile_ is extracted and read.
39
+ def public?(classpath, classfile)
40
+ begin
41
+ header = ClassFile::JavaClassHeader.new(classpath.load_binary(classfile))
42
+ rescue
43
+ puts "error #{$1} for class #{classfile} on #{classpath.to_s}"
44
+ raise
45
+ end
46
+ raise "invalid java class #{classfile}" unless header.magic.valid?
47
+ header.access_flags.accessible?
48
+ end
49
+
50
+ # Compile the class list for the given _version_ of Java. This searches the _path_ for zips and jars
51
+ # and adds them to the given _list_ of found classes. _version_ is a number >= 0, e.g. 2 for JDK 1.2.
52
+ # _list_ must provide a <code>add_class(entry, is_public, version)</code> method.
53
+ def compile_list(version, path, list)
54
+ cpe = JavaClass::Classpath::CompositeClasspath.new
55
+ cpe.find_jars(path)
56
+ filter_classes(cpe.names).each do |entry|
57
+ is_public = public?(cpe, entry)
58
+ next if @skip_package_classes && !is_public
59
+ list.add_class(entry, is_public, version) if list
60
+ yield(entry, is_public, version) if block_given?
61
+ end
62
+
63
+ list
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,126 @@
1
+ require 'javaclass/java_name'
2
+ require 'javaclass/classlist/package_entry'
3
+
4
+ module JavaClass
5
+ module ClassList # :nodoc:
6
+
7
+ # Classes to form a list of JDK classes to find classes which have been added in new releases.
8
+ # The list of classes. It list contains packages.
9
+ # Author:: Peter Kofler
10
+ class List
11
+
12
+ def initialize
13
+ @packages = {}
14
+ end
15
+
16
+ # Add a _fileentry_ to the list. The _fileentry_ is the file name of the class in the jar file. _version_
17
+ # is the version of the JDK scanned and is used if the class is new.
18
+ def add_class(fileentry, is_public, version)
19
+ class_name = fileentry.to_javaname.to_classname
20
+ package_name = class_name.package
21
+ @packages[package_name] = PackageEntry.new(package_name, version) unless @packages.has_key?(package_name)
22
+ @packages[package_name].add_class(class_name, is_public, version)
23
+ end
24
+
25
+ # Parse a _line_ from a <code>fullClassList</code> and fill the list again. _maxversion_ is the maximum
26
+ # max version from the list, i.e. the highest possible value, so we can continue to use it.
27
+ def parse_line(line, maxversion)
28
+ class_name, versions = line.scan(/^([^\s]+)\s(?:\[(.*)\]\s)?-\s*$/)[0]
29
+
30
+ # no [], so we have it from always and it is public
31
+ first_vers = 0
32
+ last_vers = maxversion
33
+ is_public = true
34
+ if versions
35
+ # extract package access and drop it
36
+ is_public = (versions !~ /p/)
37
+ versions = versions.gsub(/p/, '')
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
53
+ end
54
+
55
+ first_vers.upto(last_vers) do |v|
56
+ add_class(class_name, is_public, v)
57
+ end
58
+ rescue
59
+ raise "#{$!} in line #{line.chomp}: class_name=#{class_name}, versions=#{versions}, first_vers=#{first_vers}, last_vers=#{last_vers}, is_public=#{is_public}"
60
+ end
61
+
62
+ def packages
63
+ @packages.values.sort
64
+ end
65
+
66
+ # Return the version list of all packages.
67
+ def version
68
+ packages.collect { |p| p.version }.flatten.uniq.sort
69
+ end
70
+
71
+ # Return the first and last version of this list.
72
+ def first_last_versions
73
+ v = version
74
+ [v.first, v.last]
75
+ end
76
+
77
+ def to_s
78
+ packages.collect { |p| p.to_s }.join("\n")
79
+ end
80
+
81
+ # Return the number of classes in this list.
82
+ def size
83
+ @packages.values.inject(0) {|sum, p| sum + p.size }
84
+ end
85
+
86
+ # The access list is the raw list of all package access classes for one version. It was used to differ
87
+ # normal classes from hidden classes and was saved in <code>doc/AccessLists/*_p.txt</code>.
88
+ # This works only if JarSearcher was used with +skip_package_classes+ set to false (default).
89
+ # If there are more versions loaded, then only the last version is printed. So we get consecutive lists
90
+ # of new package access classes with every JDK version.
91
+ def old_access_list
92
+ v = first_last_versions
93
+ packages.collect { |pkg|
94
+ pkg.classes.find_all { |c| !c.public? && c.version == [v[1]]}.collect { |c| c.to_full_qualified_s(v[0], v[1]) }
95
+ }.flatten.sort{|a,b| a.casecmp b }
96
+ end
97
+
98
+ # The class list is the raw list of all classes for one version without version or package access
99
+ # descriptors. It was used to find differences and was saved in <code>doc/ClassLists/*_classes.txt</code>.
100
+ # This usually was done with JarSearcher set +skip_package_classes+ to false.
101
+ # If a block is given it is invoked with ClassEntry and should return if to add the class or not.
102
+ def plain_class_list
103
+ packages.collect { |pkg|
104
+ cls = pkg.classes
105
+ if block_given?
106
+ cls = cls.find_all { |c| yield(c) }
107
+ end
108
+ cls.collect { |c| c.full_name + "\n" }
109
+ }.flatten.sort{|a,b| a.casecmp b }
110
+ end
111
+
112
+ # Create a full class list with version numbers and different versions to compare.
113
+ # This was the base for classlists and was saved in <code>doc/fullClassList1x.txt</code>.
114
+ # This usually was done with JarSearcher set +skip_package_classes+ to false and
115
+ # contained different classlists merged together.
116
+ def full_class_list
117
+ v = first_last_versions
118
+ packages.collect { |pkg|
119
+ pkg.classes.collect { |c| c.to_full_qualified_s(v[0], v[1]) }
120
+ }.flatten.sort{|a,b| a.casecmp b }
121
+ end
122
+
123
+ end
124
+
125
+ end
126
+ end
@@ -0,0 +1,87 @@
1
+ require 'javaclass/classlist/class_entry'
2
+
3
+ module JavaClass
4
+ module ClassList # :nodoc:
5
+
6
+ # A package in the List.
7
+ # Author:: Peter Kofler
8
+ class PackageEntry # ZenTest FULL to find method <=>
9
+
10
+ attr_reader :name
11
+ # Return the list of versions this package exists. This is the sum of all versions of all classes in the package.
12
+ attr_reader :version
13
+
14
+ # Create a new package with name _name_ and first version _vers_ which is the version of the first class in the package.
15
+ def initialize(name, vers=0)
16
+ @name = name
17
+ @version = [vers]
18
+ @classes = {}
19
+ end
20
+
21
+ def add_class(class_name, is_public, version)
22
+ @version << version unless @version.include? version
23
+ @version = @version.sort
24
+
25
+ unless @classes.has_key?(class_name)
26
+ @classes[class_name] = ClassEntry.new(self, class_name, is_public, version)
27
+ else
28
+ @classes[class_name].update(version, is_public)
29
+ end
30
+ end
31
+
32
+ # Sorting in packages is: <code>java.lang</code>, other <code>java.*</code>, <code>javax.*</code> and then others.
33
+ def <=>(other)
34
+ if @name =~ /^java\.lang$/ and other.name !~ /^java\.lang$/
35
+ -1
36
+ elsif @name !~ /^java\.lang$/ and other.name =~ /^java\.lang$/
37
+ 1
38
+ elsif @name =~ /^java\./ and other.name !~ /^java\./
39
+ -1
40
+ elsif @name !~ /^java\./ and other.name =~ /^java\./
41
+ 1
42
+ elsif @name =~ /^javax\./ and other.name !~ /^javax\./
43
+ -1
44
+ elsif @name !~ /^javax\./ and other.name =~ /^javax\./
45
+ 1
46
+ else
47
+ @name <=> other.name
48
+ end
49
+ end
50
+
51
+ # Return the classes in this package.
52
+ def classes
53
+ @classes.values.sort
54
+ end
55
+
56
+ # Return the number of classes in this package.
57
+ def size
58
+ @classes.size
59
+ end
60
+
61
+ def to_s
62
+ @name
63
+ end
64
+
65
+ # Special version of +to_s+ for package shortcut. A package needs _minversion_ and _maxversion_ to
66
+ # determine if the whole package was dropped.
67
+ def to_package_shortcut_s(minversion, maxversion)
68
+ "#{@name}#{format_version(minversion, maxversion)} - \n" +
69
+ classes.collect { |c| c.to_package_shortcut_s }.join
70
+ end
71
+
72
+ private
73
+
74
+ def format_version(minversion, maxversion)
75
+ is_newer = @version.first > minversion
76
+ is_outdated = @version.last < maxversion
77
+ line =
78
+ " [#{ is_newer ? @version.first.to_s : ''}" +
79
+ "#{is_outdated ? '-' + @version.last.to_s : ''}" +
80
+ "]"
81
+ line.sub(/(\d)-\1/, "only \\1").sub(/ \[\]/, '')
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,2 @@
1
+ * Java 1.1 Home classpath with lib/classes.zip
2
+ * other classpath abstractions: - WARs and EARs contain JARs and a folder
@@ -0,0 +1,99 @@
1
+ require 'javaclass/classpath/jar_classpath'
2
+ require 'javaclass/classpath/folder_classpath'
3
+
4
+ module JavaClass
5
+ module Classpath
6
+ # List of class path elements.
7
+ # Author:: Peter Kofler
8
+ class CompositeClasspath
9
+ # Return the list of classnames found in this path.
10
+ def initialize
11
+ @elements = []
12
+ end
13
+
14
+ # Return the classpath elements of this path.
15
+ def elements
16
+ @elements.dup
17
+ end
18
+
19
+ # Search the given _path_ recursievely for zips or jars. Add all found jars to this classpath.
20
+ def find_jars(path)
21
+ current = Dir.getwd
22
+ begin
23
+ Dir.chdir File.expand_path(path)
24
+
25
+ Dir['*'].collect do |name|
26
+ if FileTest.directory?(name)
27
+ find_jars(name)
28
+ elsif name =~ /\.jar$|\.zip$/
29
+ add_file_name File.expand_path(name)
30
+ end
31
+ end
32
+
33
+ ensure
34
+ Dir.chdir current
35
+ end
36
+ end
37
+
38
+ # Add the _name_ class path file/folder name to the list.
39
+ def add_file_name(name)
40
+ if FileTest.directory? name
41
+ add_element(FolderClasspath.new(name))
42
+ else
43
+ add_element(JarClasspath.new(name))
44
+ end
45
+ end
46
+
47
+ # Add the _elem_ classpath element to the list.
48
+ def add_element(elem)
49
+ @elements << elem unless @elements.find { |cpe| cpe == elem }
50
+ elem.additional_classpath.each do |acpe|
51
+ # referred classpath elements may be missing
52
+ if FileTest.exist? acpe
53
+ add_element(JarClasspath.new(acpe))
54
+ end
55
+ end
56
+ end
57
+
58
+ # Return false.
59
+ def jar?
60
+ false
61
+ end
62
+
63
+ # Return an empty array.
64
+ def additional_classpath
65
+ []
66
+ end
67
+
68
+ # Return the list of class names found in this classpath.
69
+ def names
70
+ @elements.collect { |e| e.names }.flatten.uniq
71
+ end
72
+
73
+ # Return if _classname_ is included in this classpath.
74
+ def includes?(classname)
75
+ @elements.find { |e| e.includes?(classname) } != nil
76
+ end
77
+
78
+ # Load the binary data of the file name or class name _classname_ from this classpath.
79
+ def load_binary(classname)
80
+ found = @elements.find { |e| e.includes?(classname) }
81
+ if (found)
82
+ return found.load_binary(classname)
83
+ end
84
+ raise "class #{classname} not found in classpath #{to_s}"
85
+ end
86
+
87
+ # Return the number of classes in this jar.
88
+ def count
89
+ @elements.inject(0) { |s,e| s + e.count }
90
+ end
91
+
92
+ def to_s
93
+ @elements.collect { |e| e.to_s }.join(',')
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,83 @@
1
+ require 'javaclass/java_name'
2
+
3
+ module JavaClass
4
+ module Classpath # :nodoc:
5
+ # Abstraction of a folder on the CLASSPATH.
6
+ # Author:: Peter Kofler
7
+ class FolderClasspath
8
+ # Return the list of classnames found in this _folder_ .
9
+ def initialize(folder)
10
+ @folder = folder
11
+ raise IOError, "folder #{@folder} not found" if !FileTest.exist? @folder
12
+ raise "#{@folder} is no folder" if !FileTest.directory? @folder
13
+ @classes = list_classes.collect { |cl| cl.to_javaname }
14
+ end
15
+
16
+ # Return false.
17
+ def jar?
18
+ false
19
+ end
20
+
21
+ # Return an empty array.
22
+ def additional_classpath
23
+ []
24
+ end
25
+
26
+ # Return the list of class names found in this folder.
27
+ def names
28
+ @classes.dup
29
+ end
30
+
31
+ # Return if _classname_ is included in this folder.
32
+ def includes?(classname)
33
+ @classes.include?(normalize(classname))
34
+ end
35
+
36
+ # Load the binary data of the file name or class name _classname_ from this jar.
37
+ def load_binary(classname)
38
+ raise "class #{classname} not found in #{@folder}" unless includes?(classname)
39
+ File.open(File.join(@folder, normalize(classname)), 'rb') {|io| io.read }
40
+ end
41
+
42
+ # Return the number of classes in this folder.
43
+ def count
44
+ @classes.size
45
+ end
46
+
47
+ def to_s
48
+ @folder
49
+ end
50
+
51
+ def ==(other)
52
+ other.class == FolderClasspath && other.to_s == self.to_s
53
+ end
54
+
55
+ private
56
+
57
+ # Return the list of classnames (in fact file names) found in this folder.
58
+ def list_classes
59
+ current = Dir.getwd
60
+ begin
61
+ Dir.chdir @folder
62
+
63
+ list = Dir['**/*.class']
64
+
65
+ ensure
66
+ Dir.chdir current
67
+ end
68
+ list.sort
69
+ end
70
+
71
+ # Normalize the file name or class name _classname_ to be a file name in the jar.
72
+ def normalize(classname)
73
+ if classname !~ /\.class$/
74
+ classname.gsub(/\./,'/') + '.class'
75
+ else
76
+ classname
77
+ end
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,100 @@
1
+ require 'zip/zipfilesystem'
2
+ require 'javaclass/java_name'
3
+
4
+ module JavaClass
5
+ module Classpath
6
+
7
+ # Abstraction of a ZIP or JAR on the CLASSPATH.
8
+ # Author:: Peter Kofler
9
+ class JarClasspath
10
+
11
+ # Return the list of classnames found in this _jarfile_ .
12
+ def initialize(jarfile)
13
+ @jarfile = jarfile
14
+ raise IOError, "jarfile #{@jarfile} not found" if !FileTest.exist? @jarfile
15
+ raise "#{@jarfile} is no file" if !FileTest.file? @jarfile
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
22
+ end
23
+ end
24
+
25
+ # Return if the given classpath element is a jar.
26
+ def jar?
27
+ @manifest != nil
28
+ end
29
+
30
+ # Return list of additional classpath elements defined in the manifest of this jarfile.
31
+ def additional_classpath
32
+ if @manifest
33
+ cp = @manifest.gsub(/\s{4,}/, ' ').scan(/^(.*): (.*)\s*$/).find { |p| p[0] == 'Class-Path' }
34
+ if cp
35
+ cp[1].strip.split.collect { |jar| File.join(File.dirname(@jarfile), jar) }
36
+ else
37
+ []
38
+ end
39
+ else
40
+ []
41
+ end
42
+ end
43
+
44
+ # Return the list of class names found in this jar.
45
+ def names
46
+ @classes.dup
47
+ end
48
+
49
+ # Return if _classname_ is included in this jar.
50
+ def includes?(classname)
51
+ @classes.include?(normalize(classname))
52
+ end
53
+
54
+ # Load the binary data of the file name or class name _classname_ from this jar.
55
+ def load_binary(classname)
56
+ raise "class #{classname} not found in #{@jarfile}" unless includes?(classname)
57
+ Zip::ZipFile.open(@jarfile) do |zipfile|
58
+ zipfile.file.read(normalize(classname))
59
+ end
60
+ end
61
+
62
+ # Return the number of classes in this jar.
63
+ def count
64
+ @classes.size
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
73
+ end
74
+
75
+ private
76
+
77
+ # Return the list of classnames (in fact file names) found in this jarfile.
78
+ def list_classes
79
+ list = []
80
+ Zip::ZipFile.foreach(@jarfile) do |entry|
81
+ name = entry.name
82
+ next unless entry.file? and name =~ /\.class$/ # class file
83
+ list << name
84
+ end
85
+ list.sort
86
+ end
87
+
88
+ # Normalize the file name or class name _classname_ to be a file name in the jar.
89
+ def normalize(classname)
90
+ if classname !~ /\.class$/
91
+ classname.gsub(/\./,'/') + '.class'
92
+ else
93
+ classname
94
+ end
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,40 @@
1
+ require 'javaclass/classpath/jar_classpath'
2
+
3
+ module JavaClass
4
+ module Classpath
5
+
6
+ # Abstraction of the Java boot CLASSPATH. May return additional classpath elements for endorsed libs.
7
+ # Author:: Peter Kofler
8
+ class JavaHomeClasspath < JarClasspath
9
+
10
+ # Return the list of classnames found in this _javahome_ .
11
+ def initialize(javahome)
12
+ if FileTest.exist? rtjar=File.join(javahome, 'lib', 'rt.jar')
13
+ super(rtjar)
14
+ elsif FileTest.exist? rtjar=File.join(javahome, 'jre', 'lib', 'rt.jar')
15
+ super(rtjar)
16
+ else
17
+ raise IOError, "rt.jar not found in java home #{javahome}"
18
+ end
19
+ @lib = File.dirname(rtjar)
20
+ end
21
+
22
+ # Return list of additional classpath elements, e.g. endorsed libs found in this Java Home.
23
+ def additional_classpath
24
+ list = super
25
+
26
+ if FileTest.exist? ext=File.join(@lib, 'ext')
27
+ current = Dir.getwd
28
+ Dir.chdir ext
29
+
30
+ list += Dir['*.jar'].collect { |jar| File.join(ext, jar) }
31
+
32
+ Dir.chdir current
33
+ end
34
+ list
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+ end