Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/sourcetools/com.ibm.jpp.preprocessor/com/ibm/jpp/om/JavaPreprocessor.java
6004 views
1
/*******************************************************************************
2
* Copyright (c) 1999, 2021 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
21
*******************************************************************************/
22
package com.ibm.jpp.om;
23
24
import java.io.BufferedReader;
25
import java.io.File;
26
import java.io.FileInputStream;
27
import java.io.FileNotFoundException;
28
import java.io.FileOutputStream;
29
import java.io.IOException;
30
import java.io.InputStreamReader;
31
import java.io.OutputStream;
32
import java.io.OutputStreamWriter;
33
import java.io.PrintWriter;
34
import java.io.Writer;
35
import java.nio.charset.Charset;
36
import java.nio.charset.StandardCharsets;
37
import java.text.NumberFormat;
38
import java.text.ParseException;
39
import java.util.ArrayList;
40
import java.util.Collection;
41
import java.util.HashMap;
42
import java.util.HashSet;
43
import java.util.LinkedList;
44
import java.util.List;
45
import java.util.Map;
46
import java.util.NoSuchElementException;
47
import java.util.Set;
48
import java.util.Stack;
49
import java.util.StringTokenizer;
50
import java.util.regex.Pattern;
51
52
/**
53
* This class pre-processes the contents of an input stream, providing
54
* a mechanism for text exclusion which is similar to the #ifdef mechanism
55
* in the C pre-processor.
56
* <p>
57
* It looks for expressions of the form:
58
* <pre>
59
* /&#42;[IF flag1 | flag2 &amp; (flag3 &circ; flag4)]&#42;/
60
* &quot;lines to include if the expression is true&quot;
61
* /&#42;[ENDIF]&#42;/
62
* </pre>
63
* (Note: No space between last "*" and last "/")
64
* <p>
65
* The commands must be the first item on the line, and they may be nested.
66
* <p>
67
* Note that the source is assumed to be 8859_1 text.
68
*
69
* @author IBM Corp.
70
* @version initial
71
*/
72
public class JavaPreprocessor {
73
74
private static final class IfState {
75
76
/**
77
* The current IF or ELSEIF branch is active.
78
*/
79
static final int IF_ACTIVE = 0;
80
81
/**
82
* A previous IF or ELSEIF branch was selected.
83
*/
84
static final int IF_PREVIOUS = 1;
85
86
/**
87
* The current IF or ELSEIF branch is inactive
88
* (and no previous branch was selected).
89
*/
90
static final int IF_INACTIVE = 2;
91
92
/**
93
* We're in an active ELSE branch.
94
*/
95
static final int ELSE_ACTIVE = 3;
96
97
/**
98
* We're in an inactive ELSE branch.
99
*/
100
static final int ELSE_INACTIVE = 4;
101
102
final boolean outerActive;
103
104
int state;
105
106
IfState(boolean active, Stack<IfState> stack) {
107
super();
108
this.outerActive = stack.isEmpty() || stack.peek().isActive();
109
this.state = active ? IF_ACTIVE : IF_INACTIVE;
110
}
111
112
boolean isActive() {
113
switch (state) {
114
case IF_ACTIVE:
115
case ELSE_ACTIVE:
116
return outerActive;
117
default:
118
return false;
119
}
120
}
121
122
}
123
124
private static final Charset charset;
125
126
static {
127
if ("z/OS".equalsIgnoreCase(System.getProperty("os.name"))) { //$NON-NLS-1$ //$NON-NLS-2$
128
charset = Charset.forName("IBM-1047"); //$NON-NLS-1$
129
} else {
130
charset = StandardCharsets.US_ASCII;
131
}
132
}
133
134
private final File inFile;
135
private BufferedReader in;
136
private final Stack<BufferedReader> inStack;
137
private final Writer out;
138
139
/**
140
* Configurable options. The following can be configured by setters before
141
* preprocess() is called.
142
*/
143
private final Set<String> flags = new HashSet<>();
144
// Flags that are required in an INCLUDE-IF for the file to be included
145
private Set<String> requiredIncludeFlags = new HashSet<>();
146
// Flags which were ref'ed by source code.
147
private List<String> foundFlags = new ArrayList<>();
148
// The PRs found fixed in the code (using the PR command)
149
private Set<String> fixedPRs = new HashSet<>();
150
private final Set<String> newIDEAs = new HashSet<>();
151
private Set<String> jxeRules = new HashSet<>();
152
private List<PreprocessorWarning> invalidFlags = new ArrayList<>();
153
// Property collection containing key-value pairs declared in MSG command
154
private Map<String, String> externalMessages;
155
// Inline messages flag
156
private boolean replaceMsg_getString = false;
157
private boolean jxePreserveApi = false;
158
private final Map<String, String> macros = new HashMap<>();
159
private final Map<String, Integer> numericMacros = new HashMap<>();
160
private JitAttributes jitAttributes;
161
// Whether files with no INCLUDE-IF commands should be included
162
private boolean includeIfUnsure = false;
163
// whether to give warning about the files that do not have include if directive
164
/* [PR 117967] idea 491: Automatically create the jars required for test bootpath */
165
private boolean noWarnIncludeIf = false;
166
private final Stack<IfState> ifResults = new Stack<>();
167
private int lineCount = 0;
168
private int outputLineCount = 0;
169
private boolean echo = true;
170
private final Set<String> validFlags = new HashSet<>();
171
172
// Stores MSG-command key-value pairs until shouldInclude is determined,
173
// at which point the entries may be added to externalMessages
174
final Map<String, String> externalMessagesToBeAdded = new HashMap<>();
175
176
// variables to handle generating jxe rules for public api
177
private String packageName = null;
178
private String rootClassName = null;
179
180
private boolean useMacros = false;
181
private MsgCallInliner msgCallInliner;
182
183
char[] line = new char[0]; // current line
184
int lineLength = 0; // Length of the current line.
185
private static final int MAX_COMMAND = 16; // Maximum size of a command.
186
private String command = ""; // Current command (only 1).
187
private String argument = ""; // Current argument (only 1).
188
189
private final List<PreprocessorWarning> warnings = new LinkedList<>();
190
private PreprocessorWarning error = null;
191
private int numberOfIncludeIfs = 0;
192
private boolean shouldInclude;
193
/*
194
* true iff this file should be included based on the first INCLUDE-IF directive. If no INCLUDE-IF directive is found in the
195
* file, this will be set to the value of the flag "includeIfUnsure"
196
*/
197
private long timeTaken = 0;
198
private int numberOfBlankLines = 0;
199
// continuation of a long line
200
private final boolean continuation = false;
201
private boolean foundCopyright = false;
202
// name of file being processed
203
private final String file;
204
private Writer metadataOut;
205
206
private static final String newLine = System.lineSeparator();
207
208
private final MsgClassSubstituter msgClassSubstituter = new MsgClassSubstituter();
209
boolean substituteMsgClass = false;
210
211
private boolean addMSGcomment = false;
212
private String msg_comment;
213
/* [PR 120411] Use a javadoc tag instead of TestBootpath preprocessor tag */
214
private boolean isTestBootpathJavaDocTagWritten = false;
215
private boolean preprocessingTestBootpath = false;
216
private boolean testBootpathJavaDocIsNeeded = false;
217
218
final void replaceLine(String replacement) {
219
line = replacement.toCharArray();
220
lineLength = line.length;
221
}
222
223
/**
224
* Create the new instance of the preprocessor.
225
*
226
* @param metadataOut
227
* @param inputFile the input file handle
228
* @param out stream to write the converted output on
229
* @param outputFile the output file handle
230
*/
231
public JavaPreprocessor(OutputStream metadataOut, File inputFile, OutputStream out, File outputFile) {
232
super();
233
this.file = inputFile.getAbsolutePath();
234
this.inStack = new Stack<>();
235
this.inFile = inputFile;
236
this.out = new OutputStreamWriter(out, charset);
237
238
if (metadataOut != null) {
239
this.metadataOut = new OutputStreamWriter(metadataOut, charset);
240
try {
241
this.metadataOut.write(inputFile.getAbsolutePath());
242
this.metadataOut.write(newLine);
243
this.metadataOut.write(outputFile.getAbsolutePath());
244
this.metadataOut.write(newLine);
245
} catch (IOException ex) {
246
error("IOException on write: " + ex.getMessage(), ex);
247
}
248
}
249
}
250
251
/**
252
* Adds flags to the list of valid flags.
253
*
254
* @param flags the flags to be added
255
*/
256
public void addValidFlags(Set<String> flags) {
257
validFlags.addAll(flags);
258
}
259
260
/**
261
* Sets whether preprocessing for bootpath project or not
262
* @param bootPath true if bootPath project, otherwise false
263
*
264
*/
265
/* [PR 120411] Use a javadoc tag instead of TestBootpath preprocessor tag */
266
public void setTestBootPath(boolean bootPath) {
267
preprocessingTestBootpath = bootPath;
268
}
269
270
/**
271
* Removes all the valid flags from this Java preprocessor.
272
*/
273
public void removeValidFlags() {
274
validFlags.clear();
275
}
276
277
/**
278
* Checks if the given string matches one of the current state flags.
279
*
280
* @param arg String the flag to test.
281
* @return boolean true if we the string is defined and false otherwise.
282
*/
283
private boolean defined(String arg) {
284
return flags.contains(arg);
285
}
286
287
/**
288
* Invoke the current command/argument pair.
289
*/
290
private void doCommand() {
291
String cmd = command;
292
293
if (cmd.length() <= 0) {
294
error("Missing command");
295
}
296
297
String arg = argument.trim();
298
299
if (cmd.equals("IF")) {
300
doCommand_IF(arg);
301
} else if (cmd.equals("ENDIF")) {
302
doCommand_ENDIF(arg);
303
} else if (cmd.equals("ELSE")) {
304
doCommand_ELSE(arg);
305
} else if (cmd.equals("ELSEIF")) {
306
doCommand_ELSEIF(arg);
307
} else if (cmd.equals("PR") || cmd.equals("BUG")) {
308
doCommand_PR(arg);
309
} else if (cmd.equals("IDEA")) {
310
doCommand_IDEA(arg);
311
} else if (cmd.equals("INCLUDE-IF")) {
312
doCommand_INCLUDEIF(arg);
313
} else if (cmd.equals("MSG")) {
314
doCommand_MSG(arg);
315
} else if (cmd.equals("JXERULE")) {
316
doCommand_JXERULE(arg);
317
} else if (cmd.equals("REM")) {
318
//
319
} else if (cmd.equals("EXCLUDE-FILE")) {
320
numberOfIncludeIfs++; // INCLUDE-IF replacement
321
} else if (cmd.equals("USEMACROS")) {
322
doCommand_USEMACROS();
323
} else if (cmd.equals("#INCLUDE")) {
324
doCommand_SHARPINCLUDE(arg);
325
} else {
326
error("Bogus command -- \"" + cmd + "\"");
327
}
328
}
329
330
/**
331
* Handle the ELSE command.
332
*
333
* @param arg String the expression to test.
334
*/
335
private void doCommand_ELSE(String arg) {
336
if (ifResults.empty()) {
337
error("ELSE with no IF");
338
}
339
340
IfState top = ifResults.peek();
341
342
switch (top.state) {
343
case IfState.IF_ACTIVE:
344
case IfState.IF_PREVIOUS:
345
top.state = IfState.ELSE_INACTIVE;
346
break;
347
case IfState.IF_INACTIVE:
348
top.state = IfState.ELSE_ACTIVE;
349
break;
350
case IfState.ELSE_ACTIVE:
351
case IfState.ELSE_INACTIVE:
352
error("ELSE after ELSE");
353
break;
354
}
355
356
echo = top.isActive();
357
}
358
359
/**
360
* Handle the ELSEIF command.
361
*
362
* @param arg String the expression to test.
363
*/
364
private void doCommand_ELSEIF(String arg) {
365
if (ifResults.empty()) {
366
error("ELSEIF with no IF");
367
}
368
369
boolean satisfied;
370
371
if (arg.isEmpty()) {
372
satisfied = false;
373
} else {
374
try {
375
ExpressionResult parseResult = ExpressionParser.parse(arg, this);
376
377
if (!parseResult.isBoolean) {
378
error(String.format("boolean expression required: '%s'", arg)); //$NON-NLS-1$
379
return;
380
}
381
382
satisfied = parseResult.value != 0;
383
} catch (ParseException pe) {
384
error(pe);
385
return;
386
}
387
}
388
389
IfState top = ifResults.peek();
390
391
switch (top.state) {
392
case IfState.IF_ACTIVE:
393
top.state = IfState.IF_PREVIOUS;
394
break;
395
case IfState.IF_INACTIVE:
396
if (satisfied) {
397
top.state = IfState.IF_ACTIVE;
398
}
399
break;
400
case IfState.IF_PREVIOUS:
401
break;
402
case IfState.ELSE_ACTIVE:
403
case IfState.ELSE_INACTIVE:
404
error("ELSEIF after ELSE");
405
break;
406
}
407
408
echo = top.isActive();
409
}
410
411
/**
412
* Handle the PR command.
413
*
414
* @param arg String the PR number to be added.
415
*/
416
private void doCommand_PR(String arg) {
417
fixedPRs.add(arg);
418
}
419
420
/**
421
* Handle the IDEA command.
422
*
423
* @param arg String the PR number to be added.
424
*/
425
private void doCommand_IDEA(String arg) {
426
newIDEAs.add(arg);
427
}
428
429
/**
430
* Handle the INCLUDEIF command.
431
*
432
* @param arg String the boolean expression involving flags
433
*/
434
private void doCommand_INCLUDEIF(String arg) {
435
numberOfIncludeIfs++;
436
if (numberOfIncludeIfs != 1) {
437
warning("Extra INCLUDE-IF directive found on line " + lineCount + " will be ignored.");
438
return;
439
}
440
if (arg.isEmpty()) {
441
shouldInclude = false;
442
} else {
443
try {
444
/* [PR 120411] Use a javadoc tag instead of TestBootpath preprocessor tag */
445
if (preprocessingTestBootpath) {
446
if (arg.indexOf("TestBootpath") != -1) {
447
testBootpathJavaDocIsNeeded = true;
448
}
449
}
450
451
ExpressionResult parseResult = ExpressionParser.parse(arg, this);
452
453
if (!parseResult.isBoolean) {
454
error(String.format("boolean expression required: '%s'", arg)); //$NON-NLS-1$
455
return;
456
}
457
458
boolean expressionResult = parseResult.value != 0;
459
460
boolean forceExclude = false;
461
if (expressionResult) {
462
for (String flag : flags) {
463
if (requiredIncludeFlags.contains(flag)) {
464
forceExclude = true;
465
int pos = -1;
466
while (pos <= arg.length()) {
467
pos = arg.indexOf(flag, pos + 1);
468
if (pos == -1) {
469
break;
470
}
471
if (isWordStart(arg, pos) && isWordEnd(arg, pos + flag.length())) {
472
forceExclude = false;
473
break;
474
}
475
}
476
if (!forceExclude) {
477
break;
478
}
479
}
480
}
481
}
482
shouldInclude = forceExclude ? false : expressionResult;
483
} catch (ParseException pe) {
484
error(pe);
485
return;
486
}
487
}
488
}
489
490
/**
491
* Is the character at the given index the start of a word?
492
* That is, there's only whitespace between the preceding
493
* punctuation (if any) and that character.
494
*/
495
private static boolean isWordStart(String text, int index) {
496
while (index > 0 && Character.isWhitespace(text.charAt(index - 1))) {
497
index -= 1;
498
}
499
return (index == 0) || isOperatorOrBracket(text.charAt(index - 1));
500
}
501
502
/**
503
* Is the character at the given index the end of a word?
504
* That is, there's only whitespace between the following
505
* punctuation (if any) and that character.
506
*/
507
private static boolean isWordEnd(String text, int index) {
508
int length = text.length();
509
while (index < length && Character.isWhitespace(text.charAt(index))) {
510
index += 1;
511
}
512
return (index == length) || isOperatorOrBracket(text.charAt(index));
513
}
514
515
/**
516
* Handle the ENDIF command.
517
*
518
* @param arg String the expression to test.
519
*/
520
private void doCommand_ENDIF(String arg) {
521
if (ifResults.empty()) {
522
error("ENDIF with no IF");
523
}
524
525
ifResults.pop();
526
echo = ifResults.empty() || ifResults.peek().isActive();
527
}
528
529
/**
530
* Handle the IF command.
531
*
532
* @param arg String the expression to test.
533
*/
534
private void doCommand_IF(String arg) {
535
boolean satisfied;
536
537
if (arg.isEmpty()) {
538
satisfied = false;
539
} else {
540
try {
541
ExpressionResult parseResult = ExpressionParser.parse(arg, this);
542
543
if (!parseResult.isBoolean) {
544
error(String.format("boolean expression required: '%s'", arg)); //$NON-NLS-1$
545
return;
546
}
547
548
satisfied = parseResult.value != 0;
549
} catch (ParseException pe) {
550
error(pe);
551
return;
552
}
553
}
554
555
IfState top = new IfState(satisfied, ifResults);
556
557
ifResults.push(top);
558
echo = top.isActive();
559
}
560
561
/**
562
* Handle the MSG command
563
*
564
* @param arg String the argument to this MSG command.
565
*/
566
private void doCommand_MSG(String arg) {
567
if (externalMessages == null || !echo) {
568
return;
569
}
570
571
try {
572
MSGArg msgArg = new MSGArg(arg);
573
String key = msgArg.getKey();
574
String value = msgArg.getValue();
575
boolean store = true;
576
577
// First check to see if the key and value exist already
578
if (externalMessages.containsKey(key)) {
579
if (!value.equals(externalMessages.get(key))) {
580
throw new SyntaxException("\"" + key + "\" already has the value \"" + externalMessages.get(key) + "\"");
581
}
582
} else if (externalMessages.containsValue(value)) {
583
warning("MSG command warning: \"" + value + "\" is given multiple keys");
584
}
585
586
if (externalMessagesToBeAdded.containsKey(key)) {
587
if (value.equals(externalMessagesToBeAdded.get(key))) {
588
store = false;
589
} else {
590
throw new SyntaxException("\"" + key + "\" already has the value \"" + externalMessagesToBeAdded.get(key) + "\"");
591
}
592
} else if (externalMessagesToBeAdded.containsValue(value)) {
593
warning("MSG command warning: \"" + value + "\" is given multiple keys");
594
}
595
596
if (store) {
597
externalMessagesToBeAdded.put(key, value);
598
}
599
/* [PR 119681] Desing:952 - Core.JCL : Add external messages in generated source */
600
if (msgCallInliner == null || msgCallInliner.inlineKeys) {
601
value = value.replaceAll("\n", "\\\\n");
602
value = value.replaceAll("\b", "\\\\b");
603
value = value.replaceAll("\t", "\\\\t");
604
value = value.replaceAll("\f", "\\\\f");
605
value = value.replaceAll("\r", "\\\\r");
606
msg_comment = "// " + key + " = " + value;
607
addMSGcomment = true;
608
}
609
} catch (SyntaxException e) {
610
warning("MSG command syntax error, command ignored: MSG " + arg + " - Detail: " + e.getMessage());
611
}
612
}
613
614
/**
615
* Handles the JXERULE command. The quoted text following this command is
616
* added to the java/lang/jxeLink.rules file. Command syntax example: <code>
617
* [JXERULE "-includeField java.io.FileInputStream:java.io.FileInputStream.fd"]
618
* </code>
619
*/
620
private void doCommand_JXERULE(String arg) {
621
if (!echo) {
622
return;
623
}
624
625
StringBuilder buff = new StringBuilder();
626
boolean quoted = false;
627
boolean escapeChar = false;
628
int argLength = arg.length();
629
for (int i = 0; i < argLength; i++) {
630
char current = arg.charAt(i);
631
if (quoted) {
632
if (escapeChar) {
633
buff.append(current);
634
escapeChar = false;
635
} else {
636
if (current == '\\') {
637
escapeChar = true;
638
} else if (current == '"') {
639
quoted = false;
640
} else {
641
buff.append(current);
642
}
643
}
644
} else {
645
if (current == '"') {
646
quoted = true;
647
} else if (!Character.isWhitespace(current)) {
648
warning("JXERULE command warning: Unquoted argument for command");
649
return;
650
}
651
}
652
}
653
String rule = buff.toString();
654
655
if (rule.length() > 0 && jxeRules != null) {
656
jxeRules.add(rule);
657
}
658
}
659
660
/**
661
* Tells the preprocessor to search for macros on the following line.
662
*/
663
private void doCommand_USEMACROS() {
664
this.useMacros = true;
665
}
666
667
/**
668
* @param arg
669
*/
670
private void doCommand_SHARPINCLUDE(String arg) {
671
if (!(arg.startsWith("/") || arg.startsWith(":", 1))) {
672
arg = inFile.getParent() + File.separator + arg;
673
}
674
675
try {
676
BufferedReader includeReader = new BufferedReader(new InputStreamReader(new FileInputStream(arg), charset));
677
678
includeReader.mark(1024);
679
680
String line = includeReader.readLine();
681
682
if (line.indexOf("/*[INCLUDE-IF") == -1) {
683
includeReader.reset();
684
}
685
686
inStack.push(in);
687
in = includeReader;
688
} catch (FileNotFoundException e) {
689
error("Could not find #include file: " + arg, e);
690
} catch (IOException e) {
691
error("IO error when loading #include file: " + arg, e);
692
} catch (Exception e) {
693
error("Error when loading #include file: " + arg, e);
694
}
695
}
696
697
/** ********************** end class MSGArg ************************** */
698
699
/**
700
* Handle the ATTR command
701
*
702
* @param arg String the argument to this ATTR command.
703
*/
704
@SuppressWarnings("unused")
705
private void doCommand_ATTR(String arg) {
706
/* If we are inside an IF directive, do not process the attribute. */
707
if (!echo) {
708
return;
709
}
710
711
StringTokenizer st = new StringTokenizer(arg);
712
String type = null;
713
try {
714
type = st.nextToken();
715
716
if (type.equals("Trusted")) {
717
try {
718
int argNum = Integer.valueOf(st.nextToken()).intValue();
719
jitAttributes.addTrustedMethodOrClass(st.nextToken(), argNum);
720
} catch (NumberFormatException e) {
721
warning("ATTR command warning: Expecting integer argument for 'Trusted'");
722
}
723
} else if (type.equals("Untrusted")) {
724
try {
725
int argNum = Integer.valueOf(st.nextToken()).intValue();
726
jitAttributes.addUntrustedMethod(st.nextToken(), argNum);
727
} catch (NumberFormatException e) {
728
warning("ATTR command warning: Expecting integer argument for 'Untrusted'");
729
}
730
} else if (type.equals("Recognized")) {
731
jitAttributes.addRecognizedMethod(st.nextToken());
732
} else if (type.equals("SkipNullChecks")) {
733
jitAttributes.skipNullChecksFor(st.nextToken());
734
} else if (type.equals("SkipBoundChecks")) {
735
jitAttributes.skipBoundChecksFor(st.nextToken());
736
} else if (type.equals("SkipCheckCasts")) {
737
jitAttributes.skipCheckCastsFor(st.nextToken());
738
} else if (type.equals("SkipDivChecks")) {
739
jitAttributes.skipDivChecksFor(st.nextToken());
740
} else if (type.equals("SkipArrayStoreChecks")) {
741
jitAttributes.skipArrayStoreChecksFor(st.nextToken());
742
} else if (type.equals("SkipChecksOnArrayCopies")) {
743
jitAttributes.skipChecksOnArrayCopiesFor(st.nextToken());
744
} else if (type.equals("SkipZeroInitializationOnNewarrays")) {
745
jitAttributes.skipZeroInitializationOnNewarraysFor(st.nextToken());
746
} else {
747
warning("ATTR command warning: bogus type '" + type + "'");
748
}
749
} catch (PreprocessorException ppe) {
750
warning(ppe.toString());
751
} catch (NoSuchElementException e) {
752
if (type == null) {
753
warning("ATTR command warning: no type specified");
754
} else {
755
warning("ATTR command warning: expecting another argument for type '" + type + "'");
756
}
757
return;
758
}
759
if (st.hasMoreTokens()) {
760
warning("ATTR command warning: ignoring extra arguments");
761
}
762
}
763
764
/**
765
* Represents and parses MSG command arguments.
766
*/
767
private static final class MSGArg {
768
private final String arg;
769
private final String key;
770
private final String value;
771
private int begin;
772
private int end;
773
774
/**
775
* Construct an instance of this class which parses the string arg.
776
*
777
* @param arg String the command argument to parse
778
* @throws SyntaxException If arg does not have the correct syntax for
779
* an MSG argument string
780
*/
781
MSGArg(String arg) throws SyntaxException {
782
this.arg = arg;
783
key = determineKey();
784
value = determineValue();
785
}
786
787
/**
788
* Answers the receiver's key string
789
*
790
* @return String the receiver's key string
791
*/
792
String getKey() {
793
return key;
794
}
795
796
/**
797
* Answers the receiver's value string
798
*
799
* @return String the receiver's value string
800
*/
801
String getValue() {
802
return value;
803
}
804
805
/**
806
* Beginning at an offset this.begin in this.arg, this method steps past
807
* leading whitespace then returns the quoted string following this
808
* whitespace, in the process advancing this.end to point to the closing
809
* quote for this quoted string.
810
*
811
* @return String the next quoted string
812
*/
813
private String getNextQuotedString() throws SyntaxException {
814
while (Character.isWhitespace(arg.charAt(begin))) {
815
begin++;
816
}
817
818
if (arg.charAt(begin) != '"') {
819
throw new SyntaxException("A non-whitespace character occurred where only whitespace should.");
820
}
821
822
begin++;
823
end = indexOfUnescaped(arg, '"', begin);
824
if (end == -1) {
825
throw new SyntaxException("Non-terminated quoted string.");
826
}
827
828
return substringUnescaped(arg, '"', begin, end);
829
}
830
831
/**
832
* Determines and answers the key string for the command argument being
833
* parsed. Advances this.end to point to the closing quote following the
834
* key string.
835
*
836
* @return String the key
837
* @throws SyntaxException
838
*/
839
private String determineKey() throws SyntaxException {
840
// get the key
841
begin = 0;
842
return getNextQuotedString();
843
}
844
845
/**
846
* If called following a call to getKey(), this method will determine
847
* and answer the value string. A precondition for this method is that
848
* 'end' must point to a character which is followed by whitespace,
849
* which must, in turn, be followed by the quoted value string
850
*
851
* @return String the receiver's value string
852
*/
853
private String determineValue() throws SyntaxException {
854
// get the value
855
begin = end + 1;
856
while (Character.isWhitespace(arg.charAt(begin))) {
857
begin++;
858
}
859
860
if (arg.charAt(begin) != ',') {
861
throw new SyntaxException("A comma and optional whitespace expected between quoted key and value strings.");
862
}
863
864
begin++;
865
String value = getNextQuotedString();
866
int index = value.indexOf('\\');
867
868
if (index < 0 || index + 1 >= value.length()) {
869
return value;
870
}
871
872
int last = 0;
873
int from = 0;
874
StringBuilder result = new StringBuilder();
875
while (index >= 0) {
876
int special = "\\btnfr".indexOf(value.charAt(index + 1));
877
if (special >= 0) {
878
result.append(value.substring(from, index));
879
result.append("\\\b\t\n\f\r".charAt(special));
880
last = index + 2;
881
from = index + 2;
882
} else {
883
last = index + 1;
884
}
885
index = value.indexOf('\\', last);
886
}
887
888
if (from == 0) {
889
return value;
890
}
891
result.append(value.substring(from));
892
return result.toString();
893
}
894
}
895
896
/** ********************** end class MSGArg ************************** */
897
898
/**
899
* Finds the index, greater than or equal to fromIndex, of a 'non-escaped'
900
* instance of the character ch in the string str. 'non-escaped' means not
901
* directly preceded by an odd number of backslashes. Returns -1 if an
902
* unescaped instance of ch is not found.
903
*
904
* @param str String the string to search
905
* @param ch int the int value of the character to search for
906
* @param fromIndex int the offset in str at which to begin the search
907
*/
908
static int indexOfUnescaped(String str, int ch, int fromIndex) {
909
int result = -1;
910
boolean escaped = true;
911
while (escaped) {
912
result = str.indexOf(ch, fromIndex);
913
escaped = false;
914
int i = result - 1;
915
while (i >= 0 && str.charAt(i) == '\\') {
916
escaped = !escaped;
917
i--;
918
}
919
fromIndex = result + 1;
920
}
921
return result;
922
}
923
924
static String substringUnescaped(String str, char escapedChar, int begin, int end) {
925
StringBuilder strBuffer = new StringBuilder(str);
926
for (int i = begin; i < end; i++) {
927
if (strBuffer.charAt(i) == '\\' && strBuffer.charAt(i + 1) == escapedChar) {
928
strBuffer.deleteCharAt(i);
929
end--;
930
}
931
}
932
return strBuffer.substring(begin, end);
933
}
934
935
/**
936
* Answers a new string based on str in which all instances of charToEscape
937
* are preceded by a backslash.
938
*
939
* @param str String the string to escape
940
* @param charToEscape char the character to be escaped
941
* @return String the escaped version of str
942
*/
943
static String escape(String str, char charToEscape) {
944
StringBuilder buf = new StringBuilder(str);
945
int i = 0;
946
int lengthDiff = 0;
947
while ((i = str.indexOf(charToEscape, i)) != -1) {
948
buf.insert(i + lengthDiff, '\\');
949
lengthDiff++;
950
i++;
951
}
952
return buf.toString();
953
}
954
955
/**
956
* Each preprocessor may use a single instance of this class to replace
957
* specially-formatted calls to Msg.getString() with appropriate
958
* String-valued expressions at preprocess time.
959
*/
960
private final class MsgCallInliner {
961
// arrays and method calls
962
private int pos;
963
private int closeMethod;
964
private String lineString;
965
private StringBuilder lineBuf;
966
final boolean inlineKeys;
967
private int callIdx;
968
969
MsgCallInliner(boolean inlineKeys) {
970
super();
971
this.inlineKeys = inlineKeys;
972
}
973
974
void replaceMsg_getString() throws SyntaxException {
975
boolean argumentsSupplied = true;
976
lineString = new String(line, 0, lineLength);
977
lineBuf = new StringBuilder(lineString);
978
979
// Determine if a reference to Msg.getString is on this line
980
// otherwise return
981
callIdx = lineString.indexOf("com.ibm.oti.util.Msg.getString");
982
if (callIdx == -1) {
983
callIdx = lineString.indexOf("com.ibm.oti.rmi.util.Msg.getString");
984
if (callIdx == -1) {
985
callIdx = lineString.indexOf("Msg.getString");
986
if (callIdx == -1) {
987
return;
988
}
989
pos = callIdx + 13;
990
} else {
991
pos = callIdx + 34;
992
}
993
} else {
994
pos = callIdx + 30;
995
}
996
skipWhite();
997
998
if (line[pos] != '(') {
999
// Make sure it is a method call
1000
return;
1001
}
1002
pos++;
1003
skipWhite();
1004
1005
if (line[pos] != '"') {
1006
// Make sure the first argument is a string literal
1007
return;
1008
}
1009
pos++;
1010
1011
// Find position of closing ")" for method call
1012
closeMethod = closeParenthesis(true, pos - 1);
1013
if (closeMethod == -1) {
1014
// Make sure the method call terminates on this line
1015
throw new SyntaxException("Call to Msg.getString() spanned multiple lines in '" + file + "'");
1016
}
1017
1018
// Take key string and begin replacement message string
1019
int begin = pos;
1020
pos = indexOfUnescaped(lineString, '"', begin);
1021
String key = substringUnescaped(lineString, '"', begin, pos);
1022
String messageStr = inlineKeys ? key : externalMessagesToBeAdded.get(key);
1023
if (messageStr == null) {
1024
throw new SyntaxException("Call to Msg.getString() specified a key string with no corresponding MSG statement.");
1025
}
1026
1027
if (!advanceIfObjectArray()) {
1028
pos++;
1029
skipWhite();
1030
if (line[pos] == ')') {
1031
argumentsSupplied = false; // no arguments supplied
1032
} else if (line[pos] != ',') {
1033
throw new SyntaxException("Comma expected after literal key string in Msg.getString() call.");
1034
} else {
1035
pos++;
1036
}
1037
}
1038
1039
// int callEnd = lineString.indexOf(')', pos);
1040
messageStr = escape(messageStr, '"');
1041
/* [PR 120571] "\n" in external messages causes error */
1042
// messageStr = escape(messageStr, '\n');
1043
messageStr = messageStr.replaceAll("\n", "\\\\n");
1044
messageStr = messageStr.replaceAll("\b", "\\\\b");
1045
messageStr = messageStr.replaceAll("\t", "\\\\t");
1046
messageStr = messageStr.replaceAll("\f", "\\\\f");
1047
messageStr = messageStr.replaceAll("\r", "\\\\r");
1048
1049
messageStr = "\"" + messageStr + "\"";
1050
lineBuf.replace(callIdx, closeMethod + 1, messageStr);
1051
if (argumentsSupplied) {
1052
getAndSubstituteObjectStrings(callIdx + messageStr.length() - 1);
1053
}
1054
substituteLine();
1055
}
1056
1057
private boolean advanceIfObjectArray() {
1058
int i = lineString.indexOf("new", pos);
1059
// added pos so that it will not check before current position
1060
if (i != -1 && i < closeMethod) {
1061
for (i += 3; Character.isWhitespace(line[i]); i++) {
1062
// skip whitespace
1063
}
1064
1065
if (lineString.regionMatches(i, "Object", 0, 6)) {
1066
for (i += 6; Character.isWhitespace(line[i]); i++) {
1067
// skip whitespace
1068
}
1069
1070
if (line[i] == '[') {
1071
for (i += 1; Character.isWhitespace(line[i]); i++) {
1072
// skip whitespace
1073
}
1074
1075
if (line[i] == ']') {
1076
for (i += 1; Character.isWhitespace(line[i]); i++) {
1077
// skip whitespace
1078
}
1079
1080
if (line[i] == '{') {
1081
pos = i + 1;
1082
return true;
1083
}
1084
}
1085
}
1086
}
1087
}
1088
return false;
1089
}
1090
1091
private void skipWhite() {
1092
while (Character.isWhitespace(line[pos])) {
1093
pos++;
1094
}
1095
}
1096
1097
private void getAndSubstituteObjectStrings(int messageEnd) throws SyntaxException {
1098
int objNumber = 0;
1099
do {
1100
String objString = getNextObjectString();
1101
String objStub = "{" + NumberFormat.getNumberInstance().format(objNumber) + "}";
1102
String lineBufStr = lineBuf.toString();
1103
int stubIdx = lineBufStr.indexOf(objStub, callIdx);
1104
if (!inlineKeys && (stubIdx == -1 || stubIdx >= messageEnd)) {
1105
throw new SyntaxException("Call to Msg.getString() specified to many objects.");
1106
}
1107
1108
if (inlineKeys) {
1109
int start = messageEnd + 1;
1110
int end = messageEnd + 1;
1111
String appendString = " + \" " + objStub + "=\" + " + objString;
1112
messageEnd += appendString.length();
1113
lineBuf.replace(start, end, appendString);
1114
lineBufStr = lineBuf.toString();
1115
} else {
1116
// KRLW changed to handle using an argument multiple times
1117
String originalObjString = objString;
1118
do {
1119
int start = stubIdx;
1120
int end = stubIdx + objStub.length();
1121
1122
if (stubIdx == callIdx + 1) {
1123
objString = objString + "+\"";
1124
start--;
1125
messageEnd--;
1126
} else if (stubIdx + objStub.length() == messageEnd) {
1127
objString = "\"+" + objString;
1128
end++;
1129
messageEnd--;
1130
} else {
1131
objString = "\"+" + objString + "+\"";
1132
}
1133
1134
messageEnd += objString.length() - objStub.length();
1135
lineBuf.replace(start, end, objString);
1136
1137
lineBufStr = lineBuf.toString();
1138
stubIdx = lineBufStr.indexOf(objStub, callIdx);
1139
objString = originalObjString;
1140
} while (stubIdx > -1 && stubIdx < messageEnd);
1141
}
1142
objNumber++;
1143
} while (line[pos++] == ',');
1144
1145
String lineBufStr = lineBuf.toString();
1146
while (lineBufStr.indexOf("+\"\"+") > -1) {
1147
int idx = lineBufStr.indexOf("+\"\"+");
1148
lineBuf.replace(idx, idx + 4, "+");
1149
lineBufStr = lineBuf.toString();
1150
}
1151
}
1152
1153
private String getNextObjectString() throws SyntaxException {
1154
// boolean createdObject = false;
1155
skipWhite();
1156
int begin = pos;
1157
1158
// If a string literal, pull it out and return it
1159
if (lineString.charAt(begin) == '"') {
1160
pos = indexOfUnescaped(lineString, '"', begin + 1);
1161
pos++;
1162
return substringUnescaped(lineString, '"', begin, pos);
1163
}
1164
1165
// Make sure this is a valid java identifier or an opening
1166
// parenthesis
1167
if (!(Character.isJavaIdentifierStart(line[pos]) || line[pos] == '(')) {
1168
throw new SyntaxException("Object arguments to Msg.getString() and elements of the Object[] argument to Msg.getString() must be specified as literal strings or variable identifiers");
1169
}
1170
// while(Character.isJavaIdentifierPart(line[pos]))
1171
// pos++;
1172
1173
boolean inString = false;
1174
boolean inSingleQuote = false;
1175
int parenCount = 0;
1176
int squigleCount = 0;
1177
--pos;
1178
do {
1179
++pos;
1180
while (line[pos] != ',' || inString || inSingleQuote) {
1181
if (!inSingleQuote && pos == indexOfUnescaped(lineString, '"', pos)) {
1182
inString = !inString;
1183
}
1184
if (!inString && pos == indexOfUnescaped(lineString, '\'', pos)) {
1185
inSingleQuote = !inSingleQuote;
1186
}
1187
1188
if (!(inString || inSingleQuote)) {
1189
if (line[pos] == '(') {
1190
parenCount++;
1191
} else if (line[pos] == ')') {
1192
parenCount--;
1193
} else if (line[pos] == '{') {
1194
squigleCount++;
1195
} else if (line[pos] == '}') {
1196
squigleCount--;
1197
}
1198
1199
if (parenCount == -1) {
1200
break;
1201
}
1202
if (squigleCount == -1) {
1203
break;
1204
}
1205
}
1206
pos++;
1207
}
1208
} while (parenCount > 0 || squigleCount > 0);
1209
1210
// Check the validity of the arguments.
1211
char ch = line[pos];
1212
if (!(Character.isWhitespace(ch) || ch == ')' || ch == ',' || ch == '}')) {
1213
throw new SyntaxException("Object arguments to Msg.getString() and elements of the Object[] argument to Msg.getString() must be specified as literal strings or variable identifiers");
1214
}
1215
1216
String objString = lineString.substring(begin, pos);
1217
1218
skipWhite();
1219
return objString;
1220
}
1221
1222
private void substituteLine() {
1223
replaceLine(lineBuf.toString());
1224
}
1225
1226
private int closeParenthesis(boolean inString, int index) {
1227
int parenthesisCount = 1;
1228
boolean inSingleQuote = false;
1229
1230
while (parenthesisCount > 0 && index < (line.length - 1)) {
1231
index++;
1232
if (!inSingleQuote && index == indexOfUnescaped(lineString, '"', index)) {
1233
inString = !inString;
1234
}
1235
if (!inString && index == indexOfUnescaped(lineString, '\'', index)) {
1236
inSingleQuote = !inSingleQuote;
1237
}
1238
1239
if (!(inString || inSingleQuote)) {
1240
if (line[index] == '(') {
1241
parenthesisCount++;
1242
} else if (line[index] == ')') {
1243
parenthesisCount--;
1244
}
1245
}
1246
}
1247
1248
if (parenthesisCount > 0) {
1249
return -1;
1250
}
1251
return index;
1252
}
1253
1254
}
1255
1256
/** ********************** end class MsgCallInliner ***************** */
1257
1258
/**
1259
* Each preprocessor may use a single instance of this class to replace
1260
* the name of the class used to lookup messages
1261
*/
1262
private final class MsgClassSubstituter {
1263
private final static String GET_STRING = ".getString";
1264
private final static String DEFAULT_MSG_CLASS = "com.ibm.oti.util.Msg";
1265
private final static String DEFAULT_UNQUALIFIED_MSG_CLASS = "Msg";
1266
private final static String DEFAULT_MSG_GET_STRING = DEFAULT_MSG_CLASS + GET_STRING;
1267
private final static String DEFAULT_UNQUALIFIED_MSG_GET_STRING = DEFAULT_UNQUALIFIED_MSG_CLASS + GET_STRING;
1268
// arrays and method calls
1269
private int pos;
1270
private int closeMethod;
1271
private String lineString;
1272
private StringBuilder lineBuf;
1273
private String msgClassName;
1274
private int callIdx;
1275
int classEnd = 0;
1276
1277
MsgClassSubstituter() {
1278
super();
1279
}
1280
1281
public void setMessageClassName(String msgClassName) {
1282
this.msgClassName = msgClassName;
1283
}
1284
1285
void replaceMsgClass() throws SyntaxException {
1286
lineString = new String(line, 0, lineLength);
1287
lineBuf = new StringBuilder(lineString);
1288
1289
// Determine if a reference to Msg.getString is on this line
1290
// otherwise
1291
// return
1292
callIdx = lineString.indexOf(DEFAULT_MSG_GET_STRING);
1293
if (callIdx == -1) {
1294
callIdx = lineString.indexOf(DEFAULT_UNQUALIFIED_MSG_GET_STRING);
1295
if (callIdx == -1) {
1296
return;
1297
}
1298
pos = callIdx + DEFAULT_UNQUALIFIED_MSG_GET_STRING.length();
1299
classEnd = callIdx + DEFAULT_UNQUALIFIED_MSG_CLASS.length();
1300
} else {
1301
pos = callIdx + DEFAULT_MSG_GET_STRING.length();
1302
classEnd = callIdx + DEFAULT_MSG_CLASS.length();
1303
}
1304
skipWhite();
1305
1306
if (line[pos] != '(') {
1307
// Make sure it is a method call
1308
return;
1309
}
1310
pos++;
1311
skipWhite();
1312
1313
if (line[pos] != '"') {
1314
// Make sure the first argument is a string literal
1315
return;
1316
}
1317
pos++;
1318
1319
// Find position of closing ")" for method call
1320
closeMethod = closeParenthesis(true, pos - 1);
1321
if (closeMethod == -1) {
1322
// Make sure the method call terminates on this line
1323
throw new SyntaxException("Call to Msg.getString() spanned multiple lines in '" + file + "'");
1324
}
1325
1326
// now replace the class used
1327
lineBuf.replace(callIdx, classEnd, msgClassName);
1328
1329
substituteLine();
1330
}
1331
1332
private void skipWhite() {
1333
while (Character.isWhitespace(line[pos])) {
1334
pos++;
1335
}
1336
}
1337
1338
private int closeParenthesis(boolean inString, int index) {
1339
int parenthesisCount = 1;
1340
boolean inSingleQuote = false;
1341
1342
while (parenthesisCount > 0 && index < (line.length - 1)) {
1343
index++;
1344
if (!inSingleQuote && index == indexOfUnescaped(lineString, '"', index)) {
1345
inString = !inString;
1346
}
1347
if (!inString && index == indexOfUnescaped(lineString, '\'', index)) {
1348
inSingleQuote = !inSingleQuote;
1349
}
1350
1351
if (!(inString || inSingleQuote)) {
1352
if (line[index] == '(') {
1353
parenthesisCount++;
1354
} else if (line[index] == ')') {
1355
parenthesisCount--;
1356
}
1357
}
1358
}
1359
1360
if (parenthesisCount > 0) {
1361
return -1;
1362
}
1363
return index;
1364
}
1365
1366
private void substituteLine() {
1367
replaceLine(lineBuf.toString());
1368
}
1369
1370
}
1371
1372
/** ********************** end class MsgCallInliner ***************** */
1373
1374
/**
1375
* Exception class used in parsing MSG commands
1376
*/
1377
private static final class SyntaxException extends Exception {
1378
/**
1379
* serialVersionUID
1380
*/
1381
private static final long serialVersionUID = 3256728398444573747L;
1382
1383
/**
1384
* Syntax exception constructor.
1385
*
1386
* @param msg the exception message
1387
*/
1388
public SyntaxException(String msg) {
1389
super(msg);
1390
}
1391
}
1392
1393
/** ********************** end class SyntaxException ***************** */
1394
1395
private static final class ExpressionScanner {
1396
1397
static final class Token {
1398
1399
final int kind;
1400
1401
final int offset;
1402
1403
final String text;
1404
1405
Token(int kind, String text, int offset) {
1406
super();
1407
this.kind = kind;
1408
this.offset = offset;
1409
this.text = text;
1410
}
1411
1412
}
1413
1414
// tokens
1415
static final int TK_EOI = 0;
1416
static final int TK_IDENT = 1;
1417
static final int TK_NUMBER = 2;
1418
static final int TK_LEFT_PAREN = 3;
1419
static final int TK_RIGHT_PAREN = 4;
1420
static final int TK_AND = 5;
1421
static final int TK_NOT = 6;
1422
static final int TK_OR = 7;
1423
static final int TK_XOR = 8;
1424
static final int TK_LESS_THAN = 9;
1425
static final int TK_LESS_EQUAL = 10;
1426
static final int TK_EQUAL = 11;
1427
static final int TK_NOT_EQUAL = 12;
1428
static final int TK_GREATER_EQUAL = 13;
1429
static final int TK_GREATER_THAN = 14;
1430
1431
private int index;
1432
1433
private final String input;
1434
1435
private final int length;
1436
1437
private Token nextToken;
1438
1439
ExpressionScanner(String input) {
1440
super();
1441
this.input = input;
1442
this.length = input.length();
1443
this.nextToken = null;
1444
}
1445
1446
private void skipWhitespace() {
1447
while (index < length && Character.isWhitespace(input.charAt(index))) {
1448
index += 1;
1449
}
1450
}
1451
1452
Token getNextToken() throws ParseException {
1453
Token token = nextToken;
1454
1455
if (token != null) {
1456
nextToken = null;
1457
return token;
1458
}
1459
1460
skipWhitespace();
1461
1462
if (index >= length) {
1463
return new Token(TK_EOI, "", index);
1464
}
1465
1466
int start = index;
1467
char nextChar = input.charAt(start);
1468
1469
index += 1;
1470
1471
switch (nextChar) {
1472
case '(':
1473
token = new Token(TK_LEFT_PAREN, "(", start);
1474
break;
1475
1476
case ')':
1477
token = new Token(TK_RIGHT_PAREN, ")", start);
1478
break;
1479
1480
case '&':
1481
token = new Token(TK_AND, "&", start);
1482
break;
1483
1484
case '!':
1485
if (index < length && input.charAt(index) == '=') {
1486
index += 1;
1487
token = new Token(TK_NOT_EQUAL, "!=", start);
1488
} else {
1489
token = new Token(TK_NOT, "!", start);
1490
}
1491
break;
1492
1493
case '|':
1494
token = new Token(TK_OR, "|", start);
1495
break;
1496
1497
case '^':
1498
token = new Token(TK_XOR, "^", start);
1499
break;
1500
1501
case '<':
1502
if (index < length && input.charAt(index) == '=') {
1503
index += 1;
1504
token = new Token(TK_LESS_EQUAL, "<=", start);
1505
} else {
1506
token = new Token(TK_LESS_THAN, "<", start);
1507
}
1508
break;
1509
1510
case '=':
1511
if (index < length && input.charAt(index) == '=') {
1512
index += 1;
1513
token = new Token(TK_EQUAL, "==", start);
1514
} else {
1515
throw new ParseException("unrecognized token '='", start);
1516
}
1517
break;
1518
1519
case '>':
1520
if (index < length && input.charAt(index) == '=') {
1521
index += 1;
1522
token = new Token(TK_GREATER_EQUAL, ">=", start);
1523
} else {
1524
token = new Token(TK_GREATER_THAN, ">", start);
1525
}
1526
break;
1527
1528
default:
1529
if (Character.isDigit(nextChar)) {
1530
while (index < length && Character.isDigit(input.charAt(index))) {
1531
index += 1;
1532
}
1533
1534
String number = input.substring(start, index);
1535
1536
token = new Token(TK_NUMBER, number, start);
1537
} else {
1538
while (index < length && !isOperatorOrBracket(input.charAt(index))) {
1539
index += 1;
1540
}
1541
1542
String id = input.substring(start, index).trim();
1543
1544
token = new Token(TK_IDENT, id, start);
1545
}
1546
break;
1547
}
1548
1549
return token;
1550
}
1551
1552
void pushBackToken(Token token) {
1553
if (nextToken != null) {
1554
throw new IllegalStateException();
1555
}
1556
1557
nextToken = token;
1558
}
1559
1560
}
1561
1562
private static final class ExpressionResult {
1563
1564
final boolean isBoolean;
1565
1566
final int value;
1567
1568
ExpressionResult(boolean value) {
1569
super();
1570
this.isBoolean = true;
1571
this.value = value ? 1 : 0;
1572
}
1573
1574
ExpressionResult(int value) {
1575
super();
1576
this.isBoolean = false;
1577
this.value = value;
1578
}
1579
1580
}
1581
1582
private static final class ExpressionParser {
1583
1584
/*
1585
* Expression
1586
* : Term
1587
* | Expression '&' Term
1588
* | Expression '|' Term
1589
* | Expression '^' Term
1590
*
1591
* Term
1592
* : Primary
1593
* | Primary '<' Primary
1594
* | Primary '<=' Primary
1595
* | Primary '==' Primary
1596
* | Primary '!=' Primary
1597
* | Primary '>=' Primary
1598
* | Primary '>' Primary
1599
*
1600
* Primary
1601
* : Identifier
1602
* | Number
1603
* | '!' Primary
1604
* | '(' Expression ')'
1605
*/
1606
static ExpressionResult parse(String input, JavaPreprocessor processor) throws ParseException {
1607
ExpressionScanner scanner = new ExpressionScanner(input);
1608
ExpressionParser parser = new ExpressionParser(processor, scanner);
1609
ExpressionResult result = parser.parseExpression();
1610
ExpressionScanner.Token token = scanner.getNextToken();
1611
1612
if (token.kind == ExpressionScanner.TK_EOI) {
1613
return result;
1614
} else {
1615
throw new ParseException("malformed expression", token.offset);
1616
}
1617
}
1618
1619
/**
1620
* Evaluate a combination of two boolean values.
1621
*/
1622
private static boolean combineBoolean(int lhs, int operator, int rhs) {
1623
boolean lhsBool = lhs != 0;
1624
boolean rhsBool = rhs != 0;
1625
1626
switch (operator) {
1627
case ExpressionScanner.TK_AND:
1628
return lhsBool & rhsBool;
1629
case ExpressionScanner.TK_OR:
1630
return lhsBool | rhsBool;
1631
case ExpressionScanner.TK_XOR:
1632
return lhsBool ^ rhsBool;
1633
default:
1634
throw new IllegalArgumentException();
1635
}
1636
}
1637
1638
/**
1639
* Evaluate a comparison of two numeric values.
1640
*/
1641
private static boolean compareNumeric(int lhs, int operator, int rhs) {
1642
switch (operator) {
1643
case ExpressionScanner.TK_LESS_THAN:
1644
return lhs < rhs;
1645
case ExpressionScanner.TK_LESS_EQUAL:
1646
return lhs <= rhs;
1647
case ExpressionScanner.TK_EQUAL:
1648
return lhs == rhs;
1649
case ExpressionScanner.TK_NOT_EQUAL:
1650
return lhs != rhs;
1651
case ExpressionScanner.TK_GREATER_EQUAL:
1652
return lhs >= rhs;
1653
case ExpressionScanner.TK_GREATER_THAN:
1654
return lhs > rhs;
1655
default:
1656
throw new IllegalArgumentException();
1657
}
1658
}
1659
1660
private final JavaPreprocessor processor;
1661
1662
private final ExpressionScanner scanner;
1663
1664
private ExpressionParser(JavaPreprocessor processor, ExpressionScanner scanner) {
1665
super();
1666
this.processor = processor;
1667
this.scanner = scanner;
1668
}
1669
1670
/*
1671
* Expression
1672
* : Term
1673
* | Expression '&' Term
1674
* | Expression '|' Term
1675
* | Expression '^' Term
1676
*/
1677
private ExpressionResult parseExpression() throws ParseException {
1678
ExpressionResult result = parseTerm();
1679
1680
for (;;) {
1681
ExpressionScanner.Token token = scanner.getNextToken();
1682
1683
switch (token.kind) {
1684
case ExpressionScanner.TK_AND:
1685
case ExpressionScanner.TK_OR:
1686
case ExpressionScanner.TK_XOR:
1687
ExpressionResult rhs = parseTerm();
1688
1689
if (result.isBoolean & rhs.isBoolean) {
1690
result = new ExpressionResult(combineBoolean(result.value, token.kind, rhs.value));
1691
} else {
1692
throw new ParseException(token.text + " requires boolean operands", token.offset);
1693
}
1694
break;
1695
1696
default:
1697
scanner.pushBackToken(token);
1698
return result;
1699
}
1700
}
1701
}
1702
1703
/*
1704
* Term
1705
* : Primary
1706
* | Primary '<' Primary
1707
* | Primary '<=' Primary
1708
* | Primary '==' Primary
1709
* | Primary '!=' Primary
1710
* | Primary '>=' Primary
1711
* | Primary '>' Primary
1712
*/
1713
private ExpressionResult parseTerm() throws ParseException {
1714
ExpressionResult result = parsePrimary();
1715
ExpressionScanner.Token token = scanner.getNextToken();
1716
1717
switch (token.kind) {
1718
case ExpressionScanner.TK_LESS_THAN:
1719
case ExpressionScanner.TK_LESS_EQUAL:
1720
case ExpressionScanner.TK_EQUAL:
1721
case ExpressionScanner.TK_NOT_EQUAL:
1722
case ExpressionScanner.TK_GREATER_EQUAL:
1723
case ExpressionScanner.TK_GREATER_THAN:
1724
ExpressionResult rhs = parsePrimary();
1725
1726
if (result.isBoolean | rhs.isBoolean) {
1727
throw new ParseException(token.text + " requires numeric operands", token.offset);
1728
} else {
1729
result = new ExpressionResult(compareNumeric(result.value, token.kind, rhs.value));
1730
}
1731
break;
1732
1733
default:
1734
scanner.pushBackToken(token);
1735
break;
1736
}
1737
1738
return result;
1739
}
1740
1741
/*
1742
* Primary
1743
* : Identifier
1744
* | Number
1745
* | '!' Primary
1746
* | '(' Expression ')'
1747
*/
1748
private ExpressionResult parsePrimary() throws ParseException {
1749
ExpressionResult result;
1750
ExpressionScanner.Token token = scanner.getNextToken();
1751
1752
switch (token.kind) {
1753
case ExpressionScanner.TK_IDENT:
1754
result = processor.resolve(token.text);
1755
break;
1756
1757
case ExpressionScanner.TK_NUMBER:
1758
result = new ExpressionResult(Integer.parseInt(token.text));
1759
break;
1760
1761
case ExpressionScanner.TK_NOT:
1762
result = parsePrimary();
1763
if (result.isBoolean) {
1764
result = new ExpressionResult(result.value == 0);
1765
} else {
1766
throw new ParseException(token.text + " requires a boolean operand", token.offset);
1767
}
1768
break;
1769
1770
case ExpressionScanner.TK_LEFT_PAREN:
1771
result = parseExpression();
1772
token = scanner.getNextToken();
1773
if (token.kind != ExpressionScanner.TK_RIGHT_PAREN) {
1774
throw new ParseException("expected ')'", token.offset);
1775
}
1776
break;
1777
1778
default:
1779
throw new ParseException("malformed expression", token.offset);
1780
}
1781
1782
return result;
1783
}
1784
1785
}
1786
1787
/**
1788
* Print out an error message and quit.
1789
*/
1790
private void error(String msg) {
1791
error(msg, null);
1792
}
1793
1794
private void error(String msg, Exception cause) {
1795
if (msg == null) {
1796
msg = "";
1797
}
1798
this.error = new PreprocessorWarning(msg, lineCount, 0, lineLength);
1799
String fullMessage = msg + " [" + file + ", line " + lineCount + ']';
1800
writeLog(fullMessage);
1801
PreprocessorException exception = new PreprocessorException(fullMessage);
1802
if (cause != null) {
1803
exception.initCause(cause);
1804
}
1805
throw exception;
1806
}
1807
1808
private void error(ParseException pe) {
1809
String msg = pe.getMessage();
1810
if (msg == null) {
1811
msg = "";
1812
}
1813
this.error = new PreprocessorWarning(msg, lineCount, 0, lineLength);
1814
String fullMessage = msg + " [" + file + ", line " + lineCount + ", column " + pe.getErrorOffset() + ']';
1815
writeLog(fullMessage);
1816
PreprocessorException exception = new PreprocessorException(fullMessage);
1817
exception.initCause(pe);
1818
throw exception;
1819
}
1820
1821
/**
1822
* Write a message to the log file.
1823
*/
1824
private static void writeLog(String msg) {
1825
final String logFileName = "jpp-error.txt";
1826
if (msg == null || msg.equals("")) {
1827
return;
1828
}
1829
try (FileOutputStream fos = new FileOutputStream(logFileName, true);
1830
PrintWriter out = new PrintWriter(fos)) {
1831
out.println(msg);
1832
} catch (IOException e) {
1833
// ignore exceptions
1834
}
1835
}
1836
1837
/**
1838
* Answer the number of lines which have been preprocessed.
1839
*
1840
* @return int the number of lines that have been preprocessed.
1841
*/
1842
public int getLineCount() {
1843
return lineCount;
1844
}
1845
1846
/**
1847
* Output a line break to the output stream.
1848
*/
1849
private void newLine() {
1850
try {
1851
if (echo) {
1852
out.write(newLine);
1853
}
1854
} catch (IOException ex) {
1855
error("IOException on write", ex);
1856
}
1857
}
1858
1859
/**
1860
* Process the streams. Answer if the stream should be included based on the
1861
* first INCLUDE-IF directive found.
1862
* <p>
1863
* @return boolean the result of the first INCLUDE-IF directive in the
1864
* input, or if none is found, true iff the flag "includeIfUnsure"
1865
* is defined (this is a hack until all source files have an
1866
* INCLUDE-IF directive)
1867
*/
1868
public boolean preprocess() {
1869
long start = System.currentTimeMillis();
1870
FileInputStream fis = null;
1871
1872
try {
1873
fis = new FileInputStream(inFile);
1874
this.in = new BufferedReader(new InputStreamReader(fis, charset));
1875
} catch (FileNotFoundException e) {
1876
error("File not found: " + inFile.getAbsolutePath(), e);
1877
}
1878
1879
{ // populate the map of numeric macros
1880
final Pattern Numeric = Pattern.compile("\\d+");
1881
1882
numericMacros.clear();
1883
1884
for (Map.Entry<String, String> entry : macros.entrySet()) {
1885
String value = entry.getValue();
1886
1887
if (Numeric.matcher(value).matches()) {
1888
numericMacros.put(entry.getKey(), Integer.valueOf(value));
1889
}
1890
}
1891
}
1892
1893
/* [PR] The state machine based version was too brittle. Here is a simpler, line base version. */
1894
// parameter initialization
1895
testBootpathJavaDocIsNeeded = false;
1896
isTestBootpathJavaDocTagWritten = false;
1897
1898
// assume a copyright notice is present
1899
foundCopyright = true;
1900
1901
while (readLine()) {
1902
if (addMSGcomment) {
1903
try {
1904
for (int i = 0; i < line.length && (line[i] == '\t' || line[i] == ' '); ++i) {
1905
out.write(line[i]);
1906
}
1907
out.write(msg_comment);
1908
newLine();
1909
} catch (IOException e) {
1910
error("IOException on write: " + e.getMessage(), e);
1911
}
1912
addMSGcomment = false;
1913
msg_comment = "";
1914
}
1915
if (replaceMsg_getString && echo) {
1916
try {
1917
msgCallInliner.replaceMsg_getString();
1918
} catch (SyntaxException e) {
1919
warning("Improper usage of Msg.getString(). Method call could not be removed from code. Detail: " + e.getMessage());
1920
}
1921
}
1922
1923
if (substituteMsgClass && echo) {
1924
try {
1925
msgClassSubstituter.replaceMsgClass();
1926
} catch (SyntaxException e) {
1927
warning("Improper usage of Msg.getString(). Message class could not be substituted. Detail: " + e.getMessage());
1928
}
1929
}
1930
1931
if (!continuation && findCommand()) {
1932
doCommand();
1933
if (!shouldInclude) {
1934
break;
1935
}
1936
} else {
1937
if (!shouldInclude) {
1938
break;
1939
}
1940
1941
// check for class definition
1942
if (jxePreserveApi && echo) {
1943
if (rootClassName == null) {
1944
checkPackageDeclaration();
1945
}
1946
checkClassDeclaration();
1947
}
1948
// replace macros
1949
if (useMacros && macros != null) {
1950
replaceMacros();
1951
useMacros = false;
1952
}
1953
if ((lineLength != 0) || (numberOfBlankLines < 1)) {
1954
// don't print the line if it's a blank and we've already
1955
// printed enough blanks
1956
writeLine();
1957
}
1958
}
1959
}
1960
1961
timeTaken = System.currentTimeMillis() - start;
1962
if (numberOfIncludeIfs == 0) {
1963
if (!noWarnIncludeIf) {
1964
if (includeIfUnsure) {
1965
warnings.add(new PreprocessorWarning("No INCLUDE-IF directives found. File will be INCLUDED", lineCount, 0, lineLength, false));
1966
} else {
1967
warnings.add(new PreprocessorWarning("No INCLUDE-IF directives found. File will be EXCLUDED", lineCount, 0, lineLength, false));
1968
}
1969
}
1970
} else if (shouldInclude && !foundCopyright) {
1971
warning("No copyright");
1972
}
1973
1974
if (shouldInclude) {
1975
externalMessages.putAll(externalMessagesToBeAdded);
1976
}
1977
1978
try {
1979
if (fis != null) {
1980
fis.close();
1981
}
1982
1983
if (in != null) {
1984
in.close();
1985
}
1986
1987
if (this.metadataOut != null) {
1988
this.metadataOut.flush();
1989
}
1990
1991
if (this.out != null) {
1992
this.out.flush();
1993
}
1994
} catch (IOException e) {
1995
error(e.toString(), e);
1996
}
1997
1998
return shouldInclude;
1999
}
2000
2001
/**
2002
* Scans the current line for a public class definition. If one is found, it
2003
* is preserved by including a <code>-includeLibraryClass</code> rule in the
2004
* jxeRules collection.
2005
*/
2006
private void checkClassDeclaration() {
2007
try {
2008
String line = new String(this.line, 0, lineLength);
2009
if (line.indexOf("class") == -1 && line.indexOf("interface") == -1) {
2010
return;
2011
}
2012
2013
boolean isDefinition = false;
2014
boolean isPublic = false;
2015
StringTokenizer tokenizer = new StringTokenizer(line, " \n\r\t", false);
2016
String className = null;
2017
while (tokenizer.hasMoreTokens()) {
2018
String token = tokenizer.nextToken();
2019
if (token.equals("public")) {
2020
isPublic = true;
2021
continue;
2022
} else if (token.equals("private") || token.equals("protected")) {
2023
return;
2024
} else if (token.equals("static") || token.equals("final") || token.equals("abstract")) {
2025
continue;
2026
} else if (token.equals("class") || token.equals("interface")) {
2027
isDefinition = true;
2028
continue;
2029
} else if (isDefinition && isPublic) {
2030
// this is the identifier
2031
className = token;
2032
break;
2033
} else {
2034
return;
2035
}
2036
}
2037
if (className == null || className.equals(rootClassName)) {
2038
return;
2039
}
2040
2041
if (this.rootClassName != null) {
2042
// this is a inner class
2043
className = this.rootClassName + "$" + className;
2044
} else {
2045
this.rootClassName = className;
2046
}
2047
2048
if (this.packageName != null) {
2049
className = this.packageName + "." + className;
2050
2051
if (packageName.startsWith("java.") || packageName.startsWith("javax.")) {
2052
jxeRules.add("-includeLibraryClass " + className);
2053
}
2054
}
2055
} catch (StringIndexOutOfBoundsException e) {
2056
// ignore
2057
}
2058
}
2059
2060
/**
2061
* Scans the current line for a package declaration.
2062
*/
2063
private void checkPackageDeclaration() {
2064
try {
2065
String line = new String(this.line, 0, this.lineLength);
2066
int posPackage = line.indexOf("package");
2067
if (posPackage == -1) {
2068
return;
2069
}
2070
2071
int posIdentifierStart = posPackage + "package".length();
2072
2073
while (!Character.isJavaIdentifierStart(line.charAt(posIdentifierStart))) {
2074
posIdentifierStart++;
2075
}
2076
2077
int posIdentifierEnd = posIdentifierStart + 1;
2078
while (!(line.charAt(posIdentifierEnd) == ';')) {
2079
posIdentifierEnd++;
2080
}
2081
2082
this.packageName = line.substring(posIdentifierStart, posIdentifierEnd).trim();
2083
} catch (StringIndexOutOfBoundsException e) {
2084
// ignore
2085
}
2086
}
2087
2088
/**
2089
* Replaces macro identifiers with their associated values on this line.
2090
* Macro identifiers are denoted by %%MACRO@macroname%%, where macroname is
2091
* the name of the macro to retrieve the value from. The macro values are
2092
* stored in the macros Properties object, set in the constructor.
2093
*/
2094
private void replaceMacros() {
2095
final String macroStartId = "%%MACRO@";
2096
final String macroEndId = "%%";
2097
String line = new String(this.line, 0, this.lineLength);
2098
int posMacroStart = 0;
2099
int posMacroEnd = 0;
2100
while ((posMacroStart = line.indexOf(macroStartId, posMacroEnd)) != -1) {
2101
int posIdentifierStart = posMacroStart + macroStartId.length();
2102
int posIdentifierEnd = line.indexOf(macroEndId, posIdentifierStart);
2103
if (posIdentifierEnd == -1) {
2104
warning("Unterminated macro");
2105
return;
2106
}
2107
if (posIdentifierEnd <= posIdentifierStart) {
2108
error("A macro identifer must be at least one character long");
2109
return;
2110
}
2111
2112
posMacroEnd = posIdentifierEnd + macroEndId.length();
2113
2114
String identifier = line.substring(posIdentifierStart, posIdentifierEnd);
2115
String replacement = macros.get(identifier);
2116
if (replacement == null) {
2117
warning("A replacement for the macro \"" + identifier + "\" could not be found");
2118
replacement = "";
2119
}
2120
2121
StringBuilder newLine = new StringBuilder(line.length() - identifier.length() + replacement.length());
2122
if (posMacroStart > 0) {
2123
newLine.append(line.substring(0, posMacroStart));
2124
}
2125
newLine.append(replacement);
2126
if (posMacroEnd < line.length() - 1) {
2127
newLine.append(line.substring(posMacroEnd, line.length()));
2128
}
2129
2130
line = newLine.toString();
2131
2132
// line has now changed in size thus we need to update posMacroEnd otherwise
2133
// it will point to some other place and indexOf(str,int) will give us a wrong value
2134
posMacroEnd = (posMacroEnd + replacement.length()) - (identifier.length() + macroStartId.length() + macroEndId.length());
2135
}
2136
replaceLine(line);
2137
}
2138
2139
/**
2140
* Read the next line of input. If there are no characters available answer
2141
* false, otherwise fill the input line buffer with the characters,
2142
* excluding the trailing LF, remember the line length, and answer true.
2143
* Strip out all CR characters. If the line is too long to buffer, then just
2144
* assume it isn't a command and write it back out.
2145
*
2146
* @return boolean
2147
*/
2148
private boolean readLine() {
2149
try {
2150
for (;;) {
2151
String nextLine = in.readLine();
2152
2153
if (nextLine != null) {
2154
int length = nextLine.length();
2155
2156
while (length > 0) {
2157
char lastCh = nextLine.charAt(length - 1);
2158
2159
if ((lastCh == '\n') || (lastCh == '\r')) {
2160
length -= 1;
2161
} else {
2162
break;
2163
}
2164
}
2165
2166
line = nextLine.substring(0, length).toCharArray();
2167
lineCount += 1;
2168
lineLength = line.length;
2169
return true;
2170
} else {
2171
in.close();
2172
in = null;
2173
2174
if (inStack.isEmpty()) {
2175
return false;
2176
}
2177
2178
in = inStack.pop();
2179
}
2180
}
2181
} catch (IOException ex) {
2182
error("IOException on read", ex);
2183
}
2184
2185
return true;
2186
}
2187
2188
/**
2189
* If we are currently echoing, then write out the current line.
2190
*/
2191
private void writeLine() {
2192
if (echo) {
2193
try {
2194
out.write(line, 0, lineLength);
2195
/* [PR 120411] Use a javadoc tag instead of TestBootpath preprocessor tag */
2196
if (preprocessingTestBootpath & !isTestBootpathJavaDocTagWritten & testBootpathJavaDocIsNeeded) {
2197
newLine();
2198
out.write("\n/**\n * @TestBootpath\n */");
2199
newLine();
2200
isTestBootpathJavaDocTagWritten = true;
2201
}
2202
2203
if (metadataOut != null) {
2204
StringBuilder sb = new StringBuilder();
2205
sb.append(lineCount);
2206
sb.append(":");
2207
sb.append(++outputLineCount);
2208
metadataOut.write(sb.toString());
2209
metadataOut.write(newLine);
2210
}
2211
if (!continuation) {
2212
newLine();
2213
}
2214
2215
if (lineLength == 0) {
2216
numberOfBlankLines++;
2217
} else {
2218
numberOfBlankLines = 0;
2219
}
2220
} catch (IOException ex) {
2221
error("IOException on write: " + ex.getMessage(), ex);
2222
}
2223
}
2224
}
2225
2226
/**
2227
* Detect if the current line has a command in it. If not, just return
2228
* false. If it does have a command, pull out the command and argument
2229
* pieces, and then return true.
2230
*/
2231
private boolean findCommand() {
2232
command = "";
2233
2234
int i = 0;
2235
2236
// Skip whitespace before the command.
2237
while (i < lineLength && Character.isWhitespace(line[i])) {
2238
i += 1;
2239
}
2240
2241
// Give up if it doesn't start with the command indicator.
2242
if (i > (lineLength - 4) || line[i] != '/' || line[i + 1] != '*' || line[i + 2] != '[') {
2243
return false;
2244
}
2245
2246
i += 3;
2247
2248
// Get the command.
2249
int cmdStart = i;
2250
int cmdLen = 0;
2251
2252
for (; cmdLen <= MAX_COMMAND && i < lineLength; ++cmdLen, ++i) {
2253
char ch = line[i];
2254
2255
if (ch == ']' || Character.isWhitespace(ch)) {
2256
break;
2257
}
2258
}
2259
2260
command = new String(line, cmdStart, cmdLen).toUpperCase();
2261
2262
// Skip whitespace before the argument.
2263
while (i < lineLength && Character.isWhitespace(line[i])) {
2264
i += 1;
2265
}
2266
2267
// Find the last ']' on the line to allow arguments to include that
2268
// character, but this means that ']' cannot be used in comments
2269
// after the command on the same line.
2270
int positionOfClose = lineLength;
2271
2272
for (int pos = i; pos < lineLength; ++pos) {
2273
if (line[pos] == ']') {
2274
positionOfClose = pos;
2275
}
2276
}
2277
2278
// Get the argument if there is one.
2279
argument = new String(line, i, positionOfClose - i);
2280
2281
return true;
2282
}
2283
2284
private static final String SeparatorChars = "()|!&^<=>";
2285
2286
/**
2287
* Answer true iff the char is an operator or bracket
2288
* (i.e. in the set { '&', '^', '|', '!', '(', ')', '<', '=', '>' })
2289
*
2290
* @param c char the char to inspect
2291
*/
2292
static boolean isOperatorOrBracket(char c) {
2293
return SeparatorChars.indexOf(c) != -1;
2294
}
2295
2296
/**
2297
* Checks if the given string matches one of the current state flags, check
2298
* if this flag is a valid flag and add it to the list of incorrect flags if
2299
* it isn't.
2300
*
2301
* @param flag the flag to test.
2302
* @return boolean true if we the string is defined and false otherwise.
2303
*/
2304
private boolean checkFlag(String flag) {
2305
synchronized (foundFlags) {
2306
if (!foundFlags.contains(flag)) {
2307
foundFlags.add(flag);
2308
}
2309
}
2310
if (!validFlags.contains(flag)) {
2311
if (flag == null) {
2312
flag = "";
2313
}
2314
invalidFlags.add(new PreprocessorWarning("The flag " + flag + " is not valid.", lineCount, 0, lineLength));
2315
}
2316
return defined(flag);
2317
}
2318
2319
/**
2320
* Resolve the value of the given identifier.
2321
*/
2322
ExpressionResult resolve(String identifier) {
2323
Integer macro = numericMacros.get(identifier);
2324
2325
if (macro != null) {
2326
return new ExpressionResult(macro.intValue());
2327
} else {
2328
return new ExpressionResult(checkFlag(identifier));
2329
}
2330
}
2331
2332
/**
2333
* Adds a warning to the internal list of warnings.
2334
*
2335
* @param s the warning to add
2336
*/
2337
private void warning(String s) {
2338
if (s == null) {
2339
s = "";
2340
}
2341
warnings.add(new PreprocessorWarning(s, lineCount, 0, lineLength));
2342
}
2343
2344
/**
2345
* Answers true iff the preprocessor has found things in the input it would
2346
* like to warn about
2347
*
2348
* @return boolean if warnings exist
2349
*/
2350
public boolean hasWarnings() {
2351
return warnings.size() > 0;
2352
}
2353
2354
/**
2355
* Returns an iterator over any warnings found in the inputStream
2356
*
2357
* @return List a List of warning Strings
2358
*/
2359
public List<PreprocessorWarning> getWarnings() {
2360
return warnings;
2361
}
2362
2363
/**
2364
* Returns a collection containing the invalid flags.
2365
*
2366
* @return the invalid flags collection
2367
*/
2368
public Collection<PreprocessorWarning> getInvalidFlags() {
2369
return invalidFlags;
2370
}
2371
2372
/**
2373
* Returns the time taken to preprocess
2374
*
2375
* @return long the time taken to preprocess
2376
*/
2377
public long getTime() {
2378
return timeTaken;
2379
}
2380
2381
/**
2382
* Returns the number of flags found in the source code
2383
*
2384
* @return int the number of flags found
2385
*/
2386
public int getIncludeCount() {
2387
return numberOfIncludeIfs;
2388
}
2389
2390
/**
2391
* Returns the fatal error that occured
2392
*
2393
* @return PreprocessorWarning the error that occured
2394
*/
2395
public PreprocessorWarning getError() {
2396
return error;
2397
}
2398
2399
/**
2400
* Adds flags to include when preprocessing.
2401
*
2402
* @param flags the flags to be added
2403
*/
2404
public void addFlags(Set<String> flags) {
2405
if (flags == null) {
2406
throw new IllegalArgumentException();
2407
}
2408
this.flags.addAll(flags);
2409
}
2410
2411
/**
2412
* Sets the flags to include when preprocessing.
2413
*
2414
* @param flags the flags to be added
2415
*/
2416
public void setFlags(String[] flags) {
2417
if (flags == null) {
2418
throw new IllegalArgumentException();
2419
}
2420
for (String flag : flags) {
2421
this.flags.add(flag);
2422
}
2423
}
2424
2425
/**
2426
* Sets the required include flags.
2427
*
2428
* @param requiredIncludeFlags the required include flags
2429
*/
2430
public void setRequiredIncludeFlags(Set<String> requiredIncludeFlags) {
2431
this.requiredIncludeFlags = requiredIncludeFlags;
2432
}
2433
2434
/**
2435
* Sets the list to store flags found in this file.
2436
*
2437
* @param foundFlags the found flags to be set
2438
*/
2439
public void setFoundFlags(List<String> foundFlags) {
2440
if (foundFlags == null) {
2441
throw new IllegalArgumentException();
2442
}
2443
this.foundFlags = foundFlags;
2444
}
2445
2446
/**
2447
* Sets the list to store invalid flags found in this file.
2448
*
2449
* @param invalidFlags the invalid flags to be set
2450
*/
2451
public void setInvalidFlags(List<PreprocessorWarning> invalidFlags) {
2452
if (invalidFlags == null) {
2453
throw new IllegalArgumentException();
2454
}
2455
this.invalidFlags = invalidFlags;
2456
}
2457
2458
/**
2459
* Sets the Set to store fixed PRs found in this file.
2460
*
2461
* @param fixedPRs the fixed PRs to be stored
2462
*/
2463
public void setFixedPRs(Set<String> fixedPRs) {
2464
if (fixedPRs == null) {
2465
throw new IllegalArgumentException();
2466
}
2467
this.fixedPRs = fixedPRs;
2468
}
2469
2470
/**
2471
* Sets the Properties to store external messages found in this file.
2472
*
2473
* @param externalMessages the external messages
2474
*/
2475
public void setExternalMessages(Map<String, String> externalMessages) {
2476
this.externalMessages = externalMessages;
2477
}
2478
2479
/**
2480
* Determines whether messages should be inlined.
2481
*
2482
* @param inlineMessages the inline messages
2483
*/
2484
public void setInlineMessages(boolean inlineMessages) {
2485
replaceMsg_getString = inlineMessages;
2486
msgCallInliner = (inlineMessages) ? new MsgCallInliner(false) : null;
2487
}
2488
2489
/**
2490
* Determines whether message keys should be inlined.
2491
*
2492
* @param inlineMessageKeys the inline keys
2493
*/
2494
public void setInlineMessageKeys(boolean inlineMessageKeys) {
2495
replaceMsg_getString = inlineMessageKeys;
2496
msgCallInliner = (inlineMessageKeys) ? new MsgCallInliner(true) : null;
2497
}
2498
2499
/**
2500
* Sets the macros names and values for replacement.
2501
*
2502
* @param macros the macros
2503
*/
2504
public void setMacros(Map<String, String> macros) {
2505
this.macros.clear();
2506
this.macros.putAll(macros);
2507
}
2508
2509
/**
2510
* Sets the Set to store jxelink rules found in this file.
2511
*
2512
* @param jxeRules the JXELink rules
2513
*/
2514
public void setJxeRules(Set<String> jxeRules) {
2515
this.jxeRules = jxeRules;
2516
}
2517
2518
/**
2519
* Determines whether public api should be preserved using jxelink rules.
2520
*
2521
* @param jxePreserveApi <code>true</code> if public APIs should be preserved, <code>false</code> otherwise
2522
*/
2523
public void setJxePreserveApi(boolean jxePreserveApi) {
2524
this.jxePreserveApi = jxePreserveApi;
2525
}
2526
2527
/**
2528
* @param jitAttributes the JIT attributes
2529
*/
2530
public void setJitAttributes(JitAttributes jitAttributes) {
2531
this.jitAttributes = jitAttributes;
2532
}
2533
2534
/**
2535
* Sets the includeIfUnsure.
2536
*
2537
* @param includeIfUnsure the includeIfUnsure to set
2538
*/
2539
public void setIncludeIfUnsure(boolean includeIfUnsure) {
2540
this.includeIfUnsure = includeIfUnsure;
2541
this.shouldInclude = includeIfUnsure;
2542
}
2543
2544
/**
2545
* Sets the noWarnIncludeIf.
2546
*
2547
* @param noWarnIncludeIf the noWarnIncludeIf to set
2548
*/
2549
public void setNoWarnIncludeIf(boolean noWarnIncludeIf) {
2550
this.noWarnIncludeIf = noWarnIncludeIf;
2551
}
2552
2553
/**
2554
* Sets the name of the message class that should be used
2555
*
2556
* @param name the name of the message class
2557
*/
2558
public void setMessageClassName(String name) {
2559
this.msgClassSubstituter.setMessageClassName(name);
2560
this.substituteMsgClass = true;
2561
}
2562
2563
}
2564
2565