Path: blob/master/sourcetools/com.ibm.jpp.preprocessor/com/ibm/jpp/om/ExternalMessagesExtension.java
6004 views
/*******************************************************************************1* Copyright (c) 1999, 2017 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* 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-exception20*******************************************************************************/21package com.ibm.jpp.om;2223import java.io.File;24import java.io.FileNotFoundException;25import java.io.IOException;26import java.io.OutputStream;27import java.io.PrintWriter;28import java.util.Calendar;29import java.util.GregorianCalendar;30import java.util.HashMap;31import java.util.Map;32import java.util.Map.Entry;33import java.util.Properties;34import java.util.Set;35import java.util.TreeMap;3637/**38* This builder extension finds, loads, and writes external messages during the preprocess.39*/40public class ExternalMessagesExtension extends BuilderExtension {4142/**43* The relative path where the external messages should be found.44*/45private String messagesPath = "com/ibm/oti/util";4647/**48* The name of the external messages file.49*/50private static final String messagesFile = "ExternalMessages.properties";5152/**53* If this option is set, create externalMessages file.54*/55private boolean externalMessages = true;5657/**58* If this option is set, messages are inlined.59*/60private boolean inlineMessages = false;6162/**63* If this option is set, message keys are inlined.64*/65private boolean inlineMessageKeys = false;6667/**68* If this option is set, an error is thrown if messages are found.69*/70private boolean foundMessagesError = false;7172/**73* If this option is set, we substitute the name of the class used74* to lookup messages75*/76private String messageClassName = null;7778/**79* The external messages loaded from the output directory. This is only used80* if builder.isIncremental()==true.81*/82private Map<String, String> existingMessages = null;8384/**85* The external messages found during this preprocessor run.86*/87private final Map<String, String> messages = new TreeMap<>();8889/**90* Unconditionally update output files?91*/92private boolean force = false;9394/**95* The extension constructor.96*/97public ExternalMessagesExtension() {98super("msg");99}100101/**102* @see com.ibm.jpp.om.BuilderExtension#validateOptions(Properties)103*/104@Override105public void validateOptions(Properties options) {106//check for the need for externalMessages file, if false it is not created.107if (options.containsKey("msg:externalMessages")) {108this.externalMessages = !options.getProperty("msg:externalMessages").equalsIgnoreCase("false");109}110111// check for the external messages output directory option112if (options.containsKey("msg:outputdir")) {113this.messagesPath = options.getProperty("msg:outputdir");114}115116// check for the inline messages option117if (options.containsKey("msg:inline")) {118this.inlineMessages = options.getProperty("msg:inline").equalsIgnoreCase("true");119}120121// check for the inline keys option122if (options.containsKey("msg:inlinekeys")) {123this.inlineMessageKeys = options.getProperty("msg:inlinekeys").equalsIgnoreCase("true");124}125126// check for the found messages error option127if (options.containsKey("msg:error")) {128this.foundMessagesError = true;129}130131// check for the substitute message class option132if (options.containsKey("msg:messageclass")) {133this.messageClassName = options.getProperty("msg:messageclass");134}135136if (Builder.isForced(options)) {137this.force = true;138}139}140141/**142* @see com.ibm.jpp.om.BuilderExtension#notifyBuildBegin()143*/144@Override145public void notifyBuildBegin() {146if (builder.isIncremental() || builder.hasMultipleSources()) {147loadExistingMessages();148}149}150151/**152* @see com.ibm.jpp.om.BuilderExtension#notifyBuildEnd()153*/154@Override155public void notifyBuildEnd() {156if (externalMessages) {157if (existingMessages != null && (builder.isIncremental() || builder.hasMultipleSources())) {158// add the existing messages to the found messages (unless the159// message was already in the found messages)160for (Map.Entry<String, String> entry : existingMessages.entrySet()) {161if (messages.get(entry.getKey()) == null) {162messages.put(entry.getKey(), entry.getValue());163}164}165}166writeMessages();167}168}169170/**171* Loads the external messages from the output directory before the build.172* This is used for incremental builds to preserve existing messages.173*/174private void loadExistingMessages() {175// load the existing external messages from the output directory176this.existingMessages = new HashMap<>();177File outputFile = getOutputMessageFile();178179try {180loadProperties(this.existingMessages, outputFile);181} catch (FileNotFoundException e) {182// no external messages existed183} catch (IOException e) {184StringBuffer msgBuffer = new StringBuffer("An exception occured while loading the existing external messages from \"");185msgBuffer.append(outputFile);186builder.getLogger().log(msgBuffer.toString(), Logger.SEVERITY_ERROR, e);187}188}189190/**191* Writes the external messages to the output directory after the build.192*/193private void writeMessages() {194if (messages.size() > 0 && !inlineMessages) {195File outputFile = getOutputMessageFile();196/*[PR 115710] FileNotFoundException*/197File parent = outputFile.getParentFile();198199parent.mkdirs();200201if (parent.exists()) {202try (OutputStream output = new PhantomOutputStream(outputFile, force);203PrintWriter put = new PrintWriter(output)) {204/**205* We don't use java.util.Properties.store method, because we want the keys in ascending order in properties file,206* TreeMap is used for this reason207*/208put.println("#");209put.println("# Copyright (c) 2000, " + new GregorianCalendar().get(Calendar.YEAR) + " IBM Corp. and others");210put.println("#");211put.println("# This program and the accompanying materials are made available under");212put.println("# the terms of the Eclipse Public License 2.0 which accompanies this");213put.println("# distribution and is available at https://www.eclipse.org/legal/epl-2.0/");214put.println("# or the Apache License, Version 2.0 which accompanies this distribution and");215put.println("# is available at https://www.apache.org/licenses/LICENSE-2.0.");216put.println("#");217put.println("# This Source Code may also be made available under the following");218put.println("# Secondary Licenses when the conditions for such availability set");219put.println("# forth in the Eclipse Public License, v. 2.0 are satisfied: GNU");220put.println("# General Public License, version 2 with the GNU Classpath");221put.println("# Exception [1] and GNU General Public License, version 2 with the");222put.println("# OpenJDK Assembly Exception [2].");223put.println("#");224put.println("# [1] https://www.gnu.org/software/classpath/license.html");225put.println("# [2] http://openjdk.java.net/legal/assembly-exception.html");226put.println("#");227put.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");228put.println("#");229put.println("# External Messages for EN locale");230Set<Entry<String, String>> entries = messages.entrySet();231StringBuffer buffer = new StringBuffer(200);232for (Entry<String, String> entry : entries) {233dumpString(buffer, entry.getKey(), true);234buffer.append('=');235dumpString(buffer, entry.getValue(), false);236put.println(buffer.toString());237buffer.setLength(0);238}239put.flush();240StringBuffer msgBuffer = new StringBuffer("Messages written to \"");241msgBuffer.append(outputFile);242builder.getLogger().log(msgBuffer.toString(), Logger.SEVERITY_INFO);243} catch (IOException e) {244StringBuffer msgBuffer = new StringBuffer("An exception occured while writing the existing external messages to \"");245msgBuffer.append(outputFile);246builder.getLogger().log(msgBuffer.toString(), Logger.SEVERITY_ERROR, e);247}248}249}250}251252private static void dumpString(StringBuffer buffer, String string, boolean key) {253int i = 0;254int length = string.length();255if (!key) {256while (i < length && string.charAt(i) == ' ') {257buffer.append("\\ ");258i++;259}260}261262for (; i < length; i++) {263char ch = string.charAt(i);264switch (ch) {265case '\t':266buffer.append("\\t");267break;268case '\n':269buffer.append("\\n");270break;271case '\f':272buffer.append("\\f");273break;274case '\r':275buffer.append("\\r");276break;277default:278if ("\\#!=:".indexOf(ch) >= 0 || (key && ch == ' ')) {279buffer.append('\\');280}281282if (ch >= ' ' && ch <= '~') {283buffer.append(ch);284} else {285String hex = Integer.toHexString(ch);286int hexLength = hex.length();287buffer.append("\\u");288for (int j = 0; j < 4 - hexLength; j++) {289buffer.append("0");290}291buffer.append(hex);292}293break;294}295}296}297298/**299* Returns the File for external messages in the output directory.300*/301private File getOutputMessageFile() {302return new File(new File(builder.getOutputDir(), messagesPath), messagesFile);303}304305/**306* @see com.ibm.jpp.om.BuilderExtension#notifyConfigurePreprocessor(JavaPreprocessor)307*/308@Override309public void notifyConfigurePreprocessor(JavaPreprocessor preprocessor) {310preprocessor.setExternalMessages(this.messages);311/*[PR 120535] inlineMessageKeys and inlineMessageKeys conflict*/312if (this.inlineMessages) {313preprocessor.setInlineMessages(this.inlineMessages);314} else if (this.inlineMessageKeys) {315preprocessor.setInlineMessageKeys(this.inlineMessageKeys);316}317if (messageClassName != null) {318preprocessor.setMessageClassName(this.messageClassName);319}320}321322/**323* @see com.ibm.jpp.om.BuilderExtension#notifyBuildFileEnd(File, File,324* String)325*/326@Override327public void notifyBuildFileEnd(File sourceFile, File outputFile, String relativePath) {328if (foundMessagesError && messages.size() > 0) {329// if a message was found in this file write an error330StringBuffer msgBuffer = new StringBuffer("A preprocessor MSG command was found in the file \"");331msgBuffer.append(relativePath);332msgBuffer.append("\"");333builder.getLogger().log(msgBuffer.toString(), Logger.SEVERITY_ERROR);334messages.clear();335}336}337338}339340341