1 /* 2 * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package build.tools.spp; 27 28 import java.io.FileInputStream; 29 import java.io.FileOutputStream; 30 import java.util.*; 31 import java.util.regex.*; 32 33 /* 34 * Spp: A simple regex-based stream preprocessor based on Mark Reinhold's 35 * sed-based spp.sh 36 * 37 * Usage: 38 * java build.tools.spp.Spp [-be] [-nel] [-Kkey] -Dvar=value ... -iin -oout 39 * 40 * If -nel is declared then empty lines will not be substituted for lines of 41 * text in the template that do not appear in the output. 42 * 43 * Meaningful only at beginning of line, works with any number of keys: 44 * 45 * #if[key] Includes text between #if/#end if -Kkey specified, 46 * #else[key] otherwise changes text to blank lines; key test 47 * #end[key] may be negated by prefixing !, e.g., #if[!key] 48 * 49 * #begin If -be is specified then lines up to and including 50 * #end #begin, and from #end to EOF, are deleted 51 * 52 * #warn Changed into warning that file is generated 53 * 54 * // ## Changed into blank line 55 * 56 * Meaningful anywhere in line 57 * 58 * {#if[key]?yes} Expands to yes if -Kkey specified 59 * {#if[key]?yes:no} Expands to yes if -Kkey, otherwise no 60 * {#if[!key]?yes} Expands to yes if -Kother 61 * {#if[!key]?yes:no} Expands to yes if -Kother, otherwise no 62 * $var$ Expands to value if -Dvar=value given 63 * 64 * yes, no must not contain whitespace 65 * 66 * @author Xueming Shen 67 */ 68 69 public class Spp { main(String args[])70 public static void main(String args[]) throws Exception { 71 Map<String, String> vars = new HashMap<>(); 72 Set<String> keys = new HashSet<>(); 73 boolean be = false; 74 boolean el = true; 75 String inputFile = null; 76 String outputFile = null; 77 78 for (String arg:args) { 79 if (arg.startsWith("-D")) { 80 int i = arg.indexOf('='); 81 vars.put(arg.substring(2, i),arg.substring(i+1)); 82 } else if (arg.startsWith("-K")) { 83 keys.add(arg.substring(2)); 84 } else if (arg.startsWith("-i")) { 85 inputFile = arg.substring(2); 86 } else if (arg.startsWith("-o")) { 87 outputFile = arg.substring(2); 88 } else if ("-be".equals(arg)) { 89 be = true; 90 } else if ("-nel".equals(arg)) { 91 el = false; 92 } else { 93 System.err.println("Usage: java build.tools.spp.Spp [-be] [-nel] [-Kkey] -Dvar=value ... <in >out"); 94 System.exit(-1); 95 } 96 } 97 98 StringBuffer out = new StringBuffer(); 99 new Spp().spp(new Scanner(new FileInputStream(inputFile)), 100 out, "", 101 keys, vars, be, el, 102 false); 103 new FileOutputStream(outputFile, true).write(out.toString().getBytes()); 104 } 105 106 static final String LNSEP = System.getProperty("line.separator"); 107 static final String KEY = "([a-zA-Z0-9]+)"; 108 static final String VAR = "([a-zA-Z0-9_\\-]+)"; 109 static final String TEXT = "([\\p{Print}&&[^{#:}]]+)"; 110 111 static final int GN_NOT = 1; 112 static final int GN_KEY = 2; 113 static final int GN_YES = 3; 114 static final int GN_NO = 5; 115 static final int GN_VAR = 6; 116 117 final Matcher ifkey = Pattern.compile("^#if\\[(!)?" + KEY + "\\]").matcher(""); 118 final Matcher elsekey = Pattern.compile("^#else\\[(!)?" + KEY + "\\]").matcher(""); 119 final Matcher endkey = Pattern.compile("^#end\\[(!)?" + KEY + "\\]").matcher(""); 120 final Matcher vardef = Pattern.compile("\\{#if\\[(!)?" + KEY + "\\]\\?" + TEXT + "(:"+ TEXT + ")?\\}|\\$" + VAR + "\\$").matcher(""); 121 final Matcher vardef2 = Pattern.compile("\\$" + VAR + "\\$").matcher(""); 122 append(StringBuffer buf, String ln, Set<String> keys, Map<String, String> vars)123 void append(StringBuffer buf, String ln, 124 Set<String> keys, Map<String, String> vars) { 125 vardef.reset(ln); 126 while (vardef.find()) { 127 String repl = ""; 128 if (vardef.group(GN_VAR) != null) 129 repl = vars.get(vardef.group(GN_VAR)); 130 else { 131 boolean test = keys.contains(vardef.group(GN_KEY)); 132 if (vardef.group(GN_NOT) != null) 133 test = !test; 134 repl = test?vardef.group(GN_YES):vardef.group(GN_NO); 135 if (repl == null) 136 repl = ""; 137 else { // embedded $var$ 138 while (vardef2.reset(repl).find()) { 139 repl = vardef2.replaceFirst(vars.get(vardef2.group(1))); 140 } 141 } 142 } 143 if (repl == null) { 144 System.err.println("Error: undefined variable in line " + ln); 145 System.exit(-1); 146 } 147 vardef.appendReplacement(buf, repl); 148 } 149 vardef.appendTail(buf); 150 } 151 152 // return true if #end[key], #end or EOF reached spp(Scanner in, StringBuffer buf, String key, Set<String> keys, Map<String, String> vars, boolean be, boolean el, boolean skip)153 boolean spp(Scanner in, StringBuffer buf, String key, 154 Set<String> keys, Map<String, String> vars, 155 boolean be, boolean el, boolean skip) { 156 while (in.hasNextLine()) { 157 String ln = in.nextLine(); 158 if (be) { 159 if (ln.startsWith("#begin")) { 160 buf.setLength(0); //clean up to this line 161 continue; 162 } 163 if (ln.equals("#end")) { 164 while (in.hasNextLine()) 165 in.nextLine(); 166 return true; //discard the rest to EOF 167 } 168 } 169 if (ifkey.reset(ln).find()) { 170 String k = ifkey.group(GN_KEY); 171 boolean test = keys.contains(k); 172 if (ifkey.group(GN_NOT) != null) 173 test = !test; 174 if (el) buf.append(LNSEP); 175 if (!spp(in, buf, k, keys, vars, be, el, skip || !test)) { 176 spp(in, buf, k, keys, vars, be, el, skip || test); 177 } 178 continue; 179 } 180 if (elsekey.reset(ln).find()) { 181 if (!key.equals(elsekey.group(GN_KEY))) { 182 throw new Error("Mis-matched #if-else-end at line <" + ln + ">"); 183 } 184 if (el) buf.append(LNSEP); 185 return false; 186 } 187 if (endkey.reset(ln).find()) { 188 if (!key.equals(endkey.group(GN_KEY))) { 189 throw new Error("Mis-matched #if-else-end at line <" + ln + ">"); 190 } 191 if (el) buf.append(LNSEP); 192 return true; 193 } 194 if (ln.startsWith("#warn")) { 195 ln = "// -- This file was mechanically generated: Do not edit! -- //"; 196 } else if (ln.trim().startsWith("// ##")) { 197 ln = ""; 198 } 199 if (!skip) { 200 append(buf, ln, keys, vars); 201 if (!el) buf.append(LNSEP); 202 } 203 if (el) buf.append(LNSEP); 204 } 205 return true; 206 } 207 } 208