Skip to content

Commit

Permalink
[generator] Add --with-javadoc-xml=FILE support.
Browse files Browse the repository at this point in the history
Commit 69e1b80 added `tools/java-source-utils`, which can parse Java
source code and extract Javadoc comments into an XML file:

	$ java -jar "java-source-utils.jar" -v \
		$HOME/android-toolchain/sdk/platforms/android-29/android-stubs-src.jar \
		--output-javadoc android-javadoc.xml

What can we *do* with the generated `android-javadoc.xml`?

`android-javadoc.xml` contains parameter names, and thus can be used
with `class-parse --docspath`; see commit 806082f.

What we *really* want to do is make the Javadoc information *useful*
to consumers of the binding assembly.  This means that we want a
C# XML Documentation file for the binding assembly.

The most straightforward way to get a C# XML Documentation File is to
emit [C# XML Documentation Comments][0] into the binding source code!

Add a new `generator --with-javadoc-xml=FILE` option.  When specified,
`FILE` will be treated as an XML file containing the output from
`java-source-utils.jar --output-javadoc` (see 69e1b80)`, and all
`<javadoc/>` elements within the XML file will be associated with C#
types and members to emit, based on the `//@jni-signature` and
`//@name` attributes, as appropriate.

When the bindings are written to disk, the Javadoc comments will be
translated to C# XML Documentation comments, in a "best effort" basis.
(THIS WILL BE INCOMPLETE.)

To perform the Javadoc-to-C# XML Documentation comments conversion,
add a new Irony-based grammar to `Java.Interop.Tools.JavaSource.dll`,
in the new `Java.Interop.Tools.JavaSource.SourceJavadocToXmldocParser`
type, which parses the Javadoc content and translates to XML.

TODO:

  * Properties?

[0]: https://docs.microsoft.com/en-us/dotnet/csharp/codedoc
  • Loading branch information
jonpryor committed Oct 26, 2020
1 parent da74abd commit b7f5020
Show file tree
Hide file tree
Showing 31 changed files with 1,363 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<ProjectGuid>{5C0B3562-8DA0-4726-9762-75B9709ED6B7}</ProjectGuid>
<AssemblyTitle>Java.Interop.Tools.JavaSource</AssemblyTitle>
<Company>Microsoft Corporation</Company>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Irony.Ast;
using Irony.Parsing;

namespace Java.Interop.Tools.JavaSource {

static class IronyExtensions {

public static void MakeStarRule (this NonTerminal star, Grammar grammar, BnfTerm delimiter, BnfTerm of)
{
star.Rule = grammar.MakeStarRule (star, delimiter, of);
}

public static void MakeStarRule (this NonTerminal star, Grammar grammar, BnfTerm of)
{
star.Rule = grammar.MakeStarRule (star, of);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Xml.Linq;

using Irony.Ast;
using Irony.Parsing;

namespace Java.Interop.Tools.JavaSource {

sealed class JavadocInfo {
public readonly ICollection<XNode> Exceptions = new Collection<XNode> ();
public readonly ICollection<XNode> Extra = new Collection<XNode> ();
public readonly ICollection<XNode> Remarks = new Collection<XNode> ();
public readonly ICollection<XNode> Parameters = new Collection<XNode> ();
public readonly ICollection<XNode> Returns = new Collection<XNode> ();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;

using Irony.Ast;
using Irony.Parsing;

namespace Java.Interop.Tools.JavaSource {

public partial class SourceJavadocToXmldocGrammar {

// https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#javadoctags
public class BlockTagsBnfTerms {

internal BlockTagsBnfTerms ()
{
}

internal void CreateRules (SourceJavadocToXmldocGrammar grammar)
{
AllBlockTerms.Rule = AuthorDeclaration
| ApiSinceDeclaration
| DeprecatedDeclaration
| DeprecatedSinceDeclaration
| ExceptionDeclaration
| ParamDeclaration
| ReturnDeclaration
| SeeDeclaration
| SerialDataDeclaration
| SerialFieldDeclaration
| SinceDeclaration
| ThrowsDeclaration
| VersionDeclaration
;
BlockValue.Rule = Cdata | grammar.InlineTagsTerms.AllInlineTerms;

AuthorDeclaration.Rule = "@author" + BlockValue;
AuthorDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
// Ignore; not sure how best to convert to Xmldoc
FinishParse (context, parseNode);
};

ApiSinceDeclaration.Rule = "@apiSince" + BlockValue;
ApiSinceDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
var p = new XElement ("para", "Added in API level " + parseNode.ChildNodes [1].AstNode?.ToString ().Trim () + ".");
FinishParse (context, parseNode).Remarks.Add (p);
parseNode.AstNode = p;
};

DeprecatedDeclaration.Rule = "@deprecated" + BlockValue;
DeprecatedDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
var p = new XElement ("para", "This member is deprecated. " + AstNodeToXmlContent (parseNode.ChildNodes [1]));
FinishParse (context, parseNode).Remarks.Add (p);
parseNode.AstNode = p;
};

DeprecatedSinceDeclaration.Rule = "@deprecatedSince" + BlockValue;
DeprecatedSinceDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
var p = new XElement ("para", "This member was deprecated in API level " + AstNodeToXmlContent (parseNode.ChildNodes [1]) + ".");
FinishParse (context, parseNode).Remarks.Add (p);
parseNode.AstNode = p;
};

var nonSpaceTerm = new RegexBasedTerminal ("[^ ]", "[^ ]+") {
AstConfig = new AstNodeConfig {
NodeCreator = (context, parseNode) => parseNode.AstNode = parseNode.Token.Value,
},
};

ExceptionDeclaration.Rule = "@exception" + nonSpaceTerm + BlockValue;
ExceptionDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
// TODO: convert `nonSpaceTerm` into a proper CREF
var e = new XElement ("exception",
new XAttribute ("cref", AstNodeToXmlContent (parseNode.ChildNodes [1])),
AstNodeToXmlContent (parseNode.ChildNodes [2]));
FinishParse (context, parseNode).Exceptions.Add (e);
parseNode.AstNode = e;
};

ParamDeclaration.Rule = "@param" + nonSpaceTerm + BlockValue;
ParamDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
var p = new XElement ("param",
new XAttribute ("name", parseNode.ChildNodes [1].AstNode?.ToString ()),
parseNode.ChildNodes [2].AstNode.ToString ().Trim ());
FinishParse (context, parseNode).Parameters.Add (p);
parseNode.AstNode = p;
};

ReturnDeclaration.Rule = "@return" + BlockValue;
ReturnDeclaration.Flags = TermFlags.IsMultiline;
ReturnDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
var r = new XElement ("returns", parseNode.ChildNodes [1].AstNode.ToString ());
FinishParse (context, parseNode).Returns.Add (r);
parseNode.AstNode = r;
};

SeeDeclaration.Rule = "@see" + BlockValue;
SeeDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
// TODO: @see supports multiple forms; see: https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#see
var e = new XElement ("altmember",
new XAttribute ("cref", parseNode.ChildNodes [1].AstNode?.ToString ().Trim ()));
FinishParse (context, parseNode).Extra.Add (e);
parseNode.AstNode = e;
};

SinceDeclaration.Rule = "@since" + BlockValue;
SinceDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
var p = new XElement ("para", "Added in " + parseNode.ChildNodes [1].AstNode?.ToString () + ".");
FinishParse (context, parseNode).Remarks.Add (p);
parseNode.AstNode = p;
};

ThrowsDeclaration.Rule = "@throws" + nonSpaceTerm + BlockValue;
ThrowsDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
// TODO: convert `nonSpaceTerm` into a proper CREF
var e = new XElement ("exception",
new XAttribute ("cref", parseNode.ChildNodes [1].AstNode?.ToString ()),
parseNode.ChildNodes [2].AstNode);
FinishParse (context, parseNode).Exceptions.Add (e);
parseNode.AstNode = e;
};

// Ignore serialization informatino
SerialDeclaration.Rule = "@serial" + BlockValue;
SerialDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
FinishParse (context, parseNode);
};

SerialDataDeclaration.Rule = "@serialData" + BlockValue;
SerialDataDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
FinishParse (context, parseNode);
};

SerialFieldDeclaration.Rule = "@serialField" + BlockValue;
SerialFieldDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
FinishParse (context, parseNode);
};

// Ignore Version
VersionDeclaration.Rule = "@version" + BlockValue;
VersionDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
FinishParse (context, parseNode);
};
}

public readonly NonTerminal AllBlockTerms = new NonTerminal (nameof (AllBlockTerms), ConcatChildNodes);

public readonly Terminal Cdata = new CharacterDataTerminal ("#CDATA", preserveLeadingWhitespace: false);
/*
public readonly Terminal Cdata = new RegexBasedTerminal (nameof (BlockValue), "[^<]*") {
AstConfig = new AstNodeConfig {
NodeCreator = (context, parseNode) => parseNode.AstNode = parseNode.Token.Value.ToString (),
},
};
*/

public readonly NonTerminal BlockValue = new NonTerminal (nameof (BlockValue), ConcatChildNodes);
public readonly NonTerminal AuthorDeclaration = new NonTerminal (nameof (AuthorDeclaration));
public readonly NonTerminal ApiSinceDeclaration = new NonTerminal (nameof (ApiSinceDeclaration));
public readonly NonTerminal DeprecatedDeclaration = new NonTerminal (nameof (DeprecatedDeclaration));
public readonly NonTerminal DeprecatedSinceDeclaration = new NonTerminal (nameof (DeprecatedSinceDeclaration));
public readonly NonTerminal ExceptionDeclaration = new NonTerminal (nameof (ExceptionDeclaration));
public readonly NonTerminal ParamDeclaration = new NonTerminal (nameof (ParamDeclaration));
public readonly NonTerminal ReturnDeclaration = new NonTerminal (nameof (ReturnDeclaration));
public readonly NonTerminal SeeDeclaration = new NonTerminal (nameof (SeeDeclaration));
public readonly NonTerminal SerialDeclaration = new NonTerminal (nameof (SerialDeclaration));
public readonly NonTerminal SerialDataDeclaration = new NonTerminal (nameof (SerialDataDeclaration));
public readonly NonTerminal SerialFieldDeclaration = new NonTerminal (nameof (SerialFieldDeclaration));
public readonly NonTerminal SinceDeclaration = new NonTerminal (nameof (SinceDeclaration));
public readonly NonTerminal ThrowsDeclaration = new NonTerminal (nameof (ThrowsDeclaration));
public readonly NonTerminal VersionDeclaration = new NonTerminal (nameof (VersionDeclaration));
}
}
}
Loading

0 comments on commit b7f5020

Please sign in to comment.