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 30, 2020
1 parent b588ef5 commit 1ad9e10
Show file tree
Hide file tree
Showing 32 changed files with 1,485 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,28 @@
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 MakePlusRule (this NonTerminal star, Grammar grammar, BnfTerm delimiter)
{
star.Rule = grammar.MakePlusRule (star, delimiter);
}

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,31 @@
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> ();

public override string ToString ()
{
return new XElement ("Javadoc",
new XElement (nameof (Parameters), Parameters),
new XElement (nameof (Remarks), Remarks),
new XElement (nameof (Returns), Returns),
new XElement (nameof (Exceptions), Exceptions),
new XElement (nameof (Extra), Extra))
.ToString ();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
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;
BlockValues.MakePlusRule (grammar, BlockValue);

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 ", AstNodeToXmlContent (parseNode.ChildNodes [1]), ".");
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", string.Join ("", 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", string.Join ("", AstNodeToXmlContent (parseNode.ChildNodes [1]))),
AstNodeToXmlContent (parseNode.ChildNodes [2]));
FinishParse (context, parseNode).Parameters.Add (p);
parseNode.AstNode = p;
};

ReturnDeclaration.Rule = "@return" + BlockValues;
// ReturnDeclaration.Flags = TermFlags.IsMultiline;
ReturnDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
var r = new XElement ("returns", parseNode.ChildNodes.Skip (1).Select (c => AstNodeToXmlContent (c)));
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", string.Join ("", AstNodeToXmlContent (parseNode.ChildNodes [1]))));
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 ", AstNodeToXmlContent (parseNode.ChildNodes [1]), ".");
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", string.Join ("", AstNodeToXmlContent (parseNode.ChildNodes [1]))),
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: true);
/*
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 BlockValues = new NonTerminal (nameof (BlockValues), 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 1ad9e10

Please sign in to comment.