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
+
+ # Use that script without installing javasphinx: javasphinx-apidoc --force -o source/java/ ../src/bindings/java/org/simgrid/msg
+ PYTHONPATH=${PYTHONPATH}:source/_ext/javasphinx python3 - --force -o source/java/ ../src/bindings/java/org/simgrid/msg <<EOF
+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())
+EOF
+
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
sphinx>=1.8.0
sphinx_rtd_theme
sphinx_tabs
-javasphinx
--- /dev/null
+*.pyc
+dist/
+*.egg-info/
+.vscode
+.DS_Store
\ No newline at end of file
--- /dev/null
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
--- /dev/null
+include README.rst
--- /dev/null
+
+# javasphinx
+
+[![Documentation Status](https://readthedocs.org/projects/bronto-javasphinx/badge/?version=latest)](http://bronto-javasphinx.readthedocs.io/en/latest/?badge=latest)
+
+**This project is no longer maintained and should be used for historical purposes only.**
+
+javasphinx is an extension to the Sphinx documentation system which adds support for documenting Java projects. It includes a Java domain for writing documentation manually and a javasphinx-apidoc utility which will automatically generate API documentation from existing Javadoc markup.
+
+javasphinx is available in the Python Package Index (PyPi) under the name _javasphinx_ and can be installed using tools such as `pip` or `easy_install`.
+
+Documentation for javasphinx is available at http://bronto-javasphinx.readthedocs.io
--- /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.
+#
+
+project = 'javasphinx'
+version = '0.9.15'
+release = version
+
+extensions = ['javasphinx']
+
+master_doc = 'index'
+copyright = u'2012-2017, Bronto Software Inc. and contributors'
+primary_domain = 'rst'
--- /dev/null
+
+#######################
+javasphinx User's Guide
+#######################
+
+Welcome to the javasphinx user's guide.
+
+Introduction
+============
+
+javasphinx is a Sphinx_ extension that provides a Sphinx domain_ for documenting
+Java projects and a ``javasphinx-apidoc`` command line tool for automatically
+generating API documentation from existing Java source code and Javadoc
+documentation.
+
+.. _Sphinx: http://sphinx-doc.org
+.. _domain: http://sphinx-doc.org/domains.html
+
+Installing
+==========
+
+javasphinx is available in the Python Package Index (PyPi) and can be installed
+using tools such as ``pip`` or ``easy_install``,
+
+.. code-block:: sh
+
+ $ pip install javasphinx
+
+or,
+
+.. code-block:: sh
+
+ $ easy_install -U javasphinx
+
+Configuration
+=============
+
+To enable javasphinx for your existing Sphinx configuration add ``'javasphinx'``
+to the list of extensions in your conf.py file. javasphinx can be configured to
+cross link to external sources of documentation using the ``javadoc_url_map``
+option,
+
+.. code-block:: python
+
+ javadoc_url_map = {
+ 'com.netflix.curator' : ('http://netflix.github.com/curator/doc', 'javadoc'),
+ 'org.springframework' : ('http://static.springsource.org/spring/docs/3.1.x/javadoc-api/', 'javadoc'),
+ 'org.springframework.data.redis' : ('http://static.springsource.org/spring-data/data-redis/docs/current/api/', 'javadoc')
+ }
+
+Each key in the map should be a Java package. Each value is a tuple of the form
+``(base_url, doc_type)`` where ``base_url`` is the base URL of the documentation
+source, and ``doc_type`` is one of,
+
+``javadoc``
+ For documentation generated by the Javadoc tool *before* version 8.
+
+``javadoc8``
+ For documentation generated by the Javadoc tool after version 8. This is
+ required due to changes in how method anchors are generated (see JDK-8144118_).
+
+``sphinx``
+ For external documentation generated by javasphinx.
+
+When comparing referenced types to the list of available packages the longest
+match will be used. Entries for ``java``, ``javax``, ``org.xml``, and
+``org.w3c`` packages pointing to http://docs.oracle.com/javase/8/docs/api are
+included automatically and do not need to be defined explicitly.
+
+.. _JDK-8144118: https://bugs.openjdk.java.net/browse/JDK-8144118
+
+Java domain
+===========
+
+Directives
+----------
+
+The Java domain uses the name **java** and provides the following directives,
+
+.. rst:directive:: .. java:type:: type-signature
+
+ Describe a Java type. The signature can represent either a class, interface,
+ enum or annotation declaration.
+
+ Use the ``param`` field to document type parameters.
+
+ Example,
+
+ .. code-block:: rst
+
+ .. java:type:: public interface List<E> extends Collection<E>, Iterable<E>
+
+ An ordered collection (also known as a *sequence*)
+
+ :param E: type of item stored by the list
+
+ produces,
+
+ .. java:type:: public interface List<E> extends Collection<E>, Iterable<E>
+
+ An ordered collection (also known as a *sequence*)
+
+ :param E: type of item stored by the list
+
+.. rst:directive:: .. java:field:: field-signature
+
+ Describe a Java field.
+
+.. rst:directive:: .. java:method:: method-signature
+
+ Describe a Java method.
+
+ Use the ``param`` field to document parameters.
+
+ Use the ``throws`` field to document exceptions thrown by the method.
+
+ Use the ``return`` field to document the return type
+
+.. rst:directive:: .. java:constructor:: constructor-signature
+
+ Describe a Java constructor.
+
+ Use the ``param`` field to document parameters.
+
+ Use the ``throws`` field to document exceptions thrown by the constructor.
+
+.. rst:directive:: .. java:package:: package
+
+ Provide package-level documentation and also sets the active package for the
+ type, method, field, constructors, and references that follow.
+
+ Use the ``:noindex:`` option if the directive is only being used to specify
+ the active package. Only one directive for a given package should exclude
+ ``:noindex:``.
+
+.. rst:directive:: .. java:import:: package type
+
+ Declare the given type as being provided by the given package. This
+ information helps javasphinx create cross references for types in type,
+ method, and field declarations. It also allows explicit cross references
+ (using the ``java:ref`` role) to exclude the package qualification.
+
+The method, construct, field, and type directives all accept the following
+standard options,
+
+.. describe:: package
+
+ Specify the package the declaration is within. Can be used instead of, or to
+ override, a ``java:package`` directive.
+
+.. describe:: outertype
+
+ Specify the class/interface the documented object is contained within. This
+ option should be provided for any constructor, method, or field directive
+ that isn't nested within a corresponding type directive.
+
+Roles
+-----
+
+The following roles are provided,
+
+.. rst:role:: java:ref
+
+ This role can be used to create a cross reference to any object type within
+ the Java domain. Aliases for this role include ``java:meth``, ``java:type``,
+ ``java:field``, ``java:package``, and ``java:construct``.
+
+ An explicit title can be provided by using the standard ``title <reference>``
+ syntax.
+
+.. rst:role:: java:extdoc
+
+ This role can be used to explicitly link to an externally documented
+ type. The reference must be fully qualified and supports an explicit title
+ using the ``title <reference>`` syntax.
+
+ The ``java:ref`` role will also create external references as a fall-back if
+ it can't find a matching local declaration so using this role is not strictly
+ necessary.
+
+javasphinx-apidoc
+=================
+
+The ``javasphinx-apidoc`` tool is the counterpoint to the ``sphinx-apidoc`` tool
+within the Java domain. It can be used to generate reST source from existing
+Java source code which has been marked up with Javadoc-style comments. The
+generated reST is then processed alongside hand-written documentation by Sphinx.
+
+At minimum a source and destination directory must be provided. The input
+directory will be scanned for .java files and documentation will be generated
+for all non-private types and members. A separate output file will be generated
+for each type (including inner classes). Each file is put within a directory
+corresponding to its package (with periods replaced by directory separators) and
+with the basename of the file deriving from the type name. Inner types are
+placed in files with a basename using a hyphen to separate inner and outer
+types, e.g. ``OuterType-InnerType.rst``.
+
+By default ``javasphinx-apidoc`` will not override existing files. Two options
+can change this behavior,
+
+.. option:: -f, --force
+
+ All existing output files will be rewritten. If a cache directory is
+ specified it will be rebuilt.
+
+.. option:: -u, --update
+
+ Updated source files will have their corresponding output files
+ updated. Unchanged files will be left alone. Most projects will want to use
+ this option.
+
+For larger projects it is recommended to use a cache directory. This can speed
+up subsequent runs by an order of magnitude or more. Specify a directory to
+store cached output using the :option:`-c` option,
+
+.. option:: -c, --cache-dir
+
+ Specify a directory to cache intermediate documentation representations. This
+ directory will be created if it does not already exist.
--- /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 _
+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=_('Parameters'),
+ names=('param', 'parameter', 'arg', 'argument'),
+ typerolename='type', typenames=('type',)),
+ Field('returnvalue', label=_('Returns'), has_arg=False,
+ names=('returns', 'return')),
+ GroupedField('throws', names=('throws',), label=_('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=_('Parameters'),
+ names=('param', 'parameter', 'arg', 'argument'),
+ typerolename='type', typenames=('type',)),
+ GroupedField('throws', names=('throws',), label=_('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=_('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(_('package'), 'package', 'ref'),
+ 'type': ObjType(_('type'), 'type', 'ref'),
+ 'field': ObjType(_('field'), 'field', 'ref'),
+ 'constructor': ObjType(_('constructor'), 'construct', 'ref'),
+ 'method': ObjType(_('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)
--- /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 setuptools import setup
+
+setup(
+ name = "javasphinx",
+ packages = ["javasphinx"],
+ version = "0.9.15",
+ author = "Chris Thunes",
+ author_email = "cthunes@brewtab.com",
+ url = "http://github.com/bronto/javasphinx",
+ description = "Sphinx extension for documenting Java projects",
+ license = "Apache 2.0",
+ classifiers = [
+ "Programming Language :: Python",
+ "Development Status :: 4 - Beta",
+ "Operating System :: OS Independent",
+ "License :: OSI Approved :: Apache Software License",
+ "Intended Audience :: Developers",
+ "Topic :: Software Development :: Libraries"
+ ],
+ install_requires=[
+ "javalang>=0.10.1",
+ "lxml",
+ "beautifulsoup4",
+ "future",
+ "docutils",
+ "sphinx"
+ ],
+ entry_points={
+ 'console_scripts': [
+ 'javasphinx-apidoc = javasphinx.apidoc:main'
+ ]
+ },
+ long_description = """\
+==========
+javasphinx
+==========
+
+javasphinx is an extension to the Sphinx documentation system which adds support
+for documenting Java projects. It includes a Java domain for writing
+documentation manually and a javasphinx-apidoc utility which will automatically
+generate API documentation from existing Javadoc markup.
+"""
+)