Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit 151b03e

Browse files
authored
[Java.Interop.Tools.JavaSource] Improve <code> parsing (#1125)
Fixes: #1071 The latest API docs update contained a couple dozen parsing issues due to `<code/>` parsing, including: * Closing element doesn't match opening element: `<code>null</null>` * Content including `@`: `<code>android:label="@string/resolve_title"</code>` * Closing element is actually an opening element: `<code>Activity.RESULT_OK<code>` * Improper element nesting: `<code><pre><p>content</code></pre></p>` * Use of attributes: `<code class=prettyprint>content<code>` Fix this by replacing `CodeElementDeclaration` to use a new `CodeElementContentTerm` terminal, which is a "greedy regex" which grabs `<code` until one of: * `</code>` * `</null>` * `<code>` The result of `CodeElementDeclaration` is the end of the `<code>` element until the beginning of one of the above terminators: * `<code>null</null>` becomes `<c>null</c>` * `<code>android:label="@string/resolve_title"</code>` becomes `<c>android:label="@string/resolve_title"</c>`.` * `<code>Activity.RESULT_OK<code>` becomes `<c>Activity.RESULT_OK</c>`. * `<code><pre><p>content</code></pre></p>` becomes the mess `<c>&lt;pre&gt;&lt;p&gt;some content</c>&lt;/pre&gt;&lt;/p&gt;` 🤷‍♂️ * `<code class=prettyprint>content<code>` becomes `<c>content</c>`.`
1 parent 6a9f5cb commit 151b03e

3 files changed

Lines changed: 72 additions & 7 deletions

File tree

‎src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.HtmlBnfTerms.cs‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,17 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar)
131131
}
132132
};
133133

134-
CodeElementDeclaration.Rule=CreateStartElement("code",grammar)+InlineDeclarations+CreateEndElement("code",grammar);
134+
CodeElementDeclaration.Rule=CodeElementContentTerm;
135135
CodeElementDeclaration.AstConfig.NodeCreator=(context,parseNode)=>{
136-
vartarget=parseNode.ChildNodes[1].AstNode;
136+
// Parse the entire <code> element captured in the token
137+
varcodeElementText=parseNode.ChildNodes[0].Token.Text;
138+
intstartIndex=codeElementText.IndexOf('>');
139+
intstopIndex=codeElementText.LastIndexOf('<');
140+
if(startIndex==-1||stopIndex==-1){
141+
parseNode.AstNode=newXText(codeElementText);
142+
return;
143+
}
144+
vartarget=codeElementText.Substring(startIndex+1,stopIndex-startIndex-1);
137145
parseNode.AstNode=newXElement("c",target);
138146
};
139147
}
@@ -232,6 +240,12 @@ static string GetChildNodesAsString (ParseTreeNode parseNode)
232240
publicreadonlyNonTerminalInlineHyperLinkDeclaration=newNonTerminal(nameof(InlineHyperLinkDeclaration),ConcatChildNodes);
233241
publicreadonlyNonTerminalCodeElementDeclaration=newNonTerminal(nameof(CodeElementDeclaration),ConcatChildNodes);
234242

243+
publicreadonlyTerminalCodeElementContentTerm=newRegexBasedTerminal("<code>",$@"(?i)<code\s*[^>]*>(.|\s)*?(<\/code>|<\/null>|<code>)"){
244+
AstConfig=newAstNodeConfig{
245+
NodeCreator=(context,parseNode)=>parseNode.AstNode="",
246+
},
247+
};
248+
235249
publicreadonlyTerminalInlineHyperLinkOpenTerm=newRegexBasedTerminal("<a attr=",@"(?i)<a\s*.*="){
236250
AstConfig=newAstNodeConfig{
237251
NodeCreator=(context,parseNode)=>parseNode.AstNode="",

‎tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocGrammar.HtmlBnfTermsTests.cs‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,38 @@ public void CodeElementDeclaration ()
101101
varr=p.Parse("<code>input.position()</code>");
102102
Assert.IsFalse(r.HasErrors(),DumpMessages(r,p));
103103
Assert.AreEqual("<c>input.position()</c>",r.Root.AstNode.ToString());
104-
}
105104

105+
r=p.Parse("<code>null</null>");
106+
Assert.IsFalse(r.HasErrors(),DumpMessages(r,p));
107+
Assert.AreEqual("<c>null</c>",r.Root.AstNode.ToString());
108+
109+
r=p.Parse("<code>android:label=\"@string/resolve_title\"</code>");
110+
Assert.IsFalse(r.HasErrors(),DumpMessages(r,p));
111+
Assert.AreEqual("<c>android:label=\"@string/resolve_title\"</c>",r.Root.AstNode.ToString());
112+
113+
r=p.Parse("<code>Activity.RESULT_OK<code>");
114+
Assert.IsFalse(r.HasErrors(),DumpMessages(r,p));
115+
Assert.AreEqual("<c>Activity.RESULT_OK</c>",r.Root.AstNode.ToString());
116+
117+
r=p.Parse("<code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code>");
118+
Assert.IsFalse(r.HasErrors(),DumpMessages(r,p));
119+
Assert.AreEqual("<c>format.setString(MediaFormat.KEY_FRAME_RATE, null)</c>",r.Root.AstNode.ToString());
120+
121+
r=p.Parse(@"<code>
122+
<p> [ 0, 0, 0, 0, 0 ]
123+
<p> [ 0, 0, 0, 0, 0 ]
124+
<p> [ 0, 0, 1, 0, 0 ]
125+
<p> [ 0, 0, 0, 0, 0 ]
126+
<p> [ 0, 0, 0, 0, 0 ]
127+
</code>");
128+
Assert.IsFalse(r.HasErrors(),DumpMessages(r,p));
129+
Assert.AreEqual(@"<c>
130+
&lt;p&gt; [ 0, 0, 0, 0, 0 ]
131+
&lt;p&gt; [ 0, 0, 0, 0, 0 ]
132+
&lt;p&gt; [ 0, 0, 1, 0, 0 ]
133+
&lt;p&gt; [ 0, 0, 0, 0, 0 ]
134+
&lt;p&gt; [ 0, 0, 0, 0, 0 ]
135+
</c>",r.Root.AstNode.ToString());
136+
}
106137
}
107138
}

‎tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocParserTests.cs‎

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,36 @@ more description here.</para>
144144
</member>",
145145
},
146146
newParseResult{
147-
Javadoc="Something {@link #method}: description, \"<code>declaration</code>\" or \"<code>another declaration</code>\".\n\n@apiSince 1\n",
147+
Javadoc="Something {@link #method}: description, \"<code>declaration</code>\" or <code><pre><p>some content</code></pre></p>.\n\n@apiSince 1\n",
148148
FullXml=@"<member>
149-
<summary>Something <c>#method</c>: description, ""<c>declaration</c>"" or ""<c>another declaration</c>"".</summary>
149+
<summary>Something <c>#method</c>: description, ""<c>declaration</c>"" or <c>&lt;pre&gt;&lt;p&gt;some content</c>&lt;/pre&gt;&lt;/p&gt;.</summary>
150150
<remarks>
151-
<para>Something <c>#method</c>: description, ""<c>declaration</c>"" or ""<c>another declaration</c>"".</para>
151+
<para>Something <c>#method</c>: description, ""<c>declaration</c>"" or <c>&lt;pre&gt;&lt;p&gt;some content</c>&lt;/pre&gt;&lt;/p&gt;.</para>
152152
<para>Added in API level 1.</para>
153153
</remarks>
154154
</member>",
155155
IntelliSenseXml=@"<member>
156-
<summary>Something <c>#method</c>: description, ""<c>declaration</c>"" or ""<c>another declaration</c>"".</summary>
156+
<summary>Something <c>#method</c>: description, ""<c>declaration</c>"" or <c>&lt;pre&gt;&lt;p&gt;some content</c>&lt;/pre&gt;&lt;/p&gt;.</summary>
157+
</member>",
158+
},
159+
newParseResult{
160+
Javadoc=@"The result code will be <code>Activity.RESULT_OK<code> for success,
161+
or one of these errors:
162+
<code>RESULT_ERROR_GENERIC_FAILURE</code>",
163+
FullXml=@"<member>
164+
<summary>The result code will be <c>Activity.RESULT_OK</c> for success,
165+
or one of these errors:
166+
<c>RESULT_ERROR_GENERIC_FAILURE</c></summary>
167+
<remarks>
168+
<para>The result code will be <c>Activity.RESULT_OK</c> for success,
169+
or one of these errors:
170+
<c>RESULT_ERROR_GENERIC_FAILURE</c></para>
171+
</remarks>
172+
</member>",
173+
IntelliSenseXml=@"<member>
174+
<summary>The result code will be <c>Activity.RESULT_OK</c> for success,
175+
or one of these errors:
176+
<c>RESULT_ERROR_GENERIC_FAILURE</c></summary>
157177
</member>",
158178
},
159179
newParseResult{

0 commit comments

Comments
 (0)