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/ExternalMessagesExtension.java
6004 views
1
/*******************************************************************************
2
* Copyright (c) 1999, 2017 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.File;
25
import java.io.FileNotFoundException;
26
import java.io.IOException;
27
import java.io.OutputStream;
28
import java.io.PrintWriter;
29
import java.util.Calendar;
30
import java.util.GregorianCalendar;
31
import java.util.HashMap;
32
import java.util.Map;
33
import java.util.Map.Entry;
34
import java.util.Properties;
35
import java.util.Set;
36
import java.util.TreeMap;
37
38
/**
39
* This builder extension finds, loads, and writes external messages during the preprocess.
40
*/
41
public class ExternalMessagesExtension extends BuilderExtension {
42
43
/**
44
* The relative path where the external messages should be found.
45
*/
46
private String messagesPath = "com/ibm/oti/util";
47
48
/**
49
* The name of the external messages file.
50
*/
51
private static final String messagesFile = "ExternalMessages.properties";
52
53
/**
54
* If this option is set, create externalMessages file.
55
*/
56
private boolean externalMessages = true;
57
58
/**
59
* If this option is set, messages are inlined.
60
*/
61
private boolean inlineMessages = false;
62
63
/**
64
* If this option is set, message keys are inlined.
65
*/
66
private boolean inlineMessageKeys = false;
67
68
/**
69
* If this option is set, an error is thrown if messages are found.
70
*/
71
private boolean foundMessagesError = false;
72
73
/**
74
* If this option is set, we substitute the name of the class used
75
* to lookup messages
76
*/
77
private String messageClassName = null;
78
79
/**
80
* The external messages loaded from the output directory. This is only used
81
* if builder.isIncremental()==true.
82
*/
83
private Map<String, String> existingMessages = null;
84
85
/**
86
* The external messages found during this preprocessor run.
87
*/
88
private final Map<String, String> messages = new TreeMap<>();
89
90
/**
91
* Unconditionally update output files?
92
*/
93
private boolean force = false;
94
95
/**
96
* The extension constructor.
97
*/
98
public ExternalMessagesExtension() {
99
super("msg");
100
}
101
102
/**
103
* @see com.ibm.jpp.om.BuilderExtension#validateOptions(Properties)
104
*/
105
@Override
106
public void validateOptions(Properties options) {
107
//check for the need for externalMessages file, if false it is not created.
108
if (options.containsKey("msg:externalMessages")) {
109
this.externalMessages = !options.getProperty("msg:externalMessages").equalsIgnoreCase("false");
110
}
111
112
// check for the external messages output directory option
113
if (options.containsKey("msg:outputdir")) {
114
this.messagesPath = options.getProperty("msg:outputdir");
115
}
116
117
// check for the inline messages option
118
if (options.containsKey("msg:inline")) {
119
this.inlineMessages = options.getProperty("msg:inline").equalsIgnoreCase("true");
120
}
121
122
// check for the inline keys option
123
if (options.containsKey("msg:inlinekeys")) {
124
this.inlineMessageKeys = options.getProperty("msg:inlinekeys").equalsIgnoreCase("true");
125
}
126
127
// check for the found messages error option
128
if (options.containsKey("msg:error")) {
129
this.foundMessagesError = true;
130
}
131
132
// check for the substitute message class option
133
if (options.containsKey("msg:messageclass")) {
134
this.messageClassName = options.getProperty("msg:messageclass");
135
}
136
137
if (Builder.isForced(options)) {
138
this.force = true;
139
}
140
}
141
142
/**
143
* @see com.ibm.jpp.om.BuilderExtension#notifyBuildBegin()
144
*/
145
@Override
146
public void notifyBuildBegin() {
147
if (builder.isIncremental() || builder.hasMultipleSources()) {
148
loadExistingMessages();
149
}
150
}
151
152
/**
153
* @see com.ibm.jpp.om.BuilderExtension#notifyBuildEnd()
154
*/
155
@Override
156
public void notifyBuildEnd() {
157
if (externalMessages) {
158
if (existingMessages != null && (builder.isIncremental() || builder.hasMultipleSources())) {
159
// add the existing messages to the found messages (unless the
160
// message was already in the found messages)
161
for (Map.Entry<String, String> entry : existingMessages.entrySet()) {
162
if (messages.get(entry.getKey()) == null) {
163
messages.put(entry.getKey(), entry.getValue());
164
}
165
}
166
}
167
writeMessages();
168
}
169
}
170
171
/**
172
* Loads the external messages from the output directory before the build.
173
* This is used for incremental builds to preserve existing messages.
174
*/
175
private void loadExistingMessages() {
176
// load the existing external messages from the output directory
177
this.existingMessages = new HashMap<>();
178
File outputFile = getOutputMessageFile();
179
180
try {
181
loadProperties(this.existingMessages, outputFile);
182
} catch (FileNotFoundException e) {
183
// no external messages existed
184
} catch (IOException e) {
185
StringBuffer msgBuffer = new StringBuffer("An exception occured while loading the existing external messages from \"");
186
msgBuffer.append(outputFile);
187
builder.getLogger().log(msgBuffer.toString(), Logger.SEVERITY_ERROR, e);
188
}
189
}
190
191
/**
192
* Writes the external messages to the output directory after the build.
193
*/
194
private void writeMessages() {
195
if (messages.size() > 0 && !inlineMessages) {
196
File outputFile = getOutputMessageFile();
197
/*[PR 115710] FileNotFoundException*/
198
File parent = outputFile.getParentFile();
199
200
parent.mkdirs();
201
202
if (parent.exists()) {
203
try (OutputStream output = new PhantomOutputStream(outputFile, force);
204
PrintWriter put = new PrintWriter(output)) {
205
/**
206
* We don't use java.util.Properties.store method, because we want the keys in ascending order in properties file,
207
* TreeMap is used for this reason
208
*/
209
put.println("#");
210
put.println("# Copyright (c) 2000, " + new GregorianCalendar().get(Calendar.YEAR) + " IBM Corp. and others");
211
put.println("#");
212
put.println("# This program and the accompanying materials are made available under");
213
put.println("# the terms of the Eclipse Public License 2.0 which accompanies this");
214
put.println("# distribution and is available at https://www.eclipse.org/legal/epl-2.0/");
215
put.println("# or the Apache License, Version 2.0 which accompanies this distribution and");
216
put.println("# is available at https://www.apache.org/licenses/LICENSE-2.0.");
217
put.println("#");
218
put.println("# This Source Code may also be made available under the following");
219
put.println("# Secondary Licenses when the conditions for such availability set");
220
put.println("# forth in the Eclipse Public License, v. 2.0 are satisfied: GNU");
221
put.println("# General Public License, version 2 with the GNU Classpath");
222
put.println("# Exception [1] and GNU General Public License, version 2 with the");
223
put.println("# OpenJDK Assembly Exception [2].");
224
put.println("#");
225
put.println("# [1] https://www.gnu.org/software/classpath/license.html");
226
put.println("# [2] http://openjdk.java.net/legal/assembly-exception.html");
227
put.println("#");
228
put.println("# 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");
229
put.println("#");
230
put.println("# External Messages for EN locale");
231
Set<Entry<String, String>> entries = messages.entrySet();
232
StringBuffer buffer = new StringBuffer(200);
233
for (Entry<String, String> entry : entries) {
234
dumpString(buffer, entry.getKey(), true);
235
buffer.append('=');
236
dumpString(buffer, entry.getValue(), false);
237
put.println(buffer.toString());
238
buffer.setLength(0);
239
}
240
put.flush();
241
StringBuffer msgBuffer = new StringBuffer("Messages written to \"");
242
msgBuffer.append(outputFile);
243
builder.getLogger().log(msgBuffer.toString(), Logger.SEVERITY_INFO);
244
} catch (IOException e) {
245
StringBuffer msgBuffer = new StringBuffer("An exception occured while writing the existing external messages to \"");
246
msgBuffer.append(outputFile);
247
builder.getLogger().log(msgBuffer.toString(), Logger.SEVERITY_ERROR, e);
248
}
249
}
250
}
251
}
252
253
private static void dumpString(StringBuffer buffer, String string, boolean key) {
254
int i = 0;
255
int length = string.length();
256
if (!key) {
257
while (i < length && string.charAt(i) == ' ') {
258
buffer.append("\\ ");
259
i++;
260
}
261
}
262
263
for (; i < length; i++) {
264
char ch = string.charAt(i);
265
switch (ch) {
266
case '\t':
267
buffer.append("\\t");
268
break;
269
case '\n':
270
buffer.append("\\n");
271
break;
272
case '\f':
273
buffer.append("\\f");
274
break;
275
case '\r':
276
buffer.append("\\r");
277
break;
278
default:
279
if ("\\#!=:".indexOf(ch) >= 0 || (key && ch == ' ')) {
280
buffer.append('\\');
281
}
282
283
if (ch >= ' ' && ch <= '~') {
284
buffer.append(ch);
285
} else {
286
String hex = Integer.toHexString(ch);
287
int hexLength = hex.length();
288
buffer.append("\\u");
289
for (int j = 0; j < 4 - hexLength; j++) {
290
buffer.append("0");
291
}
292
buffer.append(hex);
293
}
294
break;
295
}
296
}
297
}
298
299
/**
300
* Returns the File for external messages in the output directory.
301
*/
302
private File getOutputMessageFile() {
303
return new File(new File(builder.getOutputDir(), messagesPath), messagesFile);
304
}
305
306
/**
307
* @see com.ibm.jpp.om.BuilderExtension#notifyConfigurePreprocessor(JavaPreprocessor)
308
*/
309
@Override
310
public void notifyConfigurePreprocessor(JavaPreprocessor preprocessor) {
311
preprocessor.setExternalMessages(this.messages);
312
/*[PR 120535] inlineMessageKeys and inlineMessageKeys conflict*/
313
if (this.inlineMessages) {
314
preprocessor.setInlineMessages(this.inlineMessages);
315
} else if (this.inlineMessageKeys) {
316
preprocessor.setInlineMessageKeys(this.inlineMessageKeys);
317
}
318
if (messageClassName != null) {
319
preprocessor.setMessageClassName(this.messageClassName);
320
}
321
}
322
323
/**
324
* @see com.ibm.jpp.om.BuilderExtension#notifyBuildFileEnd(File, File,
325
* String)
326
*/
327
@Override
328
public void notifyBuildFileEnd(File sourceFile, File outputFile, String relativePath) {
329
if (foundMessagesError && messages.size() > 0) {
330
// if a message was found in this file write an error
331
StringBuffer msgBuffer = new StringBuffer("A preprocessor MSG command was found in the file \"");
332
msgBuffer.append(relativePath);
333
msgBuffer.append("\"");
334
builder.getLogger().log(msgBuffer.toString(), Logger.SEVERITY_ERROR);
335
messages.clear();
336
}
337
}
338
339
}
340
341