echo "javasphinx not rerun: 'java' was not provided as an argument"
else
rm -rf source/java
- javasphinx-apidoc --force -o source/java/ ../src/bindings/java/org/simgrid/msg
+ source/_ext/javasphinx-apidoc --force -o source/java/ ../src/bindings/java/org/simgrid/msg
rm -f source/java/packages.rst # api_generated/source_java_packages.rst
rm -f source/java/org/simgrid/msg/package-index.rst # api_generated/source_java_org_simgrid_msg_package-index.rst
for f in source/java/org/simgrid/msg/* ; do
breathe
-javasphinx
sphinx>=1.8.0
sphinx_rtd_theme
--- /dev/null
+#!/usr/bin/python3
+
+# -*- coding: utf-8 -*-
+import re
+import sys
+
+from javasphinx.apidoc import main
+
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
--- /dev/null
+#
+# Copyright 2012-2015 Bronto Software, Inc. and contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from .domain import JavaDomain
+from .extdoc import javadoc_role
+
+def setup(app):
+ app.add_domain(JavaDomain)
+
+ app.add_config_value('javadoc_url_map', dict(), '')
+ app.add_role('java:extdoc', javadoc_role)
--- /dev/null
+#
+# Copyright 2012-2015 Bronto Software, Inc. and contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from __future__ import print_function, unicode_literals
+
+try:
+ import cPickle as pickle
+except:
+ import pickle
+
+import hashlib
+import logging
+import sys
+import os
+import os.path
+
+from optparse import OptionParser
+
+import javalang
+
+import javasphinx.compiler as compiler
+import javasphinx.util as util
+
+def encode_output(s):
+ if isinstance(s, str):
+ return s
+ else:
+ return s.encode('utf-8')
+
+def find_source_files(input_path, excludes):
+ """ Get a list of filenames for all Java source files within the given
+ directory.
+
+ """
+
+ java_files = []
+
+ input_path = os.path.normpath(os.path.abspath(input_path))
+
+ for dirpath, dirnames, filenames in os.walk(input_path):
+ if is_excluded(dirpath, excludes):
+ del dirnames[:]
+ continue
+
+ for filename in filenames:
+ if filename.endswith(".java"):
+ java_files.append(os.path.join(dirpath, filename))
+
+ return java_files
+
+def write_toc(packages, opts):
+ doc = util.Document()
+ doc.add_heading(opts.toc_title, '=')
+
+ toc = util.Directive('toctree')
+ toc.add_option('maxdepth', '2')
+ doc.add_object(toc)
+
+ for package in sorted(packages.keys()):
+ toc.add_content("%s/package-index\n" % package.replace('.', '/'))
+
+ filename = 'packages.' + opts.suffix
+ fullpath = os.path.join(opts.destdir, filename)
+
+ if os.path.exists(fullpath) and not (opts.force or opts.update):
+ sys.stderr.write(fullpath + ' already exists. Use -f to overwrite.\n')
+ sys.exit(1)
+
+ f = open(fullpath, 'w')
+ f.write(encode_output(doc.build()))
+ f.close()
+
+def write_documents(packages, documents, sources, opts):
+ package_contents = dict()
+
+ # Write individual documents
+ for fullname, (package, name, document) in documents.items():
+ if is_package_info_doc(name):
+ continue
+
+ package_path = package.replace('.', os.sep)
+ filebasename = name.replace('.', '-')
+ filename = filebasename + '.' + opts.suffix
+ dirpath = os.path.join(opts.destdir, package_path)
+ fullpath = os.path.join(dirpath, filename)
+
+ if not os.path.exists(dirpath):
+ os.makedirs(dirpath)
+ elif os.path.exists(fullpath) and not (opts.force or opts.update):
+ sys.stderr.write(fullpath + ' already exists. Use -f to overwrite.\n')
+ sys.exit(1)
+
+ # Add to package indexes
+ package_contents.setdefault(package, list()).append(filebasename)
+
+ if opts.update and os.path.exists(fullpath):
+ # If the destination file is newer than the source file than skip
+ # writing it out
+ source_mod_time = os.stat(sources[fullname]).st_mtime
+ dest_mod_time = os.stat(fullpath).st_mtime
+
+ if source_mod_time < dest_mod_time:
+ continue
+
+ f = open(fullpath, 'w')
+ f.write(encode_output(document))
+ f.close()
+
+ # Write package-index for each package
+ for package, classes in package_contents.items():
+ doc = util.Document()
+ doc.add_heading(package, '=')
+
+ #Adds the package documentation (if any)
+ if packages[package] != '':
+ documentation = packages[package]
+ doc.add_line("\n%s" % documentation)
+
+ doc.add_object(util.Directive('java:package', package))
+
+ toc = util.Directive('toctree')
+ toc.add_option('maxdepth', '1')
+
+ classes.sort()
+ for filebasename in classes:
+ toc.add_content(filebasename + '\n')
+ doc.add_object(toc)
+
+ package_path = package.replace('.', os.sep)
+ filename = 'package-index.' + opts.suffix
+ dirpath = os.path.join(opts.destdir, package_path)
+ fullpath = os.path.join(dirpath, filename)
+
+ if not os.path.exists(dirpath):
+ os.makedirs(dirpath)
+ elif os.path.exists(fullpath) and not (opts.force or opts.update):
+ sys.stderr.write(fullpath + ' already exists. Use -f to overwrite.\n')
+ sys.exit(1)
+
+ f = open(fullpath, 'w')
+ f.write(encode_output(doc.build()))
+ f.close()
+
+def get_newer(a, b):
+ if not os.path.exists(a):
+ return b
+
+ if not os.path.exists(b):
+ return a
+
+ a_mtime = int(os.stat(a).st_mtime)
+ b_mtime = int(os.stat(b).st_mtime)
+
+ if a_mtime < b_mtime:
+ return b
+
+ return a
+
+def format_syntax_error(e):
+ rest = ""
+ if e.at.position:
+ value = e.at.value
+ pos = e.at.position
+ rest = ' at %s line %d, character %d' % (value, pos[0], pos[1])
+ return e.description + rest
+
+def generate_from_source_file(doc_compiler, source_file, cache_dir):
+ if cache_dir:
+ fingerprint = hashlib.md5(source_file.encode()).hexdigest()
+ cache_file = os.path.join(cache_dir, 'parsed-' + fingerprint + '.p')
+
+ if get_newer(source_file, cache_file) == cache_file:
+ return pickle.load(open(cache_file, 'rb'))
+ else:
+ cache_file = None
+
+ f = open(source_file)
+ source = f.read()
+ f.close()
+
+ try:
+ ast = javalang.parse.parse(source)
+ except javalang.parser.JavaSyntaxError as e:
+ util.error('Syntax error in %s: %s', source_file, format_syntax_error(e))
+ except Exception:
+ util.unexpected('Unexpected exception while parsing %s', source_file)
+
+ documents = {}
+ try:
+ if source_file.endswith("package-info.java"):
+ if ast.package is not None:
+ documentation = doc_compiler.compile_docblock(ast.package)
+ documents[ast.package.name] = (ast.package.name, 'package-info', documentation)
+ else:
+ documents = doc_compiler.compile(ast)
+ except Exception:
+ util.unexpected('Unexpected exception while compiling %s', source_file)
+
+ if cache_file:
+ dump_file = open(cache_file, 'wb')
+ pickle.dump(documents, dump_file)
+ dump_file.close()
+
+ return documents
+
+def generate_documents(source_files, cache_dir, verbose, member_headers, parser):
+ documents = {}
+ sources = {}
+ doc_compiler = compiler.JavadocRestCompiler(None, member_headers, parser)
+
+ for source_file in source_files:
+ if verbose:
+ print('Processing', source_file)
+
+ this_file_documents = generate_from_source_file(doc_compiler, source_file, cache_dir)
+ for fullname in this_file_documents:
+ sources[fullname] = source_file
+
+ documents.update(this_file_documents)
+
+ #Existing packages dict, where each key is a package name
+ #and each value is the package documentation (if any)
+ packages = {}
+
+ #Gets the name of the package where the document was declared
+ #and adds it to the packages dict with no documentation.
+ #Package documentation, if any, will be collected from package-info.java files.
+ for package, name, _ in documents.values():
+ packages[package] = ""
+
+ #Gets packages documentation from package-info.java documents (if any).
+ for package, name, content in documents.values():
+ if is_package_info_doc(name):
+ packages[package] = content
+
+ return packages, documents, sources
+
+def normalize_excludes(rootpath, excludes):
+ f_excludes = []
+ for exclude in excludes:
+ if not os.path.isabs(exclude) and not exclude.startswith(rootpath):
+ exclude = os.path.join(rootpath, exclude)
+ f_excludes.append(os.path.normpath(exclude) + os.path.sep)
+ return f_excludes
+
+def is_excluded(root, excludes):
+ sep = os.path.sep
+ if not root.endswith(sep):
+ root += sep
+ for exclude in excludes:
+ if root.startswith(exclude):
+ return True
+ return False
+
+def is_package_info_doc(document_name):
+ ''' Checks if the name of a document represents a package-info.java file. '''
+ return document_name == 'package-info'
+
+
+def main(argv=sys.argv):
+ logging.basicConfig(level=logging.WARN)
+
+ parser = OptionParser(
+ usage="""\
+usage: %prog [options] -o <output_path> <input_path> [exclude_paths, ...]
+
+Look recursively in <input_path> for Java sources files and create reST files
+for all non-private classes, organized by package under <output_path>. A package
+index (package-index.<ext>) will be created for each package, and a top level
+table of contents will be generated named packages.<ext>.
+
+Paths matching any of the given exclude_paths (interpreted as regular
+expressions) will be skipped.
+
+Note: By default this script will not overwrite already created files.""")
+
+ parser.add_option('-o', '--output-dir', action='store', dest='destdir',
+ help='Directory to place all output', default='')
+ parser.add_option('-f', '--force', action='store_true', dest='force',
+ help='Overwrite all files')
+ parser.add_option('-c', '--cache-dir', action='store', dest='cache_dir',
+ help='Directory to stored cachable output')
+ parser.add_option('-u', '--update', action='store_true', dest='update',
+ help='Overwrite new and changed files', default=False)
+ parser.add_option('-T', '--no-toc', action='store_true', dest='notoc',
+ help='Don\'t create a table of contents file')
+ parser.add_option('-t', '--title', dest='toc_title', default='Javadoc',
+ help='Title to use on table of contents')
+ parser.add_option('--no-member-headers', action='store_false', default=True, dest='member_headers',
+ help='Don\'t generate headers for class members')
+ parser.add_option('-s', '--suffix', action='store', dest='suffix',
+ help='file suffix (default: rst)', default='rst')
+ parser.add_option('-I', '--include', action='append', dest='includes',
+ help='Additional input paths to scan', default=[])
+ parser.add_option('-p', '--parser', dest='parser_lib', default='lxml',
+ help='Beautiful Soup---html parser library option.')
+ parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
+ help='verbose output')
+
+ (opts, args) = parser.parse_args(argv[1:])
+
+ if not args:
+ parser.error('A source path is required.')
+
+ rootpath, excludes = args[0], args[1:]
+
+ input_paths = opts.includes
+ input_paths.append(rootpath)
+
+ if not opts.destdir:
+ parser.error('An output directory is required.')
+
+ if opts.suffix.startswith('.'):
+ opts.suffix = opts.suffix[1:]
+
+ for input_path in input_paths:
+ if not os.path.isdir(input_path):
+ sys.stderr.write('%s is not a directory.\n' % (input_path,))
+ sys.exit(1)
+
+ if not os.path.isdir(opts.destdir):
+ os.makedirs(opts.destdir)
+
+ if opts.cache_dir and not os.path.isdir(opts.cache_dir):
+ os.makedirs(opts.cache_dir)
+
+ excludes = normalize_excludes(rootpath, excludes)
+ source_files = []
+
+ for input_path in input_paths:
+ source_files.extend(find_source_files(input_path, excludes))
+
+ packages, documents, sources = generate_documents(source_files, opts.cache_dir, opts.verbose,
+ opts.member_headers, opts.parser_lib)
+
+ write_documents(packages, documents, sources, opts)
+
+ if not opts.notoc:
+ write_toc(packages, opts)
--- /dev/null
+#
+# Copyright 2012-2015 Bronto Software, Inc. and contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import javalang
+
+import javasphinx.formatter as formatter
+import javasphinx.util as util
+import javasphinx.htmlrst as htmlrst
+
+class JavadocRestCompiler(object):
+ """ Javadoc to ReST compiler. Builds ReST documentation from a Java syntax
+ tree. """
+
+ def __init__(self, filter=None, member_headers=True, parser='lxml'):
+ if filter:
+ self.filter = filter
+ else:
+ self.filter = self.__default_filter
+
+ self.converter = htmlrst.Converter(parser)
+
+ self.member_headers = member_headers
+
+ def __default_filter(self, node):
+ """Excludes private members and those tagged "@hide" / "@exclude" in their
+ docblocks.
+
+ """
+
+ if not isinstance(node, javalang.tree.Declaration):
+ return False
+
+ if 'private' in node.modifiers:
+ return False
+
+ if isinstance(node, javalang.tree.Documented) and node.documentation:
+ doc = javalang.javadoc.parse(node.documentation)
+ if 'hide' in doc.tags or 'exclude' in doc.tags:
+ return False
+
+ return True
+
+ def __html_to_rst(self, s):
+ return self.converter.convert(s)
+
+ def __output_doc(self, documented):
+ if not isinstance(documented, javalang.tree.Documented):
+ raise ValueError('node not documented')
+
+ output = util.Document()
+
+ if not documented.documentation:
+ return output
+
+ doc = javalang.javadoc.parse(documented.documentation)
+
+ if doc.description:
+ output.add(self.__html_to_rst(doc.description))
+ output.clear()
+
+ if doc.authors:
+ output.add_line(':author: %s' % (self.__html_to_rst(', '.join(doc.authors)),))
+
+ for name, value in doc.params:
+ output.add_line(':param %s: %s' % (name, self.__html_to_rst(value)))
+
+ for exception in doc.throws:
+ description = doc.throws[exception]
+ output.add_line(':throws %s: %s' % (exception, self.__html_to_rst(description)))
+
+ if doc.return_doc:
+ output.add_line(':return: %s' % (self.__html_to_rst(doc.return_doc),))
+
+ if doc.tags.get('see'):
+ output.clear()
+
+ see_also = ', '.join(self.__output_see(see) for see in doc.tags['see'])
+ output.add_line('**See also:** %s' % (see_also,))
+
+ return output
+
+ def __output_see(self, see):
+ """ Convert the argument to a @see tag to rest """
+
+ if see.startswith('<a href'):
+ # HTML link -- <a href="...">...</a>
+ return self.__html_to_rst(see)
+ elif '"' in see:
+ # Plain text
+ return see
+ else:
+ # Type reference (default)
+ return ':java:ref:`%s`' % (see.replace('#', '.').replace(' ', ''),)
+
+ def compile_type(self, declaration):
+ signature = util.StringBuilder()
+ formatter.output_declaration(declaration, signature)
+
+ doc = self.__output_doc(declaration)
+
+ directive = util.Directive('java:type', signature.build())
+ directive.add_content(doc)
+
+ return directive
+
+ def compile_enum_constant(self, enum, constant):
+ signature = util.StringBuilder()
+
+ for annotation in constant.annotations:
+ formatter.output_annotation(annotation, signature)
+
+ # All enum constants are public, static, and final
+ signature.append('public static final ')
+ signature.append(enum)
+ signature.append(' ')
+ signature.append(constant.name)
+
+ doc = self.__output_doc(constant)
+
+ directive = util.Directive('java:field', signature.build())
+ directive.add_content(doc)
+
+ return directive
+
+ def compile_field(self, field):
+ signature = util.StringBuilder()
+
+ for annotation in field.annotations:
+ formatter.output_annotation(annotation, signature)
+
+ formatter.output_modifiers(field.modifiers, signature)
+ signature.append(' ')
+
+ formatter.output_type(field.type, signature)
+ signature.append(' ')
+ signature.append(field.declarators[0].name)
+
+ doc = self.__output_doc(field)
+
+ directive = util.Directive('java:field', signature.build())
+ directive.add_content(doc)
+
+ return directive
+
+ def compile_constructor(self, constructor):
+ signature = util.StringBuilder()
+
+ for annotation in constructor.annotations:
+ formatter.output_annotation(annotation, signature)
+
+ formatter.output_modifiers(constructor.modifiers, signature)
+ signature.append(' ')
+
+ if constructor.type_parameters:
+ formatter.output_type_params(constructor.type_parameters, signature)
+ signature.append(' ')
+
+ signature.append(constructor.name)
+
+ signature.append('(')
+ formatter.output_list(formatter.output_formal_param, constructor.parameters, signature, ', ')
+ signature.append(')')
+
+ if constructor.throws:
+ signature.append(' throws ')
+ formatter.output_list(formatter.output_exception, constructor.throws, signature, ', ')
+
+ doc = self.__output_doc(constructor)
+
+ directive = util.Directive('java:constructor', signature.build())
+ directive.add_content(doc)
+
+ return directive
+
+ def compile_method(self, method):
+ signature = util.StringBuilder()
+
+ for annotation in method.annotations:
+ formatter.output_annotation(annotation, signature)
+
+ formatter.output_modifiers(method.modifiers, signature)
+ signature.append(' ')
+
+ if method.type_parameters:
+ formatter.output_type_params(method.type_parameters, signature)
+ signature.append(' ')
+
+ formatter.output_type(method.return_type, signature)
+ signature.append(' ')
+
+ signature.append(method.name)
+
+ signature.append('(')
+ formatter.output_list(formatter.output_formal_param, method.parameters, signature, ', ')
+ signature.append(')')
+
+ if method.throws:
+ signature.append(' throws ')
+ formatter.output_list(formatter.output_exception, method.throws, signature, ', ')
+
+ doc = self.__output_doc(method)
+
+ directive = util.Directive('java:method', signature.build())
+ directive.add_content(doc)
+
+ return directive
+
+ def compile_type_document(self, imports_block, package, name, declaration):
+ """ Compile a complete document, documenting a type and its members """
+
+ outer_type = name.rpartition('.')[0]
+
+ document = util.Document()
+ document.add(imports_block)
+ document.add_heading(name, '=')
+
+ method_summary = util.StringBuilder()
+ document.add_object(method_summary)
+
+ package_dir = util.Directive('java:package', package)
+ package_dir.add_option('noindex')
+ document.add_object(package_dir)
+
+ # Add type-level documentation
+ type_dir = self.compile_type(declaration)
+ if outer_type:
+ type_dir.add_option('outertype', outer_type)
+ document.add_object(type_dir)
+
+ if isinstance(declaration, javalang.tree.EnumDeclaration):
+ enum_constants = list(declaration.body.constants)
+ enum_constants.sort(key=lambda c: c.name)
+
+ document.add_heading('Enum Constants')
+ for enum_constant in enum_constants:
+ if self.member_headers:
+ document.add_heading(enum_constant.name, '^')
+ c = self.compile_enum_constant(name, enum_constant)
+ c.add_option('outertype', name)
+ document.add_object(c)
+
+ fields = list(filter(self.filter, declaration.fields))
+ if fields:
+ document.add_heading('Fields', '-')
+ fields.sort(key=lambda f: f.declarators[0].name)
+ for field in fields:
+ if self.member_headers:
+ document.add_heading(field.declarators[0].name, '^')
+ f = self.compile_field(field)
+ f.add_option('outertype', name)
+ document.add_object(f)
+
+ constructors = list(filter(self.filter, declaration.constructors))
+ if constructors:
+ document.add_heading('Constructors', '-')
+ constructors.sort(key=lambda c: c.name)
+ for constructor in constructors:
+ if self.member_headers:
+ document.add_heading(constructor.name, '^')
+ c = self.compile_constructor(constructor)
+ c.add_option('outertype', name)
+ document.add_object(c)
+
+ methods = list(filter(self.filter, declaration.methods))
+ if methods:
+ document.add_heading('Methods', '-')
+ methods.sort(key=lambda m: m.name)
+ for method in methods:
+ if self.member_headers:
+ document.add_heading(method.name, '^')
+ m = self.compile_method(method)
+ m.add_option('outertype', name)
+ document.add_object(m)
+
+ return document
+
+ def compile(self, ast):
+ """ Compile autodocs for the given Java syntax tree. Documents will be
+ returned documenting each separate type. """
+
+ documents = {}
+
+ imports = util.StringBuilder()
+ for imp in ast.imports:
+ if imp.static or imp.wildcard:
+ continue
+
+ package_parts = []
+ cls_parts = []
+
+ for part in imp.path.split('.'):
+ if cls_parts or part[0].isupper():
+ cls_parts.append(part)
+ else:
+ package_parts.append(part)
+
+
+ # If the import's final part wasn't capitalized,
+ # append it to the class parts anyway so sphinx doesn't complain.
+ if cls_parts == []:
+ cls_parts.append(package_parts.pop())
+
+ package = '.'.join(package_parts)
+ cls = '.'.join(cls_parts)
+
+ imports.append(util.Directive('java:import', package + ' ' + cls).build())
+ import_block = imports.build()
+
+ if not ast.package:
+ raise ValueError('File must have package declaration')
+
+ package = ast.package.name
+ type_declarations = []
+ for path, node in ast.filter(javalang.tree.TypeDeclaration):
+ if not self.filter(node):
+ continue
+
+ classes = [n.name for n in path if isinstance(n, javalang.tree.TypeDeclaration)]
+ classes.append(node.name)
+
+ name = '.'.join(classes)
+ type_declarations.append((package, name, node))
+
+ for package, name, declaration in type_declarations:
+ full_name = package + '.' + name
+ document = self.compile_type_document(import_block, package, name, declaration)
+ documents[full_name] = (package, name, document.build())
+ return documents
+
+ def compile_docblock(self, documented):
+ ''' Compiles a single, standalone docblock. '''
+ return self.__output_doc(documented).build()
--- /dev/null
+#
+# Copyright 2012-2015 Bronto Software, Inc. and contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import re
+import string
+
+from docutils import nodes
+from docutils.parsers.rst import Directive, directives
+
+from sphinx import addnodes, version_info
+from sphinx.roles import XRefRole
+from sphinx.locale import l_, _
+from sphinx.domains import Domain, ObjType
+from sphinx.directives import ObjectDescription
+from sphinx.util.nodes import make_refnode
+from sphinx.util.docfields import Field, TypedField, GroupedField
+
+import javalang
+
+import javasphinx.extdoc as extdoc
+import javasphinx.formatter as formatter
+import javasphinx.util as util
+
+# Classes in java.lang. These are available without an import.
+java_dot_lang = set([
+ 'AbstractMethodError', 'Appendable', 'ArithmeticException',
+ 'ArrayIndexOutOfBoundsException', 'ArrayStoreException', 'AssertionError',
+ 'AutoCloseable', 'Boolean', 'BootstrapMethodError', 'Byte', 'Character',
+ 'CharSequence', 'Class', 'ClassCastException', 'ClassCircularityError',
+ 'ClassFormatError', 'ClassLoader', 'ClassNotFoundException', 'ClassValue',
+ 'Cloneable', 'CloneNotSupportedException', 'Comparable', 'Compiler',
+ 'Deprecated', 'Double', 'Enum', 'EnumConstantNotPresentException', 'Error',
+ 'Exception', 'ExceptionInInitializerError', 'Float', 'IllegalAccessError',
+ 'IllegalAccessException', 'IllegalArgumentException',
+ 'IllegalMonitorStateException', 'IllegalStateException',
+ 'IllegalThreadStateException', 'IncompatibleClassChangeError',
+ 'IndexOutOfBoundsException', 'InheritableThreadLocal', 'InstantiationError',
+ 'InstantiationException', 'Integer', 'InternalError', 'InterruptedException',
+ 'Iterable', 'LinkageError', 'Long', 'Math', 'NegativeArraySizeException',
+ 'NoClassDefFoundError', 'NoSuchFieldError', 'NoSuchFieldException',
+ 'NoSuchMethodError', 'NoSuchMethodException', 'NullPointerException', 'Number',
+ 'NumberFormatException', 'Object', 'OutOfMemoryError', 'Override', 'Package',
+ 'Process', 'ProcessBuilder', 'Readable', 'ReflectiveOperationException',
+ 'Runnable', 'Runtime', 'RuntimeException', 'RuntimePermission', 'SafeVarargs',
+ 'SecurityException', 'SecurityManager', 'Short', 'StackOverflowError',
+ 'StackTraceElement', 'StrictMath', 'String', 'StringBuffer', 'StringBuilder',
+ 'StringIndexOutOfBoundsException', 'SuppressWarnings', 'System', 'Thread',
+ 'ThreadDeath', 'ThreadGroup', 'ThreadLocal', 'Throwable',
+ 'TypeNotPresentException', 'UnknownError', 'UnsatisfiedLinkError',
+ 'UnsupportedClassVersionError', 'UnsupportedOperationException', 'VerifyError',
+ 'VirtualMachineError', 'Void'])
+
+class JavaObject(ObjectDescription):
+ option_spec = {
+ 'noindex': directives.flag,
+ 'package': directives.unchanged,
+ 'outertype': directives.unchanged
+ }
+
+ def _build_ref_node(self, target):
+ ref = addnodes.pending_xref('', refdomain='java', reftype='type', reftarget=target, modname=None, classname=None)
+ ref['java:outertype'] = self.get_type()
+
+ package = self.env.temp_data.get('java:imports', dict()).get(target, None)
+
+ if not package and target in java_dot_lang:
+ package = 'java.lang'
+
+ if package:
+ ref['java:imported'] = True
+ ref['java:package'] = package
+ else:
+ ref['java:imported'] = False
+ ref['java:package'] = self.get_package()
+
+ return ref
+
+ def _build_type_node(self, typ):
+ if isinstance(typ, javalang.tree.ReferenceType):
+ if typ.dimensions:
+ dim = '[]' * len(typ.dimensions)
+ else:
+ dim = ''
+
+ target = typ.name
+ parts = []
+
+ while typ:
+ ref_node = self._build_ref_node(target)
+ ref_node += nodes.Text(typ.name, typ.name)
+ parts.append(ref_node)
+
+ if typ.arguments:
+ parts.append(nodes.Text('<', '<'))
+
+ first = True
+ for type_arg in typ.arguments:
+ if first:
+ first = False
+ else:
+ parts.append(nodes.Text(', ', ', '))
+
+ if type_arg.pattern_type == '?':
+ parts.append(nodes.Text('?', '?'))
+ else:
+ if type_arg.pattern_type:
+ s = '? %s ' % (type_arg.pattern_type,)
+ parts.append(nodes.Text(s, s))
+ parts.extend(self._build_type_node(type_arg.type))
+
+ parts.append(nodes.Text('>', '>'))
+
+ typ = typ.sub_type
+
+ if typ:
+ target = target + '.' + typ.name
+ parts.append(nodes.Text('.', '.'))
+ elif dim:
+ parts.append(nodes.Text(dim, dim))
+
+ return parts
+ else:
+ type_repr = formatter.output_type(typ).build()
+ return [nodes.Text(type_repr, type_repr)]
+
+ def _build_type_node_list(self, types):
+ parts = self._build_type_node(types[0])
+ for typ in types[1:]:
+ parts.append(nodes.Text(', ', ', '))
+ parts.extend(self._build_type_node(typ))
+ return parts
+
+ def handle_signature(self, sig, signode):
+ handle_name = 'handle_%s_signature' % (self.objtype,)
+ handle = getattr(self, handle_name, None)
+
+ if handle:
+ return handle(sig, signode)
+ else:
+ raise NotImplementedError
+
+ def get_index_text(self, package, type, name):
+ raise NotImplementedError
+
+ def get_package(self):
+ return self.options.get('package', self.env.temp_data.get('java:package'))
+
+ def get_type(self):
+ return self.options.get('outertype', '.'.join(self.env.temp_data.get('java:outertype', [])))
+
+ def add_target_and_index(self, name, sig, signode):
+ package = self.get_package()
+ type = self.get_type();
+
+ fullname = '.'.join(filter(None, (package, type, name)))
+ basename = fullname.partition('(')[0]
+
+ # note target
+ if fullname not in self.state.document.ids:
+ signode['names'].append(fullname)
+ signode['ids'].append(fullname)
+ signode['first'] = (not self.names)
+ self.state.document.note_explicit_target(signode)
+
+ objects = self.env.domaindata['java']['objects']
+ if fullname in objects:
+ self.state_machine.reporter.warning(
+ 'duplicate object description of %s, ' % fullname +
+ 'other instance in ' + self.env.doc2path(objects[fullname][0]) +
+ ', use :noindex: for one of them',
+ line=self.lineno)
+
+ objects[fullname] = (self.env.docname, self.objtype, basename)
+
+ indextext = self.get_index_text(package, type, name)
+ if indextext:
+ self.indexnode['entries'].append(_create_indexnode(indextext, fullname))
+
+ def before_content(self):
+ self.set_type = False
+
+ if self.objtype == 'type' and self.names:
+ self.set_type = True
+ self.env.temp_data.setdefault('java:outertype', list()).append(self.names[0])
+
+ def after_content(self):
+ if self.set_type:
+ self.env.temp_data['java:outertype'].pop()
+
+class JavaMethod(JavaObject):
+ doc_field_types = [
+ TypedField('parameter', label=l_('Parameters'),
+ names=('param', 'parameter', 'arg', 'argument'),
+ typerolename='type', typenames=('type',)),
+ Field('returnvalue', label=l_('Returns'), has_arg=False,
+ names=('returns', 'return')),
+ GroupedField('throws', names=('throws',), label=l_('Throws'), rolename='type')
+ ]
+
+ def handle_method_signature(self, sig, signode):
+ try:
+ member = javalang.parse.parse_member_signature(sig)
+ except javalang.parser.JavaSyntaxError:
+ raise self.error("syntax error in method signature")
+
+ if not isinstance(member, javalang.tree.MethodDeclaration):
+ raise self.error("expected method declaration")
+
+ mods = formatter.output_modifiers(member.modifiers).build()
+ signode += nodes.Text(mods + ' ', mods + ' ')
+
+ if member.type_parameters:
+ type_params = formatter.output_type_params(member.type_parameters).build()
+ signode += nodes.Text(type_params, type_params)
+ signode += nodes.Text(' ', ' ')
+
+ rnode = addnodes.desc_type('', '')
+ rnode += self._build_type_node(member.return_type)
+
+ signode += rnode
+ signode += nodes.Text(' ', ' ')
+ signode += addnodes.desc_name(member.name, member.name)
+
+ paramlist = addnodes.desc_parameterlist()
+ for parameter in member.parameters:
+ param = addnodes.desc_parameter('', '', noemph=True)
+ param += self._build_type_node(parameter.type)
+
+ if parameter.varargs:
+ param += nodes.Text('...', '')
+
+ param += nodes.emphasis(' ' + parameter.name, ' ' + parameter.name)
+ paramlist += param
+ signode += paramlist
+
+ param_reprs = [formatter.output_type(param.type, with_generics=False).build() for param in member.parameters]
+ return member.name + '(' + ', '.join(param_reprs) + ')'
+
+ def get_index_text(self, package, type, name):
+ return _('%s (Java method)' % (name,))
+
+class JavaConstructor(JavaObject):
+ doc_field_types = [
+ TypedField('parameter', label=l_('Parameters'),
+ names=('param', 'parameter', 'arg', 'argument'),
+ typerolename='type', typenames=('type',)),
+ GroupedField('throws', names=('throws',), label=l_('Throws'))
+ ]
+
+ def handle_constructor_signature(self, sig, signode):
+ try:
+ member = javalang.parse.parse_constructor_signature(sig)
+ except javalang.parser.JavaSyntaxError:
+ raise self.error("syntax error in constructor signature")
+
+ if not isinstance(member, javalang.tree.ConstructorDeclaration):
+ raise self.error("expected constructor declaration")
+
+ mods = formatter.output_modifiers(member.modifiers).build()
+ signode += nodes.Text(mods + ' ', mods + ' ')
+
+ signode += addnodes.desc_name(member.name, member.name)
+
+ paramlist = addnodes.desc_parameterlist()
+ for parameter in member.parameters:
+ param = addnodes.desc_parameter('', '', noemph=True)
+ param += self._build_type_node(parameter.type)
+
+ if parameter.varargs:
+ param += nodes.Text('...', '')
+
+ param += nodes.emphasis(' ' + parameter.name, ' ' + parameter.name)
+ paramlist += param
+ signode += paramlist
+
+ param_reprs = [formatter.output_type(param.type, with_generics=False).build() for param in member.parameters]
+ return '%s(%s)' % (member.name, ', '.join(param_reprs))
+
+ def get_index_text(self, package, type, name):
+ return _('%s (Java constructor)' % (name,))
+
+class JavaType(JavaObject):
+ doc_field_types = [
+ GroupedField('parameter', names=('param',), label=l_('Parameters'))
+ ]
+
+ declaration_type = None
+
+ def handle_type_signature(self, sig, signode):
+ try:
+ member = javalang.parse.parse_type_signature(sig)
+ except javalang.parser.JavaSyntaxError:
+ raise self.error("syntax error in field signature")
+
+ if isinstance(member, javalang.tree.ClassDeclaration):
+ self.declaration_type = 'class'
+ elif isinstance(member, javalang.tree.InterfaceDeclaration):
+ self.declaration_type = 'interface'
+ elif isinstance(member, javalang.tree.EnumDeclaration):
+ self.declaration_type = 'enum'
+ elif isinstance(member, javalang.tree.AnnotationDeclaration):
+ self.declaration_type = 'annotation'
+ else:
+ raise self.error("expected type declaration")
+
+ mods = formatter.output_modifiers(member.modifiers).build()
+ signode += nodes.Text(mods + ' ', mods + ' ')
+
+ if self.declaration_type == 'class':
+ signode += nodes.Text('class ', 'class ')
+ elif self.declaration_type == 'interface':
+ signode += nodes.Text('interface ', 'interface ')
+ elif self.declaration_type == 'enum':
+ signode += nodes.Text('enum ', 'enum ')
+ elif self.declaration_type == 'annotation':
+ signode += nodes.Text('@interface ', '@interface ')
+
+ signode += addnodes.desc_name(member.name, member.name)
+
+ if self.declaration_type in ('class', 'interface') and member.type_parameters:
+ type_params = formatter.output_type_params(member.type_parameters).build()
+ signode += nodes.Text(type_params, type_params)
+
+ if self.declaration_type == 'class':
+ if member.extends:
+ extends = ' extends '
+ signode += nodes.Text(extends, extends)
+ signode += self._build_type_node(member.extends)
+ if member.implements:
+ implements = ' implements '
+ signode += nodes.Text(implements, implements)
+ signode += self._build_type_node_list(member.implements)
+ elif self.declaration_type == 'interface':
+ if member.extends:
+ extends = ' extends '
+ signode += nodes.Text(extends, extends)
+ signode += self._build_type_node_list(member.extends)
+ elif self.declaration_type == 'enum':
+ if member.implements:
+ implements = ' implements '
+ signode += nodes.Text(implements, implements)
+ signode += self._build_type_node_list(member.implements)
+
+ return member.name
+
+ def get_index_text(self, package, type, name):
+ return _('%s (Java %s)' % (name, self.declaration_type))
+
+class JavaField(JavaObject):
+ def handle_field_signature(self, sig, signode):
+ try:
+ member = javalang.parse.parse_member_signature(sig)
+ except javalang.parser.JavaSyntaxError:
+ raise self.error("syntax error in field signature")
+
+ if not isinstance(member, javalang.tree.FieldDeclaration):
+ raise self.error("expected field declaration")
+
+ mods = formatter.output_modifiers(member.modifiers).build()
+ signode += nodes.Text(mods + ' ', mods + ' ')
+
+ tnode = addnodes.desc_type('', '')
+ tnode += self._build_type_node(member.type)
+
+ signode += tnode
+ signode += nodes.Text(' ', ' ')
+
+ if len(member.declarators) > 1:
+ self.error('only one field may be documented at a time')
+
+ declarator = member.declarators[0]
+ signode += addnodes.desc_name(declarator.name, declarator.name)
+
+ dim = '[]' * len(declarator.dimensions)
+ signode += nodes.Text(dim)
+
+ if declarator.initializer and isinstance(declarator.initializer, javalang.tree.Literal):
+ signode += nodes.Text(' = ' + declarator.initializer.value)
+
+ return declarator.name
+
+ def get_index_text(self, package, type, name):
+ return _('%s (Java field)' % (name,))
+
+class JavaPackage(Directive):
+ """
+ Directive to mark description of a new package.
+ """
+
+ has_content = False
+ required_arguments = 1
+ optional_arguments = 0
+ final_argument_whitespace = False
+ option_spec = {
+ 'noindex': directives.flag,
+ }
+
+ def run(self):
+ env = self.state.document.settings.env
+ package = self.arguments[0].strip()
+ noindex = 'noindex' in self.options
+ env.temp_data['java:package'] = package
+ env.domaindata['java']['objects'][package] = (env.docname, 'package', package)
+ ret = []
+
+ if not noindex:
+ targetnode = nodes.target('', '', ids=['package-' + package], ismod=True)
+ self.state.document.note_explicit_target(targetnode)
+
+ # the platform and synopsis aren't printed; in fact, they are only
+ # used in the modindex currently
+ ret.append(targetnode)
+
+ indextext = _('%s (package)') % (package,)
+ inode = addnodes.index(entries=[_create_indexnode(indextext, 'package-' + package)])
+ ret.append(inode)
+
+ return ret
+
+class JavaImport(Directive):
+ """
+ This directive is just to tell Sphinx the source of a referenced type.
+ """
+
+ has_content = False
+ required_arguments = 2
+ optional_arguments = 0
+ final_argument_whitespace = False
+ option_spec = {}
+
+ def run(self):
+ env = self.state.document.settings.env
+ package, typename = self.arguments
+
+ env.temp_data.setdefault('java:imports', dict())[typename] = package
+ return []
+
+class JavaXRefRole(XRefRole):
+ def process_link(self, env, refnode, has_explicit_title, title, target):
+ refnode['java:outertype'] = '.'.join(env.temp_data.get('java:outertype', list()))
+
+ target = target.lstrip('~')
+
+ # Strip a method component from the target
+ basetype = target
+ if '(' in basetype:
+ basetype = basetype.partition('(')[0]
+ if '.' in basetype:
+ basetype = basetype.rpartition('.')[0]
+
+ package = env.temp_data.get('java:imports', dict()).get(basetype, None)
+
+ if package:
+ refnode['java:imported'] = True
+ refnode['java:package'] = package
+ else:
+ refnode['java:imported'] = False
+ refnode['java:package'] = env.temp_data.get('java:package')
+
+ if not has_explicit_title:
+ # if the first character is a tilde, don't display the module/class
+ # parts of the contents
+ if title[0:1] == '~':
+ title = title.partition('(')[0]
+ title = title[1:]
+ dot = title.rfind('.')
+ if dot != -1:
+ title = title[dot+1:]
+
+ return title, target
+
+class JavaDomain(Domain):
+ """Java language domain."""
+ name = 'java'
+ label = 'Java'
+
+ object_types = {
+ 'package': ObjType(l_('package'), 'package', 'ref'),
+ 'type': ObjType(l_('type'), 'type', 'ref'),
+ 'field': ObjType(l_('field'), 'field', 'ref'),
+ 'constructor': ObjType(l_('constructor'), 'construct', 'ref'),
+ 'method': ObjType(l_('method'), 'meth', 'ref')
+ }
+
+ directives = {
+ 'package': JavaPackage,
+ 'type': JavaType,
+ 'field': JavaField,
+ 'constructor': JavaConstructor,
+ 'method': JavaMethod,
+ 'import': JavaImport
+ }
+
+ roles = {
+ 'package': JavaXRefRole(),
+ 'type': JavaXRefRole(),
+ 'field': JavaXRefRole(),
+ 'construct': JavaXRefRole(),
+ 'meth': JavaXRefRole(),
+ 'ref': JavaXRefRole(),
+ }
+
+ initial_data = {
+ 'objects': {}, # fullname -> docname, objtype, basename
+ }
+
+ def clear_doc(self, docname):
+ objects = dict(self.data['objects'])
+
+ for fullname, (fn, _, _) in objects.items():
+ if fn == docname:
+ del self.data['objects'][fullname]
+
+ def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
+ objects = self.data['objects']
+ package = node.get('java:package')
+ imported = node.get('java:imported')
+ type_context = node.get('java:outertype')
+
+ # Partial function to make building the response easier
+ make_ref = lambda fullname: make_refnode(builder, fromdocname, objects[fullname][0], fullname, contnode, fullname)
+
+ # Check for fully qualified references
+ if target in objects:
+ return make_ref(target)
+
+ # Try with package name prefixed
+ if package:
+ fullname = package + '.' + target
+ if fullname in objects:
+ return make_ref(fullname)
+
+ # Try with package and type prefixed
+ if package and type_context:
+ fullname = package + '.' + type_context + '.' + target
+ if fullname in objects:
+ return make_ref(fullname)
+
+ # Try to find a matching suffix
+ suffix = '.' + target
+ basename_match = None
+ basename_suffix = suffix.partition('(')[0]
+
+ for fullname, (_, _, basename) in objects.items():
+ if fullname.endswith(suffix):
+ return make_ref(fullname)
+ elif basename.endswith(basename_suffix):
+ basename_match = fullname
+
+ if basename_match:
+ return make_ref(basename_match)
+
+ # Try creating an external documentation reference
+ ref = extdoc.get_javadoc_ref(self.env, target, target)
+
+ if not ref and target in java_dot_lang:
+ fulltarget = 'java.lang.' + target
+ ref = extdoc.get_javadoc_ref(self.env, fulltarget, fulltarget)
+
+ # If the target was imported try with the package prefixed
+ if not ref and imported:
+ fulltarget = package + '.' + target
+ ref = extdoc.get_javadoc_ref(self.env, fulltarget, fulltarget)
+
+ if ref:
+ ref.append(contnode)
+ return ref
+ else:
+ return None
+
+ def get_objects(self):
+ for refname, (docname, type, _) in self.data['objects'].items():
+ yield (refname, refname, type, docname, refname, 1)
+
+
+def _create_indexnode(indextext, fullname):
+ # See https://github.com/sphinx-doc/sphinx/issues/2673
+ if version_info < (1, 4):
+ return ('single', indextext, fullname, '')
+ else:
+ return ('single', indextext, fullname, '', None)
--- /dev/null
+#
+# Copyright 2012-2015 Bronto Software, Inc. and contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import re
+
+from docutils import nodes, utils
+from sphinx.util.nodes import split_explicit_title
+
+def get_javadoc_ref(app, rawtext, text):
+ javadoc_url_map = app.config.javadoc_url_map
+
+ # Add default Java SE sources
+ if not javadoc_url_map.get("java"):
+ javadoc_url_map["java"] = ("http://docs.oracle.com/javase/8/docs/api", 'javadoc8')
+ if not javadoc_url_map.get("javax"):
+ javadoc_url_map["javax"] = ("http://docs.oracle.com/javase/8/docs/api", 'javadoc8')
+ if not javadoc_url_map.get("org.xml"):
+ javadoc_url_map["org.xml"] = ("http://docs.oracle.com/javase/8/docs/api", 'javadoc8')
+ if not javadoc_url_map.get("org.w3c"):
+ javadoc_url_map["org.w3c"] = ("http://docs.oracle.com/javase/8/docs/api", 'javadoc8')
+
+ source = None
+ package = ''
+ method = None
+
+ if '(' in text:
+ # If the javadoc contains a line like this:
+ # {@link #sort(List)}
+ # there is no package so the text.rindex will fail
+ try:
+ split_point = text.rindex('.', 0, text.index('('))
+ method = text[split_point + 1:]
+ text = text[:split_point]
+ except ValueError:
+ pass
+
+ for pkg, (baseurl, ext_type) in javadoc_url_map.items():
+ if text.startswith(pkg + '.') and len(pkg) > len(package):
+ source = baseurl, ext_type
+ package = pkg
+
+ if not source:
+ return None
+
+ baseurl, ext_type = source
+
+ package_parts = []
+ cls_parts = []
+
+ for part in text.split('.'):
+ if cls_parts or part[0].isupper():
+ cls_parts.append(part)
+ else:
+ package_parts.append(part)
+
+ package = '.'.join(package_parts)
+ cls = '.'.join(cls_parts)
+
+ if not baseurl.endswith('/'):
+ baseurl = baseurl + '/'
+
+ if ext_type == 'javadoc':
+ if not cls:
+ cls = 'package-summary'
+ source = baseurl + package.replace('.', '/') + '/' + cls + '.html'
+ if method:
+ source = source + '#' + method
+ elif ext_type == 'javadoc8':
+ if not cls:
+ cls = 'package-summary'
+ source = baseurl + package.replace('.', '/') + '/' + cls + '.html'
+ if method:
+ source = source + '#' + re.sub(r'[()]', '-', method)
+ elif ext_type == 'sphinx':
+ if not cls:
+ cls = 'package-index'
+ source = baseurl + package.replace('.', '/') + '/' + cls.replace('.', '-') + '.html'
+ if method:
+ source = source + '#' + package + '.' + cls + '.' + method
+ else:
+ raise ValueError('invalid target specifier ' + ext_type)
+
+ title = '.'.join(filter(None, (package, cls, method)))
+ node = nodes.reference(rawtext, '')
+ node['refuri'] = source
+ node['reftitle'] = title
+
+ return node
+
+def javadoc_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
+ """ Role for linking to external Javadoc """
+
+ has_explicit_title, title, target = split_explicit_title(text)
+ title = utils.unescape(title)
+ target = utils.unescape(target)
+
+ if not has_explicit_title:
+ target = target.lstrip('~')
+
+ if title[0] == '~':
+ title = title[1:].rpartition('.')[2]
+
+ app = inliner.document.settings.env.app
+ ref = get_javadoc_ref(app, rawtext, target)
+
+ if not ref:
+ raise ValueError("no Javadoc source found for %s in javadoc_url_map" % (target,))
+
+ ref.append(nodes.Text(title, title))
+
+ return [ref], []
--- /dev/null
+#
+# Copyright 2012-2015 Bronto Software, Inc. and contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Convert Java syntax tree nodes to string representations.
+
+"""
+
+import javalang
+
+from .util import StringBuilder
+
+# The order for displaying modifiers
+__modifiers_order = ('public', 'protected', 'private', 'static', 'abstract', 'final',
+ 'native', 'synchronized', 'transient', 'volatile', 'strictfp')
+
+def formatter(f):
+ def _f(node, output=None, **kwargs):
+ if output is None:
+ output = StringBuilder()
+
+ f(node, output, **kwargs)
+ return output
+ return _f
+
+def output_list(f, items, output=None, sep=', '):
+ if items:
+ f(items[0], output)
+ for item in items[1:]:
+ output.append(sep)
+ f(item, output)
+
+@formatter
+def output_annotation(annotation, output):
+ output.append('@')
+ output.append(annotation.name)
+ output.append(' ')
+
+@formatter
+def output_type(type, output, with_generics=True):
+ if not type:
+ output.append('void')
+ return
+
+ if type.dimensions:
+ dim = '[]' * len(type.dimensions)
+ else:
+ dim = ''
+
+ if isinstance(type, javalang.tree.BasicType):
+ output.append(type.name)
+ else:
+ while type:
+ output.append(type.name)
+
+ if with_generics:
+ output_type_args(type.arguments, output)
+
+ type = type.sub_type
+
+ if type:
+ output.append('.')
+ output.append(dim)
+
+@formatter
+def output_exception(exception, output):
+ output.append(exception)
+
+@formatter
+def output_type_arg(type_arg, output):
+ if type_arg.pattern_type == '?':
+ output.append('?')
+ else:
+ if type_arg.pattern_type:
+ output.append('? ')
+ output.append(type_arg.pattern_type)
+ output.append(' ')
+
+ output_type(type_arg.type, output)
+
+@formatter
+def output_type_args(type_args, output):
+ if type_args:
+ output.append('<')
+ output_list(output_type_arg, type_args, output, ', ')
+ output.append('>')
+
+@formatter
+def output_type_param(type_param, output):
+ output.append(type_param.name)
+
+ if type_param.extends:
+ output.append(' extends ')
+ output_list(output_type, type_param.extends, output, ' & ')
+
+@formatter
+def output_type_params(type_params, output):
+ if type_params:
+ output.append('<')
+ output_list(output_type_param, type_params, output, ', ')
+ output.append('>')
+
+@formatter
+def output_declaration(declaration, output):
+ for annotation in declaration.annotations:
+ output_annotation(annotation, output)
+
+ output_modifiers(declaration.modifiers, output)
+ output.append(' ')
+
+ if isinstance(declaration, javalang.tree.ClassDeclaration):
+ output.append('class ')
+ elif isinstance(declaration, javalang.tree.EnumDeclaration):
+ output.append('enum ')
+ elif isinstance(declaration, javalang.tree.InterfaceDeclaration):
+ output.append('interface ')
+ elif isinstance(declaration, javalang.tree.AnnotationDeclaration):
+ output.append('@interface ')
+
+ output.append(declaration.name)
+
+ if isinstance(declaration, (javalang.tree.ClassDeclaration, javalang.tree.InterfaceDeclaration)):
+ output_type_params(declaration.type_parameters, output)
+
+ if isinstance(declaration, javalang.tree.ClassDeclaration) and declaration.extends:
+ output.append(' extends ')
+ output_type(declaration.extends, output)
+
+ if isinstance(declaration, javalang.tree.InterfaceDeclaration) and declaration.extends:
+ output.append(' extends ')
+ output_list(output_type, declaration.extends, output, ', ')
+
+ if isinstance(declaration, (javalang.tree.ClassDeclaration, javalang.tree.EnumDeclaration)) and declaration.implements:
+ output.append(' implements ')
+ output_list(output_type, declaration.implements, output, ', ')
+
+@formatter
+def output_formal_param(param, output):
+ output_type(param.type, output)
+
+ if param.varargs:
+ output.append('...')
+
+ output.append(' ')
+ output.append(param.name)
+
+@formatter
+def output_modifiers(modifiers, output):
+ ordered_modifiers = [mod for mod in __modifiers_order if mod in modifiers]
+ output_list(lambda mod, output: output.append(mod), ordered_modifiers, output, ' ')
--- /dev/null
+#
+# Copyright 2013-2015 Bronto Software, Inc. and contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from __future__ import unicode_literals
+from builtins import str
+
+import collections
+import re
+
+from xml.sax.saxutils import escape as html_escape
+from bs4 import BeautifulSoup
+
+Cell = collections.namedtuple('Cell', ['type', 'rowspan', 'colspan', 'contents'])
+
+class Converter(object):
+ def __init__(self, parser):
+ self._unknown_tags = set()
+ self._clear = '\n\n..\n\n'
+
+ # Regular expressions
+ self._preprocess_anchors = re.compile(r'<a\s+name\s*=\s*["\']?(.+?)["\']?\s*>')
+ self._post_process_empty_lines = re.compile(r'^\s+$', re.MULTILINE)
+ self._post_process_compress_lines = re.compile(r'\n{3,}')
+ self._whitespace_with_newline = re.compile(r'[\s\n]+')
+ self._whitespace = re.compile(r'\s+')
+ self._html_tag = re.compile(r'<.*?>')
+
+ self._preprocess_entity = re.compile(r'&(nbsp|lt|gt|amp)([^;]|[\n])')
+ self._parser = parser
+
+ # --------------------------------------------------------------------------
+ # ---- reST Utility Methods ----
+
+ def _unicode(self, s):
+ if isinstance(s, unicode):
+ return s
+ else:
+ return unicode(s, 'utf8')
+
+ def _separate(self, s):
+ return u'\n\n' + s + u'\n\n'
+
+ def _escape_inline(self, s):
+ return '\\ ' + s + '\\ '
+
+ def _inline(self, tag, s):
+ # Seems fishy if our inline markup spans lines. We will instead just return
+ # the string as is
+ if '\n' in s:
+ return s
+
+ s = s.strip()
+
+ if not s:
+ return s
+
+ return self._escape_inline(tag + s.strip() + tag)
+
+ def _role(self, role, s, label=None):
+ if label:
+ return self._escape_inline(':%s:`%s <%s>`' % (role, label, s))
+ else:
+ return self._escape_inline(':%s:`%s`' % (role, s))
+
+ def _directive(self, directive, body=None):
+ header = '\n\n.. %s::\n\n' % (directive,)
+
+ if body:
+ return header + self._left_justify(body, 3) + '\n\n'
+ else:
+ return header + '\n'
+
+ def _hyperlink(self, target, label):
+ return self._escape_inline('`%s <%s>`_' % (label, target))
+
+ def _listing(self, marker, items):
+ items = [self._left_justify(item, len(marker) + 1) for item in items]
+ items = [marker + item[len(marker):] for item in items]
+ return self._separate('..') + self._separate('\n'.join(items))
+
+ def _left_justify(self, s, indent=0):
+ lines = [l.rstrip() for l in s.split('\n')]
+ indents = [len(l) - len(l.lstrip()) for l in lines if l]
+
+ if not indents:
+ return s
+
+ shift = indent - min(indents)
+
+ if shift < 0:
+ return '\n'.join(l[-shift:] for l in lines)
+ else:
+ prefix = ' ' * shift
+ return '\n'.join(prefix + l for l in lines)
+
+ def _compress_whitespace(self, s, replace=' ', newlines=True):
+ if newlines:
+ return self._whitespace_with_newline.sub(replace, s)
+ else:
+ return self._whitespace.sub(replace, s)
+
+ # --------------------------------------------------------------------------
+ # ---- DOM Tree Processing ----
+
+ def _process_table_cells(self, table):
+ """ Compile all the table cells.
+
+ Returns a list of rows. The rows may have different lengths because of
+ column spans.
+
+ """
+
+ rows = []
+
+ for i, tr in enumerate(table.find_all('tr')):
+ row = []
+
+ for c in tr.contents:
+ cell_type = getattr(c, 'name', None)
+
+ if cell_type not in ('td', 'th'):
+ continue
+
+ rowspan = int(c.attrs.get('rowspan', 1))
+ colspan = int(c.attrs.get('colspan', 1))
+ contents = self._process_children(c).strip()
+
+ if cell_type == 'th' and i > 0:
+ contents = self._inline('**', contents)
+
+ row.append(Cell(cell_type, rowspan, colspan, contents))
+
+ rows.append(row)
+
+ return rows
+
+ def _process_table(self, node):
+ rows = self._process_table_cells(node)
+
+ if not rows:
+ return ''
+
+ table_num_columns = max(sum(c.colspan for c in row) for row in rows)
+
+ normalized = []
+
+ for row in rows:
+ row_num_columns = sum(c.colspan for c in row)
+
+ if row_num_columns < table_num_columns:
+ cell_type = row[-1].type if row else 'td'
+ row.append(Cell(cell_type, 1, table_num_columns - row_num_columns, ''))
+
+ col_widths = [0] * table_num_columns
+ row_heights = [0] * len(rows)
+
+ for i, row in enumerate(rows):
+ j = 0
+ for cell in row:
+ current_w = sum(col_widths[j:j + cell.colspan])
+ required_w = max(len(l) for l in cell.contents.split('\n'))
+
+ if required_w > current_w:
+ additional = required_w - current_w
+ col_widths[j] += additional - (cell.colspan - 1) * (additional // cell.colspan)
+ for jj in range(j + 1, j + cell.colspan):
+ col_widths[jj] += (additional // cell.colspan)
+
+ current_h = row_heights[i]
+ required_h = len(cell.contents.split('\n'))
+
+ if required_h > current_h:
+ row_heights[i] = required_h
+
+ j += cell.colspan
+
+ row_sep = '+' + '+'.join('-' * (l + 2) for l in col_widths) + '+'
+ header_sep = '+' + '+'.join('=' * (l + 2) for l in col_widths) + '+'
+ lines = [row_sep]
+
+ for i, row in enumerate(rows):
+ for y in range(0, row_heights[i]):
+ line = []
+ j = 0
+ for c in row:
+ w = sum(n + 3 for n in col_widths[j:j+c.colspan]) - 2
+ h = row_heights[i]
+
+ line.append('| ')
+ cell_lines = c.contents.split('\n')
+ content = cell_lines[y] if y < len(cell_lines) else ''
+ line.append(content.ljust(w))
+
+ j += c.colspan
+
+ line.append('|')
+ lines.append(''.join(line))
+
+ if i == 0 and all(c.type == 'th' for c in row):
+ lines.append(header_sep)
+ else:
+ lines.append(row_sep)
+
+ return self._separate('\n'.join(lines))
+
+ def _process_children(self, node):
+ parts = []
+ is_newline = False
+
+ for c in node.contents:
+ part = self._process(c)
+
+ if is_newline:
+ part = part.lstrip()
+
+ if part:
+ parts.append(part)
+ is_newline = part.endswith('\n')
+
+ return ''.join(parts)
+
+ def _process_text(self, node):
+ return ''.join(node.strings)
+
+ def _process(self, node):
+ if isinstance(node, str):
+ return self._compress_whitespace(node)
+
+ simple_tags = {
+ 'b' : lambda s: self._inline('**', s),
+ 'strong' : lambda s: self._inline('**', s),
+ 'i' : lambda s: self._inline('*', s),
+ 'em' : lambda s: self._inline('*', s),
+ 'tt' : lambda s: self._inline('``', s),
+ 'code' : lambda s: self._inline('``', s),
+ 'h1' : lambda s: self._inline('**', s),
+ 'h2' : lambda s: self._inline('**', s),
+ 'h3' : lambda s: self._inline('**', s),
+ 'h4' : lambda s: self._inline('**', s),
+ 'h5' : lambda s: self._inline('**', s),
+ 'h6' : lambda s: self._inline('**', s),
+ 'sub' : lambda s: self._role('sub', s),
+ 'sup' : lambda s: self._role('sup', s),
+ 'hr' : lambda s: self._separate('') # Transitions not allowed
+ }
+
+ if node.name in simple_tags:
+ return simple_tags[node.name](self._process_text(node))
+
+ if node.name == 'p':
+ return self._separate(self._process_children(node).strip())
+
+ if node.name == 'pre':
+ return self._directive('parsed-literal', self._process_text(node))
+
+ if node.name == 'a':
+ if 'name' in node.attrs:
+ return self._separate('.. _' + node['name'] + ':')
+ elif 'href' in node.attrs:
+ target = node['href']
+ label = self._compress_whitespace(self._process_text(node).strip('\n'))
+
+ if target.startswith('#'):
+ return self._role('ref', target[1:], label)
+ elif target.startswith('@'):
+ return self._role('java:ref', target[1:], label)
+ else:
+ return self._hyperlink(target, label)
+
+ if node.name == 'ul':
+ items = [self._process(n) for n in node.find_all('li', recursive=False)]
+ return self._listing('*', items)
+
+ if node.name == 'ol':
+ items = [self._process(n) for n in node.find_all('li', recursive=False)]
+ return self._listing('#.', items)
+
+ if node.name == 'li':
+ s = self._process_children(node)
+ s = s.strip()
+
+ # If it's multiline clear the end to correcly support nested lists
+ if '\n' in s:
+ s = s + '\n\n'
+
+ return s
+
+ if node.name == 'table':
+ return self._process_table(node)
+
+ self._unknown_tags.add(node.name)
+
+ return self._process_children(node)
+
+ # --------------------------------------------------------------------------
+ # ---- HTML Preprocessing ----
+
+ def _preprocess_inline_javadoc_replace(self, tag, f, s):
+ parts = []
+
+ start = '{@' + tag
+ start_length = len(start)
+
+ i = s.find(start)
+ j = 0
+
+ while i != -1:
+ parts.append(s[j:i])
+
+ # Find a closing bracket such that the brackets are balanced between
+ # them. This is necessary since code examples containing { and } are
+ # commonly wrapped in {@code ...} tags
+
+ try:
+ j = s.find('}', i + start_length) + 1
+ while s.count('{', i, j) != s.count('}', i, j):
+ j = s.index('}', j) + 1
+ except ValueError:
+ raise ValueError('Unbalanced {} brackets in ' + tag + ' tag')
+
+ parts.append(f(s[i + start_length:j - 1].strip()))
+ i = s.find(start, j)
+
+ parts.append(s[j:])
+
+ return ''.join(parts)
+
+ def _preprocess_replace_javadoc_link(self, s):
+ s = self._compress_whitespace(s)
+
+ target = None
+ label = ''
+
+ if ' ' not in s:
+ target = s
+ else:
+ i = s.find(' ')
+
+ while s.count('(', 0, i) != s.count(')', 0, i):
+ i = s.find(' ', i + 1)
+
+ if i == -1:
+ i = len(s)
+ break
+
+ target = s[:i]
+ label = s[i:]
+
+ if target[0] == '#':
+ target = target[1:]
+
+ target = target.replace('#', '.').replace(' ', '').strip()
+
+ # Strip HTML tags from the target
+ target = self._html_tag.sub('', target)
+
+ label = label.strip()
+
+ return '<a href="@%s">%s</a>' % (target, label)
+
+ def _preprocess_close_anchor_tags(self, s):
+ # Add closing tags to all anchors so they are better handled by the parser
+ return self._preprocess_anchors.sub(r'<a name="\1"></a>', s)
+
+ def _preprocess_fix_entities(self, s):
+ return self._preprocess_entity.sub(r'&\1;\2', s)
+
+ def _preprocess(self, s_html):
+ to_tag = lambda t: lambda m: '<%s>%s</%s>' % (t, html_escape(m), t)
+ s_html = self._preprocess_inline_javadoc_replace('code', to_tag('code'), s_html)
+ s_html = self._preprocess_inline_javadoc_replace('literal', to_tag('span'), s_html)
+ s_html = self._preprocess_inline_javadoc_replace('docRoot', lambda m: '', s_html)
+ s_html = self._preprocess_inline_javadoc_replace('linkplain', self._preprocess_replace_javadoc_link, s_html)
+ s_html = self._preprocess_inline_javadoc_replace('link', self._preprocess_replace_javadoc_link, s_html)
+
+ # Make sure all anchor tags are closed
+ s_html = self._preprocess_close_anchor_tags(s_html)
+
+ # Fix up some entitities without closing ;
+ s_html = self._preprocess_fix_entities(s_html)
+
+ return s_html
+
+ # --------------------------------------------------------------------------
+ # ---- Conversion entry point ----
+
+ def convert(self, s_html):
+ if not isinstance(s_html, str):
+ s_html = str(s_html, 'utf8')
+
+ s_html = self._preprocess(s_html)
+
+ if not s_html.strip():
+ return ''
+
+ soup = BeautifulSoup(s_html, self._parser)
+ top = soup.html.body
+
+ result = self._process_children(top)
+
+ # Post processing
+ result = self._post_process_empty_lines.sub('', result)
+ result = self._post_process_compress_lines.sub('\n\n', result)
+ result = result.strip()
+
+ return result
--- /dev/null
+#
+# Copyright 2012-2015 Bronto Software, Inc. and contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from __future__ import unicode_literals
+from builtins import str
+
+import logging
+import re
+import sys
+
+class StringBuilder(list):
+ def build(self):
+ return str(self)
+
+ def __str__(self):
+ return ''.join(self)
+
+class Directive(object):
+
+ def __init__(self, type, argument=''):
+ self.type = type
+ self.argument = argument
+
+ self.options = []
+ self.content = []
+
+ def add_option(self, name, value=''):
+ self.options.append((name, value))
+
+ def add_content(self, o):
+ assert o is not None
+ self.content.append(o)
+
+ def build(self):
+ doc = Document()
+ doc.add_line('.. %s:: %s' % (self.type, self.argument))
+
+ for name, value in self.options:
+ doc.add_line(' :%s: %s\n' % (name, value))
+
+ content = Document()
+
+ for obj in self.content:
+ content.add_object(obj)
+
+ doc.clear()
+ for line in content.build().splitlines():
+ doc.add_line(' ' + line)
+ doc.clear()
+
+ return doc.build()
+
+class Document(object):
+ remove_trailing_whitespace_re = re.compile('[ \t]+$', re.MULTILINE)
+ collapse_empty_lines_re = re.compile('\n' + '{3,}', re.DOTALL)
+
+ def __init__(self):
+ self.content = []
+
+ def add_object(self, o):
+ assert o is not None
+
+ self.content.append(o)
+
+ def add(self, s):
+ self.add_object(s)
+
+ def add_line(self, s):
+ self.add(s)
+ self.add('\n')
+
+ def add_heading(self, s, t='-'):
+ self.add_line(s)
+ self.add_line(t * len(s))
+
+ def clear(self):
+ self.add('\n\n')
+
+ def build(self):
+ output = StringBuilder()
+
+ for obj in self.content:
+ if isinstance(obj, Directive):
+ output.append('\n\n')
+ output.append(obj.build())
+ output.append('\n\n')
+ elif isinstance(obj, Document):
+ output.append(obj.build())
+ else:
+ output.append(str(obj))
+
+ output.append('\n\n')
+
+ output = str(output)
+ output = self.remove_trailing_whitespace_re.sub('', output)
+ output = self.collapse_empty_lines_re.sub('\n\n', output)
+
+ return output
+
+def error(s, *args, **kwargs):
+ logging.error(s, *args, **kwargs)
+ sys.exit(1)
+
+def unexpected(s, *args, **kwargs):
+ logging.exception(s, *args, **kwargs)
+ sys.exit(1)
if read_the_docs_build:
subprocess.call('cd source; doxygen', shell=True)
- subprocess.call('javasphinx-apidoc --force -o source/java/ ../src/bindings/java/org/simgrid/msg', shell=True)
+ subprocess.call('source/_ext/javasphinx-apidoc --force -o source/java/ ../src/bindings/java/org/simgrid/msg', shell=True)
subprocess.call('rm source/java/packages.rst', shell=True)
# -- Project information -----------------------------------------------------