-
Notifications
You must be signed in to change notification settings - Fork 778
/
Copy pathJFlexNonXref.java
511 lines (459 loc) · 17 KB
/
JFlexNonXref.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* See LICENSE.txt included in this distribution for the specific
* language governing permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at LICENSE.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
* Portions Copyright 2011 Jens Elkner.
* Portions Copyright (c) 2017-2018, Chris Fraire <[email protected]>.
*/
package org.opensolaris.opengrok.analysis;
import java.io.CharArrayReader;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Set;
import java.util.regex.Pattern;
import org.opensolaris.opengrok.analysis.Scopes.Scope;
import org.opensolaris.opengrok.configuration.Project;
import org.opensolaris.opengrok.configuration.RuntimeEnvironment;
import org.opensolaris.opengrok.history.Annotation;
import org.opensolaris.opengrok.util.StringUtils;
import org.opensolaris.opengrok.web.Util;
/**
* Represents a base class for non-traditional xref lexers whose
* cross-references are not straight-forward representations of source code but
* instead are attempts to show derived presentations (e.g. a visual
* representation of troff or mandoc(5)).
* <p>
* This approach is controversial (see
* <a href="https://github.com/oracle/opengrok/issues/33">
* "man pages are not cross-referenced"</a>,
* <a href="https://github.com/oracle/opengrok/issues/393">
* "man2html rendering of sh.1 shows garbage"</a>,
* <a href="https://github.com/oracle/opengrok/issues/433">
* "Annotate for man pages does not work"</a>,
* <a href="https://github.com/oracle/opengrok/issues/553">
* "man page garbled"</a>) and probably should be considered deprecated as a
* candidate for future removal in favor of a feature described by
* <a href="https://github.com/kahatlen">kahatlen</a>:
* <p>
* "The [troff or mandoc(5)] xref could show the markup, and then there could
* be a button you could click to see the rendered output."
*/
public abstract class JFlexNonXref extends JFlexStateStacker
implements Xrefer {
protected Writer out;
protected String urlPrefix = RuntimeEnvironment.getInstance().getUrlPrefix();
protected Annotation annotation;
protected Project project;
protected Definitions defs;
private boolean scopesEnabled;
private boolean foldingEnabled;
private boolean scopeOpen;
protected Scopes scopes = new Scopes();
protected Scope scope;
private int scopeLevel;
/**
* The following field is set to {@code true} (via {@link #phLOC()}) when
* applicable during lexing before a call to {@link #startNewLine()} so
* that the lines-of-code count is also incremented.
*/
protected boolean didSeePhysicalLOC;
protected int loc;
/**
* See {@link RuntimeEnvironment#getUserPage()}. Per default initialized in
* the constructor and here to be consistent and avoid lot of unnecessary
* lookups.
*
* @see #startNewLine()
*/
protected String userPageLink;
/**
* See {@link RuntimeEnvironment#getUserPageSuffix()}. Per default
* initialized in the constructor and here to be consistent and avoid lot of
* unnecessary lookups.
*
* @see #startNewLine()
*/
protected String userPageSuffix;
/**
* The span class name from the last call to
* {@link #disjointSpan(java.lang.String)}.
*/
private String disjointSpanClassName;
protected JFlexNonXref() {
userPageLink = RuntimeEnvironment.getInstance().getUserPage();
if (userPageLink != null && userPageLink.length() == 0) {
userPageLink = null;
}
userPageSuffix = RuntimeEnvironment.getInstance().getUserPageSuffix();
if (userPageSuffix != null && userPageSuffix.length() == 0) {
userPageSuffix = null;
}
}
/**
* Reinitialize the xref with new contents.
*
* @param contents a char buffer with text to analyze
* @param length the number of characters to use from the char buffer
*/
public void reInit(char[] contents, int length) {
reInit(new CharArrayReader(contents, 0, length));
}
/**
* Reinitialize the lexer with new reader.
*
* @param reader new reader for this lexer
*/
public void reInit(Reader reader) {
this.yyreset(reader);
reset();
}
/**
* Resets the xref tracked state; {@inheritDoc}
*/
@Override
public void reset() {
super.reset();
annotation = null;
didSeePhysicalLOC = false;
disjointSpanClassName = null;
loc = 0;
scopes = new Scopes();
scope = null;
scopeLevel = 0;
scopeOpen = false;
setLineNumber(1);
}
/**
* Gets the document physical lines-of-code count.
* @return a number greater than or equal to 0
*/
@Override
public int getLOC() { return loc; }
@Override
public void setAnnotation(Annotation annotation) {
this.annotation = annotation;
}
/**
* set definitions
* @param defs definitions
*/
public void setDefs(Definitions defs) {
this.defs = defs;
}
@Override
public void setProject(Project project) {
this.project = project;
}
/**
* set scopes
* @param scopesEnabled if they should be enabled or disabled
*/
public void setScopesEnabled(boolean scopesEnabled) {
this.scopesEnabled = scopesEnabled;
}
/**
* set folding of code
* @param foldingEnabled whether to fold or not
*/
public void setFoldingEnabled(boolean foldingEnabled) {
this.foldingEnabled = foldingEnabled;
}
/**
* Sets a value indicating that a physical line-of-code was encountered.
*/
protected void phLOC() { didSeePhysicalLOC = true; }
/**
* Calls {@link #appendLink(java.lang.String, boolean)} with {@code url}
* and false.
* @param url the URL to append
* @throws IOException if an error occurs while appending
*/
protected void appendLink(String url) throws IOException {
appendLink(url, false);
}
/**
* Calls
* {@link #appendLink(java.lang.String, boolean, java.util.regex.Pattern)}
* with {@code url}, {@code doEndingPushback}, and null.
* @param url the URL to append
* @param doEndingPushback a value indicating whether to test the
* {@code url} with
* {@link StringUtils#countURIEndingPushback(java.lang.String)}
* @throws IOException if an error occurs while appending
*/
protected void appendLink(String url, boolean doEndingPushback)
throws IOException {
appendLink(url, doEndingPushback, null);
}
/**
* Calls
* {@link JFlexXrefUtils#appendLink(java.io.Writer, org.opensolaris.opengrok.analysis.JFlexLexer, java.lang.String, boolean, java.util.regex.Pattern)}
* with the active {@link Writer}, the field {@code matcher}, {@code url},
* {@code doEndingPushback}, and {@code collateralCapture}.
* @param url the URL to append
* @param doEndingPushback a value indicating whether to test the
* {@code url} with
* {@link StringUtils#countURIEndingPushback(java.lang.String)}
* @param collateralCapture optional pattern to indicate characters which
* may have been captured as valid URI characters but in a particular
* context should mark the start of a pushback
* @throws IOException if an error occurs while appending
*/
protected void appendLink(String url, boolean doEndingPushback,
Pattern collateralCapture)
throws IOException {
JFlexXrefUtils.appendLink(out, this, url, doEndingPushback,
collateralCapture);
}
protected String getProjectPostfix(boolean encoded) {
String amp = encoded ? "&" : "&";
return project == null ? "" : (amp + "project=" + project.getName());
}
protected void startScope() {
Scope newScope = JFlexXrefUtils.maybeNewScope(scopesEnabled, scope,
this, defs);
if (newScope != null) {
scope = newScope;
scopeLevel = 0;
}
}
protected void incScope() {
if (scope != null) {
scopeLevel++;
}
}
protected void decScope() {
if (scope != null && scopeLevel > 0) {
scopeLevel--;
if (scopeLevel == 0) {
scope.setLineTo(getLineNumber());
scopes.addScope(scope);
scope = null;
}
}
}
protected void endScope() {
if (scope != null && scopeLevel == 0) {
scope.setLineTo(getLineNumber());
scopes.addScope(scope);
scope = null;
}
}
/**
* Get generated scopes.
* @return scopes for current line
*/
public Scopes getScopes() {
return scopes;
}
/**
* Writes the closing of an open span tag previously opened by this method
* and the opening -- if {@code className} is non-null -- of a new span
* tag.
* <p>This method's disjoint spans are independent of any span used for
* scopes.
* <p>Any open span is closed at the end of {@link #write(java.io.Writer)}
* just before any open scope is closed.
* @param className the class name for the new tag or {@code null} just to
* close an open tag.
* @throws IOException if an output error occurs
*/
public void disjointSpan(String className) throws IOException {
disjointSpanClassName = JFlexXrefUtils.disjointSpan(out, className,
disjointSpanClassName);
}
/**
* Gets the argument from the last call to
* {@link #disjointSpan(java.lang.String)}.
* @return a defined value or null
*/
public String getDisjointSpanClassName() {
return disjointSpanClassName;
}
/**
* Write xref to the specified {@code Writer}.
*
* @param out xref destination
* @throws IOException on error when writing the xref
*/
public void write(Writer out) throws IOException {
this.out = out;
if (defs != null) defs.resetUnused();
JFlexXrefUtils.writeSymbolTable(out, defs);
setLineNumber(1);
startNewLine();
while (yylex() != getYYEOF()) { // NOPMD while statement intentionally empty
// nothing to do here, yylex() will do the work
}
disjointSpan(null);
// terminate scopes
if (scopeOpen) {
out.write("</span>");
scopeOpen = false;
}
while (!stack.empty()) {
yypop();
}
}
/**
* Calls {@link Util#prehtmlize(java.lang.String)}.
* @param raw Raw string
* @return String with escaped characters
*/
protected String htmlize(String raw) {
return Util.prehtmlize(raw);
}
/**
* Terminate the current line and insert preamble for the next line. The
* line count will be incremented.
*
* @throws IOException on error when writing the xref
*/
public void startNewLine() throws IOException {
String iconId = null;
int line = getLineNumber();
boolean skipNl = false;
setLineNumber(line + 1);
if (didSeePhysicalLOC) {
++loc;
didSeePhysicalLOC = false;
}
if (scopesEnabled) {
startScope();
if (scopeOpen && scope == null) {
scopeOpen = false;
out.write("\n</span>");
skipNl = true;
} else if (scope != null) {
String scopeId = JFlexXrefUtils.generateId(scope);
if (scope.getLineFrom() == line) {
out.write("\n<span id='");
out.write(scopeId);
out.write("' class='scope-head'><span class='scope-signature'>");
out.write(htmlize(scope.getName() + scope.getSignature()));
out.write("</span>");
iconId = scopeId + "_fold_icon";
skipNl = true;
} else if (scope.getLineFrom() == line - 1) {
if (scopeOpen) {
out.write("</span>");
}
out.write("\n<span id='");
out.write(scopeId);
out.write("_fold' class='scope-body'>");
skipNl = true;
}
scopeOpen = true;
}
}
Util.readableLine(line, out, annotation, userPageLink, userPageSuffix,
getProjectPostfix(true), skipNl);
if (foldingEnabled && scopesEnabled) {
if (iconId != null) {
out.write("<a style='cursor:pointer;' onclick='fold(this.parentNode.id)' id='");
out.write(iconId);
/* space inside span for IE support */
out.write("'><span class='fold-icon'> </span></a>");
} else {
out.write("<span class='fold-space'> </span>");
}
}
}
/**
* Write a symbol and generate links as appropriate.
*
* @param symbol the symbol to write
* @param keywords a set of keywords recognized by this analyzer (no links
* will be generated if the symbol is a keyword)
* @param line the line number on which the symbol appears
* @return true if the {@code symbol} was not in {@code keywords} or if
* {@code keywords} was null
* @throws IOException if an error occurs while writing to the stream
*/
protected boolean writeSymbol(String symbol, Set<String> keywords, int line)
throws IOException {
return writeSymbol(symbol, keywords, line, true, false);
}
/**
* Write a symbol and generate links as appropriate.
*
* @param symbol the symbol to write
* @param keywords a set of keywords recognized by this analyzer (no links
* will be generated if the symbol is a keyword)
* @param line the line number on which the symbol appears
* @param caseSensitive Whether the keyword list is case sensitive
* @return true if the {@code symbol} was not in {@code keywords} or if
* {@code keywords} was null
* @throws IOException if an error occurs while writing to the stream
*/
protected boolean writeSymbol(String symbol, Set<String> keywords, int line,
boolean caseSensitive) throws IOException {
return writeSymbol(symbol, keywords, line, caseSensitive, false);
}
/**
* Write a symbol and generate links as appropriate.
*
* @param symbol the symbol to write
* @param keywords a set of keywords recognized by this analyzer (no links
* will be generated if the symbol is a keyword)
* @param line the line number on which the symbol appears
* @param caseSensitive Whether the keyword list is case sensitive
* @param isKeyword Whether the symbol is certainly a keyword without
* bothering to look up in a defined {@code keywords}
* @return true if the {@code symbol} was not in {@code keywords} or if
* {@code keywords} was null and if-and-only-if {@code isKeyword} is false
* @throws IOException if an error occurs while writing to the stream
*/
protected boolean writeSymbol(String symbol, Set<String> keywords, int line,
boolean caseSensitive, boolean isKeyword) throws IOException {
return JFlexXrefUtils.writeSymbol(out, defs, urlPrefix, project,
symbol, symbol, keywords, line, caseSensitive, isKeyword);
}
/**
* Writes a keyword symbol.
*
* @param symbol the symbol to write
* @param line the line number on which the symbol appears
* @throws IOException if an error occurs while writing to the stream
*/
protected void writeKeyword(String symbol, int line) throws IOException {
writeSymbol(symbol, null, line, false, true);
}
/**
* Calls {@link JFlexXrefUtils#writeUnicodeChar(java.io.Writer, char)} with
* the active {@link Writer} and {@code c}.
* @param c the character to write
* @throws IOException if an error occurs while writing to the stream
*/
protected void writeUnicodeChar(char c) throws IOException {
JFlexXrefUtils.writeUnicodeChar(out, c);
}
/**
* Write an e-mail address. The address will be obfuscated if
* {@link RuntimeEnvironment#isObfuscatingEMailAddresses()} returns
* {@code true}.
*
* @param address the address to write
* @throws IOException if an error occurs while writing to the stream
*/
protected void writeEMailAddress(String address) throws IOException {
JFlexXrefUtils.writeEMailAddress(out, address);
}
}