1 /* 2 * Copyright (c) 2002, 2013, 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 package build.tools.generatenimbus; 26 27 import java.io.*; 28 import java.util.HashMap; 29 import java.util.Map; 30 import javax.xml.bind.JAXBContext; 31 import javax.xml.bind.Unmarshaller; 32 33 /** 34 * Generates the various Java artifacts based on a SynthModel. 35 * <p/> 36 * Generated source files are split up among two different locations. There are those source files that are meant to be 37 * edited (generally, only the LookAndFeel class itself) and those that are autogenerated (everything else). 38 * <p/> 39 * All autogenerated files are placed in "buildPackageRoot" and are package private. A LAF author (one who has access to 40 * the generated sources) will be able to access any of the generated classes. Those referencing the library, however, 41 * will only be able to access the main LookAndFeel class itself (since everything else is package private). 42 * 43 * @author Richard Bair 44 * @author Jasper Potts 45 */ 46 public class Generator { 47 private static Generator instance; 48 49 /** A map of variables that are used for variable substitution in the template files. */ 50 private Map<String, String> variables; 51 private boolean full = false; 52 private File buildPackageRoot; 53 private String packageNamePrefix; 54 private String lafName; 55 private SynthModel model; 56 57 /** 58 * MAIN APPLICATION 59 * <p/> 60 * This is for using the generator as part of the java build process 61 * 62 * @param args The commandline arguments 63 */ main(String[] args)64 public static void main(String[] args) throws Exception { 65 if (args.length == 0 || (args.length % 2) != 0) { 66 System.out.println("Usage: generator [-options]\n" + 67 " -full <true|false> True if we should build the whole LAF or false for building just states and painters.\n" + 68 " -skinFile <value> Path to the skin.laf file for the LAF to be generated from.\n" + 69 " -buildDir <value> The directory beneath which the build-controlled artifacts (such as the Painters) should\n" + 70 " be placed. This is the root directory beneath which the necessary packages and source\n" + 71 " files will be created.\n" + 72 " -resourcesDir <value> The resources directory containing templates and images.\n" + 73 " -packagePrefix <value> The package name associated with this synth look and feel. For example,\n" + 74 " \"org.mypackage.mylaf\"\n" + 75 " -lafName <value> The name of the laf, such as \"MyLAF\".\n"); 76 } else { 77 boolean full = false; 78 File skinFile = new File(System.getProperty("user.dir")); 79 File buildDir = new File(System.getProperty("user.dir")); 80 File resourcesDir = new File(System.getProperty("user.dir")); 81 String packagePrefix = "org.mypackage.mylaf"; 82 String lafName = "MyLAF"; 83 for (int i = 0; i < args.length; i += 2) { 84 String key = args[i].trim().toLowerCase(); 85 String value = args[i + 1].trim(); 86 if ("-full".equals(key)) { 87 full = Boolean.parseBoolean(value); 88 } else if ("-skinfile".equals(key)) { 89 skinFile = new File(value); 90 } else if ("-builddir".equals(key)) { 91 buildDir = new File(value); 92 } else if ("-resourcesdir".equals(key)) { 93 resourcesDir = new File(value); 94 } else if ("-packageprefix".equals(key)) { 95 packagePrefix = value; 96 } else if ("-lafname".equals(key)) { 97 lafName = value; 98 } 99 } 100 System.out.println("### GENERATING LAF CODE ################################"); 101 System.out.println(" full :" + full); 102 System.out.println(" skinFile :" + skinFile.getAbsolutePath()); 103 System.out.println(" buildDir :" + buildDir.getAbsolutePath()); 104 System.out.println(" resourcesDir :" + resourcesDir.getAbsolutePath()); 105 System.out.println(" packagePrefix :" +packagePrefix); 106 System.out.println(" lafName :" +lafName); 107 108 JAXBContext ctx = JAXBContext.newInstance("build.tools.generatenimbus"); 109 Unmarshaller u = ctx.createUnmarshaller(); 110 SynthModel model = (SynthModel) u.unmarshal(skinFile); 111 Generator.init(full, buildDir, packagePrefix, lafName, model); 112 Generator.getInstance().generate(); 113 } 114 } 115 116 /** 117 * Creates a new Generator, capable of outputting the source code artifacts related to a given SynthModel. It is 118 * capable of generating the one-time artifacts in addition to the regeneration of build-controlled artifacts. 119 * 120 * @param full True if we should build the whole LAF or false for building just states and painters. 121 * @param buildDir The directory beneath which the build-controlled artifacts (such as the Painters) should 122 * be placed. This is the root directory beneath which the necessary packages and source 123 * files will be created. 124 * @param srcDir The directory beneath which the normal user-controlled artifacts (such as the core 125 * LookAndFeel file) should be placed. These are one-time generated files. This is the root 126 * directory beneath which the necessary packages and source files will be created. 127 * @param packageNamePrefix The package name associated with this synth look and feel. For example, 128 * org.mypackage.mylaf 129 * @param lafName The name of the laf, such as MyLAF. 130 * @param model The actual SynthModel to base these generated files on. 131 */ Generator(boolean full, File buildDir, String packageNamePrefix, String lafName, SynthModel model)132 private Generator(boolean full, File buildDir, 133 String packageNamePrefix, String lafName, SynthModel model) { 134 this.full = full; 135 //validate the input variables 136 if (packageNamePrefix == null) { 137 throw new IllegalArgumentException("You must specify a package name prefix"); 138 } 139 if (buildDir == null) { 140 throw new IllegalArgumentException("You must specify the build directory"); 141 } 142 if (model == null) { 143 throw new IllegalArgumentException("You must specify the SynthModel"); 144 } 145 if (lafName == null) { 146 throw new IllegalArgumentException("You must specify the name of the look and feel"); 147 } 148 149 //construct the map which is used to do variable substitution of the template 150 //files 151 variables = new HashMap<String, String>(); 152 variables.put("PACKAGE", packageNamePrefix); 153 variables.put("LAF_NAME", lafName); 154 155 //generate and save references to the package-root directories. 156 //(That is, given the buildDir and srcDir, generate references to the 157 //org.mypackage.mylaf subdirectories) 158 buildPackageRoot = new File(buildDir, packageNamePrefix.replaceAll("\\.", "\\/")); 159 buildPackageRoot.mkdirs(); 160 161 //save the variables 162 this.packageNamePrefix = packageNamePrefix; 163 this.lafName = lafName; 164 this.model = model; 165 } 166 init(boolean full, File buildDir, String packageNamePrefix, String lafName, SynthModel model)167 public static void init(boolean full, File buildDir, 168 String packageNamePrefix, String lafName, SynthModel model) { 169 instance = new Generator(full, buildDir, packageNamePrefix, lafName, model); 170 model.initStyles(); 171 } 172 getInstance()173 public static Generator getInstance() { 174 return instance; 175 } 176 getVariables()177 public static Map<String, String> getVariables() { 178 return new HashMap<String, String>(instance.variables); 179 } 180 generate()181 public void generate() { 182 if (full) { 183 //create the LookAndFeel file 184 writeSrcFileImpl("LookAndFeel", variables, lafName + "LookAndFeel"); 185 186 writeSrcFileImpl("AbstractRegionPainter", variables); 187 writeSrcFileImpl("BlendingMode", variables); 188 writeSrcFileImpl("SynthPainterImpl", variables); 189 writeSrcFileImpl("IconImpl", variables, lafName + "Icon.java"); 190 writeSrcFileImpl("StyleImpl", variables, lafName + "Style.java"); 191 writeSrcFileImpl("Effect", variables); 192 writeSrcFileImpl("EffectUtils", variables); 193 writeSrcFileImpl("ShadowEffect", variables); 194 writeSrcFileImpl("DropShadowEffect", variables); 195 writeSrcFileImpl("InnerShadowEffect", variables); 196 writeSrcFileImpl("InnerGlowEffect", variables); 197 writeSrcFileImpl("OuterGlowEffect", variables); 198 writeSrcFileImpl("State", variables); 199 writeSrcFileImpl("ImageCache", variables); 200 writeSrcFileImpl("ImageScalingHelper", variables); 201 } 202 //next, populate the first set of ui defaults based on what is in the 203 //various palettes of the synth model 204 StringBuilder defBuffer = new StringBuilder(); 205 StringBuilder styleBuffer = new StringBuilder(); 206 model.write(defBuffer, styleBuffer, packageNamePrefix); 207 208 Map<String, String> vars = getVariables(); 209 vars.put("UI_DEFAULT_INIT", defBuffer.toString()); 210 vars.put("STYLE_INIT", styleBuffer.toString()); 211 writeSrcFile("Defaults", vars, lafName + "Defaults"); 212 } 213 writeSrcFileImpl(String name, Map<String, String> variables)214 private void writeSrcFileImpl(String name, Map<String, String> variables) { 215 writeSrcFileImpl(name, variables, name); 216 } 217 writeSrcFileImpl(String templateName, Map<String, String> variables, String outputName)218 private void writeSrcFileImpl(String templateName, 219 Map<String, String> variables, String outputName) { 220 PrintWriter out = null; 221 try { 222 InputStream stream = getClass().getResourceAsStream( 223 "resources/" + templateName + ".template"); 224 TemplateReader in = new TemplateReader(variables, stream); 225 226 out = new PrintWriter(new File(buildPackageRoot, outputName + ".java")); 227 String line = in.readLine(); 228 while (line != null) { 229 out.println(line); 230 line = in.readLine(); 231 } 232 } catch (IOException e) { 233 throw new RuntimeException("IOException in writer", e); 234 } finally { 235 if (out != null) out.close(); 236 } 237 } 238 writeSrcFile(String templateName, Map<String, String> variables, String outputName)239 public static void writeSrcFile(String templateName, 240 Map<String, String> variables, String outputName) { 241 instance.writeSrcFileImpl(templateName, variables, outputName); 242 } 243 244 /** A BufferedReader implementation that automatically performs 245 * string replacements as needed. 246 */ 247 private static final class TemplateReader extends BufferedReader { 248 private Map<String, String> variables; 249 TemplateReader(Map<String, String> variables, InputStream template)250 TemplateReader(Map<String, String> variables, InputStream template) { 251 super(new InputStreamReader(template)); 252 this.variables = variables; 253 } 254 readLine()255 @Override public String readLine() throws IOException { 256 return substituteVariables(super.readLine()); 257 } 258 substituteVariables(String input)259 private String substituteVariables(String input) { 260 if (input == null) return null; 261 for (Map.Entry<String, String> variable : variables.entrySet()) { 262 input = input.replace("${" + variable.getKey() + "}", variable.getValue()); 263 } 264 return input; 265 } 266 } 267 } 268