Skip to content

Commit a7e1d03

Browse files
committed
fix ArrayIndexOutOfBoundsException thrown while parsing devhelp2 #41
- add new SAXParser to handle the new gtk-doc devhelp2 format where references are to a single file and do not include links - rename HTMLSaxParser to HTMLSAXParserOld and move to new file - create new file HTMLSAXParser which parses the new format - support for devhelp2 files only, if no links exist for devhelp file, bail on parsing
1 parent d0096d2 commit a7e1d03

File tree

4 files changed

+610
-275
lines changed

4 files changed

+610
-275
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2022 Red Hat Inc. and others.
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Red Hat Inc. - Initial implementation
12+
*******************************************************************************/
13+
package org.eclipse.linuxtools.internal.cdt.libhover.devhelp;
14+
15+
import java.util.TreeMap;
16+
17+
import org.eclipse.linuxtools.cdt.libhover.FunctionInfo;
18+
19+
interface DevHelpSAXParser {
20+
public TreeMap<String, FunctionInfo> getFunctionInfos();
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2022 Red Hat Inc. and others.
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Red Hat Inc. - Initial implementation
12+
*******************************************************************************/
13+
package org.eclipse.linuxtools.internal.cdt.libhover.devhelp;
14+
15+
import java.util.Map;
16+
import java.util.TreeMap;
17+
18+
import org.apache.xerces.parsers.AbstractSAXParser;
19+
import org.apache.xerces.xni.Augmentations;
20+
import org.apache.xerces.xni.QName;
21+
import org.apache.xerces.xni.XMLAttributes;
22+
import org.apache.xerces.xni.XMLString;
23+
import org.cyberneko.html.HTMLConfiguration;
24+
import org.eclipse.linuxtools.cdt.libhover.FunctionInfo;
25+
import org.eclipse.linuxtools.internal.cdt.libhover.devhelp.preferences.FuncFoundSaxException;
26+
27+
class HTMLSAXParser extends AbstractSAXParser implements DevHelpSAXParser {
28+
29+
private boolean begin;
30+
private boolean returnType;
31+
private boolean protoStart;
32+
private boolean parmStart;
33+
private boolean descStart;
34+
private boolean rowIgnore;
35+
private boolean srcLink;
36+
private boolean valid = true;
37+
private Map<String, String> funcs;
38+
private String returnValue;
39+
private String funcName;
40+
private String rowTag;
41+
private StringBuilder prototype = new StringBuilder();
42+
private StringBuilder description = new StringBuilder();
43+
private int divCounter;
44+
private int rowItemCount;
45+
private TreeMap<String, FunctionInfo> infos = new TreeMap<>();
46+
47+
public HTMLSAXParser(Map<String, String> funcs) {
48+
super(new HTMLConfiguration());
49+
this.funcs = funcs;
50+
}
51+
52+
@Override
53+
public void startElement(QName name, XMLAttributes a, Augmentations aug) {
54+
// look for a tag that matches one of the functions we are trying to find
55+
if ("A".equals(name.rawname)) { //$NON-NLS-1$
56+
String classValue = a.getValue("class"); //$NON-NLS-1$
57+
if (classValue != null) {
58+
if (classValue.equals("anchor")) { //$NON-NLS-1$
59+
String href = a.getValue("href"); //$NON-NLS-1$
60+
if (href != null) {
61+
if (href.equals("#declaration")) { //$NON-NLS-1$
62+
String mapName = funcs.get("name"); //$NON-NLS-1$
63+
if (mapName != null) {
64+
// We have found one of the functions we are looking for.
65+
// Register the name for later and allow function parsing to begin.
66+
funcName = mapName.trim();
67+
if (funcName.endsWith("()")) { //$NON-NLS-1$
68+
// Remove () at end of function name and remove all space chars which might be
69+
// non-breaking space chars which unfortunately do not get caught by the trim() method.
70+
funcName = this.funcName.replaceAll("\\(\\)", "").replaceAll("\\p{javaSpaceChar}",""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
71+
}
72+
begin = true;
73+
}
74+
} else if (href.equals("#description")) { //$NON-NLS-1$
75+
descStart = true;
76+
} else if (href.equals("#return-value")) { //$NON-NLS-1$
77+
descStart = true;
78+
} else if (href.equals("#parameters")) { //$NON-NLS-1$
79+
description.append("<br><br><h4>Parameters:</h4>"); //$NON-NLS-1$
80+
descStart = true;
81+
}
82+
}
83+
} else if (classValue.equals("srclink")) { //$NON-NLS-1$
84+
srcLink = true;
85+
}
86+
}
87+
}
88+
if (begin) {
89+
if ("DIV".equals(name.rawname)) { //$NON-NLS-1$
90+
++divCounter;
91+
}
92+
if (!descStart) {
93+
if ("SPAN".equals(name.rawname)) { //$NON-NLS-1$
94+
String type = a.getValue("class"); //$NON-NLS-1$
95+
if (returnValue == null && type != null &&
96+
type.equals("n")) { //$NON-NLS-1$
97+
returnType = true;
98+
}
99+
}
100+
}
101+
if (protoStart) {
102+
if ("P".equals(name.rawname)) { //$NON-NLS-1$
103+
protoStart = false;
104+
descStart = true;
105+
description.append("<p>"); //$NON-NLS-1$
106+
}
107+
} else if (descStart) {
108+
if ("P".equals(name.rawname)) { //$NON-NLS-1$
109+
description.append("<p>"); //$NON-NLS-1$
110+
} else if ("TABLE".equals(name.rawname)) { //$NON-NLS-1$
111+
description.append("<dl>"); //$NON-NLS-1$
112+
} else if ("TR".equals(name.rawname)) { //$NON-NLS-1$
113+
rowItemCount = 0;
114+
} else if ("TD".equals(name.rawname)) { //$NON-NLS-1$
115+
String type = a.getValue("class"); //$NON-NLS-1$
116+
if (type != null && type.equals("listing_lines")) { //$NON-NLS-1$
117+
rowIgnore = true;
118+
} else {
119+
rowIgnore = false;
120+
if (rowItemCount++ == 0) {
121+
rowTag = "<dt>"; //$NON-NLS-1$
122+
} else {
123+
rowTag = "<dd>"; //$NON-NLS-1$
124+
}
125+
description.append(rowTag);
126+
}
127+
} else if ("H4".equals(name.rawname)) { //$NON-NLS-1$
128+
// description.append("<br>"); //$NON-NLS-1$
129+
}
130+
}
131+
}
132+
}
133+
134+
@Override
135+
public void endElement(QName name, Augmentations aug) {
136+
if (srcLink) {
137+
if ("A".equals(name.rawname)) { //$NON-NLS-1$
138+
srcLink = false;
139+
}
140+
} else if (begin) {
141+
if ("DIV".equals(name.rawname)) { //$NON-NLS-1$
142+
--divCounter;
143+
if (divCounter <= 0) {
144+
// We have finished parsing the current area, reset all flags
145+
descStart = false;
146+
parmStart = false;
147+
protoStart = false;
148+
}
149+
} else if ("SECTION".equals(name.rawname)) { //$NON-NLS-1$
150+
// We have finished parsing the function
151+
// If valid, create and save the function info
152+
if (valid && returnValue != null &&
153+
!returnValue.startsWith("#") && //$NON-NLS-1$
154+
!returnValue.startsWith("typedef ")) { //$NON-NLS-1$
155+
FunctionInfo info = new FunctionInfo(funcName);
156+
info.setReturnType(returnValue);
157+
info.setPrototype(prototype.toString());
158+
info.setDescription(description.toString());
159+
infos.put(funcName, info);
160+
throw new FuncFoundSaxException(); // indicate we are done and stop parser
161+
}
162+
}
163+
if (descStart) {
164+
if ("P".equals(name.rawname)) {//$NON-NLS-1$
165+
description.append("</p>"); //$NON-NLS-1$
166+
} else if ("TABLE".equals(name.rawname)) { //$NON-NLS-1$
167+
description.append("</dl>"); //$NON-NLS-1$
168+
} else if ("TR".equals(name.rawname)) { //$NON-NLS-1$
169+
rowItemCount = 0;
170+
} else if ("TD".equals(name.rawname)) { //$NON-NLS-1$
171+
if (!rowIgnore) {
172+
if (rowTag != null && rowTag.equals("<dt>")) {//$NON-NLS-1$
173+
description.append("</dt>"); //$NON-NLS-1$
174+
} else {
175+
description.append("</dd>"); //$NON-NLS-1$
176+
}
177+
}
178+
rowIgnore = false;
179+
}
180+
}
181+
}
182+
}
183+
184+
@Override
185+
public void characters(XMLString data, Augmentations aug) {
186+
if (begin && !srcLink) {
187+
if (returnType) {
188+
returnValue = ""; //$NON-NLS-1$
189+
String tmp = data.toString().trim();
190+
boolean completed = false;
191+
if (tmp.endsWith(");")) { //$NON-NLS-1$
192+
completed = true;
193+
tmp = tmp.substring(0, tmp.length() - 2);
194+
}
195+
String[] tokens = tmp.split("\\s+"); //$NON-NLS-1$
196+
String separator = ""; //$NON-NLS-1$
197+
protoStart = true;
198+
for (int i = 0; i < tokens.length; ++i) {
199+
String token = tokens[i];
200+
if (!token.equals(funcName)) {
201+
returnValue += separator + token;
202+
separator = " "; //$NON-NLS-1$
203+
} else {
204+
separator = ""; //$NON-NLS-1$
205+
for (int j = i + 1; j < tokens.length; ++j) {
206+
String jtoken = tokens[j];
207+
if (j == i + 1 && jtoken.charAt(0) == '(') {
208+
jtoken = jtoken.substring(1);
209+
parmStart = true;
210+
protoStart = false;
211+
}
212+
prototype.append(separator).append(jtoken);
213+
separator = " "; //$NON-NLS-1$
214+
}
215+
if (parmStart && completed) {
216+
parmStart = false;
217+
descStart = true;
218+
}
219+
break;
220+
}
221+
}
222+
returnType = false;
223+
} else if (protoStart) {
224+
String temp = data.toString().trim();
225+
boolean completed = false;
226+
if (temp.endsWith(");")) { //$NON-NLS-1$
227+
completed = true;
228+
temp = temp.substring(0, temp.length() - 2);
229+
}
230+
String separator = " "; //$NON-NLS-1$
231+
while (temp.startsWith("*") || temp.startsWith("const")) { //$NON-NLS-1$ //$NON-NLS-2$
232+
if (temp.charAt(0) == '*') {
233+
returnValue += separator + "*"; //$NON-NLS-1$
234+
temp = temp.substring(1).trim();
235+
separator = ""; //$NON-NLS-1$
236+
} else {
237+
returnValue += "const"; //$NON-NLS-1$
238+
temp = temp.substring(5).trim();
239+
separator = " "; //$NON-NLS-1$
240+
}
241+
}
242+
int index = temp.lastIndexOf('(');
243+
int index2 = temp.lastIndexOf(')');
244+
if (index2 < index) {
245+
if (index + 1 < temp.length()) {
246+
temp = temp.substring(index + 1).trim();
247+
prototype.append(temp);
248+
}
249+
parmStart = true;
250+
protoStart = false;
251+
}
252+
if (parmStart && completed) {
253+
parmStart = false;
254+
descStart = true;
255+
}
256+
} else if (parmStart) {
257+
String parmData = data.toString().trim();
258+
int index = parmData.indexOf(')');
259+
if (index >= 0) {
260+
parmStart = false;
261+
descStart = true;
262+
parmData = parmData.substring(0, index);
263+
}
264+
if (prototype.length() == 0) {
265+
if (!parmData.equals(",") && !parmData.isEmpty()) { //$NON-NLS-1$
266+
parmData = " " + parmData; //$NON-NLS-1$
267+
}
268+
}
269+
prototype.append(parmData);
270+
} else if (descStart) {
271+
if (!rowIgnore) {
272+
description.append(String.valueOf(data));
273+
}
274+
}
275+
}
276+
}
277+
278+
@Override
279+
public TreeMap<String, FunctionInfo> getFunctionInfos() {
280+
return infos;
281+
}
282+
283+
@Override
284+
public String toString() {
285+
return "funcName: <" + funcName + "> returnType: <" + returnValue + //$NON-NLS-1$ //$NON-NLS-2$
286+
"> prototype: <" + prototype + "> description: " + description; //$NON-NLS-1$ //$NON-NLS-2$
287+
}
288+
}

0 commit comments

Comments
 (0)