/******************************************************************************* * * Copyright 2011-2014 Spiffy UI Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ package org.spiffyui.build; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Reader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Properties; /** * This is generic task for generating HTML properties files */ public final class HTMLPropertiesUtil { private HTMLPropertiesUtil() { /* Our constructor is private */ } /** * We need to figure out the locale of the file based on the file name. The file can be * something like foo.html, foo_fr.html foo_fr-FR.html, or foo_fr_FR.html. We parse that * name and create a locale object based on the language and country codes in the name * * @param name the name of the file * * @return the locale for this file name */ private static Locale findLocale(String name) { if (name == null || name.length() < 4) { return null; } name = name.substring(0, name.lastIndexOf('.')); String country = null; String language = null; if (name.length() > 7 && (name.charAt(name.length() - 6) == '-' || name.charAt(name.length() - 6) == '_')) { language = name.substring(name.length() - 5, name.length() - 3); } if (name.length() > 4 && (name.charAt(name.length() - 3) == '-' || name.charAt(name.length() - 3) == '_')) { if (language == null) { /* Then they specified a language without a country */ language = name.substring(name.length() - 2, name.length()); } else { /* Then we already have the language and this is the country */ country = name.substring(name.length() - 2, name.length()); } } if (language == null && country == null) { /* In this case there was no locale information and we return null to indicate this is the default locale. */ return null; } else if (country == null) { /* In this case they have a language code, but no country code. This means we want a locale like French or English with no country code. */ return new Locale(language); } else { /* This case is where there was a language and a country */ return new Locale(language, country); } } private static Properties getProperties(Locale locale, HashMap<String, Properties> props) { String key = null; if (locale == null) { key = ""; } else { key = "_" + locale.toString(); } if (!props.containsKey(key)) { props.put(key, new Properties()); } return props.get(key); } /** * <p> * Generate the HTML properties file based on the input source files * </p> * * <p> * GWT can server strings stored in GWT Java files and in Properties files if you use a * Messages class. These work well for short strings in forms, but aren't well suited to * longer HTML strings. * </p> * * <p> * This task takes a set of HTML files and creates a GWT Messages class and properties * files where you can access these strings from a GWT class. * </p> * * <p> * This task also supports localizations by looking at the locale in the file name. For * example, a file named myFile.html is assumed to be English and a file named myFile_fr.html * is stored as French. This task hooks into the GWT localization framework in standard * GWT. * </p> * * @param files the HTML source files * @param destinationFile * the destination properties file * @param packageName * the name of the package for the Java file. If the package * isn't specified the Java file won't be generated. * * @throws IOException * if there is an error writing the file */ public static void generatePropertiesFiles(final List<File> files, final File destinationFile, final String packageName) throws IOException { if (destinationFile == null) { throw new IllegalArgumentException("Must specify a Properties file destination"); } if (files == null || files.size() == 0) { /* If there are no input files then there is nothing to do */ return; } HashMap<String, Properties> props = new HashMap<String, Properties>(); ArrayList<String> methods = new ArrayList<String>(); for (File f : files) { Reader in = null; try { in = new InputStreamReader(new FileInputStream(f), "UTF-8"); StringBuffer sb = new StringBuffer(); int c = -1; while ((c = in.read()) > -1) { if (c == '\'') { sb.append("'"); } else if (c == '{' || c == '}') { sb.append('\''); sb.append((char) c); sb.append('\''); } else { sb.append((char) c); } } Locale loc = findLocale(f.getName()); String name = f.getName(); if (loc != null) { /* Then we want to take the locale out of the file name */ name = name.substring(0, name.lastIndexOf('.') - loc.toString().length() - 1) + name.substring(name.lastIndexOf('.'), name.length()); } if (!methods.contains(name)) { methods.add(name); } getProperties(loc, props).setProperty(name.replace(' ', '_').replace('.', '_'), sb.toString()); } finally { if (in != null) { in.close(); } } if (packageName != null) { File javaFile = new File(destinationFile.getParentFile(), destinationFile.getName().substring(0, destinationFile.getName().lastIndexOf('.')) + ".java"); generateJavaFile(methods, javaFile, packageName); } } /* Now we need to write out all the properties files */ for (String loc : props.keySet()) { PrintWriter out = null; try { String name = destinationFile.getName(); name = name.substring(0, name.lastIndexOf('.')) + loc + name.substring(name.lastIndexOf('.'), name.length()); out = new PrintWriter(new OutputStreamWriter(new FileOutputStream( new File(destinationFile.getParentFile(), name)), "UTF-8")); props.get(loc).store(out, ""); } finally { if (out != null) { out.close(); } } } } /** * <p> * Generate the HTML properties file based on the input source files * </p> * * <p> * GWT can server strings stored in GWT Java files and in Properties files if you use a * Messages class. These work well for short strings in forms, but aren't well suited to * longer HTML strings. * </p> * * <p> * This task takes a set of HTML files and creates a GWT Messages class and properties * files where you can access these strings from a GWT class. * </p> * * <p> * This task also supports localizations by looking at the locale in the file name. For * example, a file named myFile.html is assumed to be English and a file named myFile_fr.html * is stored as French. This task hooks into the GWT localization framework in standard * GWT. * </p> * * @param files the HTML source files * @param destinationFile * the destination properties file * @param packageName * the name of the package for the Java file. If the package * isn't specified the Java file won't be generated. * * @throws IOException * if there is an error writing the file */ public static void generateEmptyPropertiesFiles(final List<File> files, final File destinationFile, final String packageName) throws IOException { if (destinationFile == null) { throw new IllegalArgumentException("Must specify a Properties file destination"); } if (files == null || files.size() == 0) { /* If there are no input files then there is nothing to do */ return; } HashMap<String, Properties> props = new HashMap<String, Properties>(); ArrayList<String> methods = new ArrayList<String>(); for (File f : files) { Locale loc = findLocale(f.getName()); String name = f.getName(); if (loc != null) { /* Then we want to take the locale out of the file name */ name = name.substring(0, name.lastIndexOf('.') - loc.toString().length() - 1) + name.substring(name.lastIndexOf('.'), name.length()); } if (!methods.contains(name)) { methods.add(name); } getProperties(loc, props).setProperty(name.replace(' ', '_').replace('.', '_'), " "); if (packageName != null) { File javaFile = new File(destinationFile.getParentFile(), destinationFile.getName().substring(0, destinationFile.getName().lastIndexOf('.')) + ".java"); generateJavaFile(methods, javaFile, packageName); } } /* Now we need to write out all the properties files */ for (String loc : props.keySet()) { PrintWriter out = null; try { String name = destinationFile.getName(); name = name.substring(0, name.lastIndexOf('.')) + loc + name.substring(name.lastIndexOf('.'), name.length()); out = new PrintWriter(new OutputStreamWriter(new FileOutputStream( new File(destinationFile.getParentFile(), name)), "UTF-8")); props.get(loc).store(out, ""); } finally { if (out != null) { out.close(); } } } } private static final String START = "import com.google.gwt.i18n.client.Messages;\n\n" + "/**\n" + " * This is a generated localized message bundle for accessing HTML string\n" + " *\n" + " */\n" + "public interface "; /** * The message bundle we need has a set of properties files and a single Java file. * This method handles generating that Java file with a separate method for each * properties file. * * @param methods the list of methods to add * @param destinationFile * the destination file to write the Java file to * @param packageName * the package name of the Java file * * @throws IOException if there is an exception writing the file */ private static void generateJavaFile(final List<String> methods, final File destinationFile, final String packageName) throws IOException { PrintWriter out = null; try { out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(destinationFile), "UTF-8")); /* Output the package name */ out.write("package " + packageName + ";\n\n"); /* Write out the import and the class declaration */ out.write(START); /* Now the class name */ out.write(destinationFile.getName().substring(0, destinationFile.getName().lastIndexOf('.'))); out.write(" extends Messages {\n"); /* Write out a method for each file */ for (String m : methods) { out.write(" public String " + m.replace('.', '_') + "();\n"); } /* Write out the ending curly brace */ out.write("}\n"); } finally { if (out != null) { out.close(); } } } }