javaclass

classhook.py

88:6837caf38a99
2004-12-14 Paul Boddie Added some real behaviour to InputStream and added stream classes of use for java.lang.System.
     1 #!/usr/bin/env python     2      3 import ihooks # for the import machinery     4 import os, glob # for getting suitably-named files     5 from imp import PY_SOURCE, PKG_DIRECTORY, C_BUILTIN # import machinery magic     6 import classfile, bytecode # Java class support     7 import zipfile # for Java archive inspection     8      9 # NOTE: Arbitrary constants pulled from thin air.    10     11 JAVA_PACKAGE = 20041113    12 JAVA_CLASS = 20041114    13 JAVA_ARCHIVE = 20041115    14     15 class ClassHooks(ihooks.Hooks):    16     17     "A filesystem hooks class providing information about supported files."    18     19     def get_suffixes(self):    20     21         "Return the recognised suffixes."    22     23         return [("", "", JAVA_PACKAGE), (os.extsep + "jar", "r", JAVA_ARCHIVE)] + ihooks.Hooks.get_suffixes(self)    24     25     def path_isdir(self, x, archive=None):    26     27         "Return whether 'x' is a directory in the given 'archive'."    28     29         if archive is None:    30             return ihooks.Hooks.path_isdir(self, x)    31     32         return self._get_dirname(x) in archive.namelist()    33     34     def _get_dirname(self, x):    35     36         """    37         Return the directory name for 'x'.    38         In zip files, the presence of "/" seems to indicate a directory.    39         """    40     41         if x.endswith("/"):    42             return x    43         else:    44             return x + "/"    45     46     def listdir(self, x, archive=None):    47     48         "Return the contents of the directory 'x' in the given 'archive'."    49     50         if archive is None:    51             return ihooks.Hooks.listdir(self, x)    52     53         x = self._get_dirname(x)    54         l = []    55         for path in archive.namelist():    56     57             # Find out if the path is within the given directory.    58     59             if path != x and path.startswith(x):    60     61                 # Get the path below the given directory.    62     63                 subpath = path[len(x):]    64     65                 # Find out whether the path is an object in the current directory.    66     67                 if subpath.count("/") == 0 or subpath.count("/") == 1 and subpath.endswith("/"):    68                     l.append(subpath)    69     70         return l    71     72     def matching(self, dir, extension, archive=None):    73     74         """    75         Return the matching files in the given directory 'dir' having the given    76         'extension' within the given 'archive'. Produce a list containing full    77         paths as opposed to simple filenames.    78         """    79     80         if archive is None:    81             return glob.glob(self.path_join(dir, "*" + extension))    82     83         dir = self._get_dirname(dir)    84         l = []    85         for path in self.listdir(dir, archive):    86             if path.endswith(extension):    87                 l.append(self.path_join(dir, path))    88         return l    89     90     def read(self, filename, archive=None):    91     92         """    93         Return the contents of the file with the given 'filename' in the given    94         'archive'.    95         """    96     97         if archive is None:    98             f = open(filename, "rb")    99             s = f.read()   100             f.close()   101             return s   102         return archive.read(filename)   103    104 class ClassLoader(ihooks.ModuleLoader):   105    106     "A class providing support for searching directories for supported files."   107    108     def find_module(self, name, path=None):   109    110         """   111         Find the module with the given 'name', using the given 'path' to locate   112         it. Note that ModuleLoader.find_module is almost sufficient, but does   113         not provide enough support for "package unions" where the root of a   114         package hierarchy may appear in several places.   115    116         Return a list of locations (each being the "stuff" data structure used   117         by load_module); this replaces the single "stuff" value or None returned   118         by ModuleLoader.find_module.   119         """   120    121         if path is None:   122             path = [None] + self.default_path()   123    124         found_locations = []   125    126         for dir in path:   127             stuff = self.find_module_in_dir(name, dir)   128             if stuff:   129                 found_locations.append(stuff)   130    131         return found_locations   132    133     def find_module_in_dir(self, name, dir, allow_packages=1):   134    135         """   136         Find the module with the given 'name' in the given directory 'dir'.   137         Since Java packages/modules are directories containing class files,   138         return the required information tuple only when the path constructed   139         from 'dir' and 'name' refers to a directory containing class files.   140         """   141    142         result = ihooks.ModuleLoader.find_module_in_dir(self, name, dir, allow_packages)   143         if result is not None:   144             return result   145    146         # An archive may be opened.   147    148         archive = None   149    150         # Provide a special name for the current directory.   151    152         if name == "__this__":   153             path = "."   154    155         # Where no directory is given, return failure immediately.   156    157         elif dir is None:   158             return None   159    160         # Detect archives.   161    162         else:   163             archive, archive_path, path = self._get_archive_and_path(dir, name)   164    165         #print "Processing name", name, "in", dir, "producing", path, "within archive", archive   166    167         if self._find_module_at_path(path, archive):   168             if archive is not None:   169                 return (archive, archive_path + ":" + path, (os.extsep + "jar", "r", JAVA_ARCHIVE))   170             else:   171                 return (None, path, ("", "", JAVA_PACKAGE))   172         else:   173             return None   174    175     def _get_archive_and_path(self, dir, name):   176         parts = dir.split(":")   177         archive_path = parts[0]   178    179         # Archives may include an internal path, but will in any case have   180         # a primary part ending in .jar.   181    182         if archive_path.endswith(os.extsep + "jar"):   183             archive = zipfile.ZipFile(archive_path, "r")   184             path = self.hooks.path_join(":".join(parts[1:]), name)   185    186         # Otherwise, produce a filesystem-based path.   187    188         else:   189             archive = None   190             path = self.hooks.path_join(dir, name)   191    192         return archive, archive_path, path   193    194     def _get_path_in_archive(self, path):   195         parts = path.split(":")   196         if len(parts) == 1:   197             return parts[0]   198         else:   199             return ":".join(parts[1:])   200    201     def _find_module_at_path(self, path, archive):   202         if self.hooks.path_isdir(path, archive):   203             #print "Looking in", path, "using archive", archive   204    205             # Look for classes in the directory.   206    207             if len(self.hooks.matching(path, os.extsep + "class", archive)) != 0:   208                 return 1   209    210             # Otherwise permit importing where directories containing classes exist.   211    212             #print "Filenames are", self.hooks.listdir(path, archive)   213             for filename in self.hooks.listdir(path, archive):   214                 pathname = self.hooks.path_join(path, filename)   215                 result = self._find_module_at_path(pathname, archive)   216                 if result is not None:   217                     return result   218    219         return 0   220    221     def load_module(self, name, stuff):   222    223         """   224         Load the module with the given 'name', with a list of 'stuff' items,   225         each of which describes the location of the module and is a tuple of the   226         form (file, filename, (suffix, mode, data type)).   227    228         Return a module object or raise an ImportError if a problem occurred in   229         the import operation.   230    231         Note that the 'stuff' parameter is a list and not a single item as in   232         ModuleLoader.load_module. This should still work, however, since the   233         find_module method produces such a list.   234         """   235    236         # Set up the module.   237         # A union of all locations is placed in the module's path.   238    239         module = self.hooks.add_module(name)   240         module.__path__ = [item_filename for (item_archive, item_filename, item_info) in stuff]   241    242         # Just go into each package and find the class files.   243    244         for stuff_item in stuff:   245    246             # Extract the details, delegating loading responsibility to the   247             # default loader where appropriate.   248             # NOTE: Should we not be using some saved loader remembered upon   249             # NOTE: installation?   250    251             archive, filename, info = stuff_item   252             suffix, mode, datatype = info   253             if datatype not in (JAVA_PACKAGE, JAVA_ARCHIVE):   254                 return ihooks.ModuleLoader.load_module(self, name, stuff_item)   255    256             #print "Loading", archive, filename, info   257    258             # Prepare a dictionary of globals.   259    260             global_names = module.__dict__   261             global_names["__builtins__"] = __builtins__   262    263             # Get the real filename.   264    265             filename = self._get_path_in_archive(filename)   266             #print "Real filename", filename   267    268             # Load the class files.   269    270             class_files = {}   271             for class_filename in self.hooks.matching(filename, os.extsep + "class", archive):   272                 #print "Loading class", class_filename   273                 s = self.hooks.read(class_filename, archive)   274                 class_file = classfile.ClassFile(s)   275                 class_files[str(class_file.this_class.get_name())] = class_file   276    277             # Get an index of the class files.   278    279             class_file_index = class_files.keys()   280    281             # NOTE: Unnecessary sorting for test purposes.   282    283             class_file_index.sort()   284    285             # Now go through the classes arranging them in a safe loading order.   286    287             position = 0   288             while position < len(class_file_index):   289                 class_name = class_file_index[position]   290                 super_class_name = str(class_files[class_name].super_class.get_name())   291    292                 # Discover whether the superclass appears later.   293    294                 try:   295                     super_class_position = class_file_index.index(super_class_name)   296                     if super_class_position > position:   297    298                         # If the superclass appears later, swap this class and the   299                         # superclass, then process the superclass.   300    301                         class_file_index[position] = super_class_name   302                         class_file_index[super_class_position] = class_name   303                         continue   304    305                 except ValueError:   306                     pass   307    308                 position += 1   309    310             # Process each class file, producing a genuine Python class.   311             # Create the classes, but establish a proper initialisation order.   312    313             class_file_init_index = []   314             class_file_init = {}   315    316             for class_name in class_file_index:   317                 print "* Class", class_name   318                 class_file = class_files[class_name]   319                 translator = bytecode.ClassTranslator(class_file)   320                 cls, external_names = translator.process(global_names)   321                 module.__dict__[cls.__name__] = cls   322    323                 # Process external names.   324    325                 this_class_name_parts = class_file.this_class.get_python_name().split(".")   326                 this_class_module, this_class_name = this_class_name_parts[:-1], this_class_name_parts[-1]   327    328                 for external_name in external_names:   329                     print "* Name", external_name   330                     external_name_parts = external_name.split(".")   331                     external_class_module, external_class_name = external_name_parts[:-1], external_name_parts[-1]   332    333                     # Names not local to this package need importing.   334    335                     if len(external_name_parts) > 1 and this_class_module != external_class_module:   336    337                         external_module_name = ".".join(external_class_module)   338                         print "* Importing", external_module_name   339                         obj = __import__(external_module_name, global_names, {}, [])   340                         global_names[external_name_parts[0]] = obj   341    342                     # Names local to this package may affect initialisation order.   343    344                     elif external_class_name not in class_file_init_index:   345                         try:   346                             this_class_name_index = class_file_init_index.index(this_class_name)   347    348                             # Either insert this name before the current class's   349                             # name.   350    351                             print "* Inserting", external_class_name   352                             class_file_init_index.insert(this_class_name_index, external_class_name)   353    354                         except ValueError:   355    356                             # Or add this name in anticipation of the current   357                             # class's name appearing.   358    359                             print "* Including", external_class_name   360                             class_file_init_index.append(external_class_name)   361    362                 # Add this class name to the initialisation index.   363    364                 if class_name not in class_file_init_index:   365                     class_file_init_index.append(this_class_name)   366                 class_file_init[this_class_name] = (cls, class_file)   367    368             # Finally, call __clinit__ methods for all relevant classes.   369    370             print "** Initialisation order", class_file_init_index   371             for class_name in class_file_init_index:   372                 cls, class_file = class_file_init[class_name]   373                 print "**", cls, class_file   374                 if hasattr(cls, "__clinit__"):   375                     eval(cls.__clinit__.func_code, global_names)   376    377         return module   378    379 ihooks.ModuleImporter(loader=ClassLoader(hooks=ClassHooks())).install()   380    381 # vim: tabstop=4 expandtab shiftwidth=4