/* * ScanForUnusedIl8nKeys.java * Copyright James Dempsey, 2012 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Created on 20/02/2012 7:31:38 AM * * $Id$ */ package pcgen.gui2; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Reader; import java.io.Writer; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.TreeSet; import org.apache.commons.io.DirectoryWalker; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateFormatUtils; import org.junit.Ignore; import org.junit.Test; /** * The Class <code>ScanForUnusedIl8nKeys</code> check for any unused keys in * the il8n properties. Currently it is a utility class masquerading as a unit * test but after completion of localisation work it will be used as means of * verifying the properties files. * * <br/> * * @author James Dempsey <jdempsey@users.sourceforge.net> */ public class ScanForUnusedIl8nKeys { private static final String CODE_PATH = "code/src/java/"; private static final String PROPERTIES_PATH = "pcgen/resources/lang/"; private static final String PROPERTIES_FILE = "LanguageBundle.properties"; private static final String NEW_PROPERTIES_FILE = "cleaned.properties"; private static final String UNUSED_PROPERTIES_FILE = "unused.properties"; private static final String[] PACKAGES = {"pcgen/gui2", "pcgen/core", "pcgen/system", "gmgen", "plugin", "pcgen/io", "pcgen/persistence", "pcgen/cdom", "pcgen/rules/context", "pcgen/util", }; @Ignore @Test public void scanForUnusedKeys() throws Exception { //Read in bundle, grab all keys Properties p = new Properties(); p.load(new FileInputStream(CODE_PATH + PROPERTIES_PATH + PROPERTIES_FILE)); Set<String> keys = new TreeSet<>(); for (Entry e : p.entrySet()) { keys.add((String)e.getKey()); } // Grab a list of files to be scanned List<File> fileList = buildFileList(); // Scan each file marking each found entry Set<String> missingKeys = new TreeSet<>(keys); actionWhitelistedKeys(missingKeys); for (File file : fileList) { scanJavaFileForKeys(file, missingKeys); } // Report all missing entries for (String key : missingKeys) { //System.out.println("Found unused key '" + key + "'."); } System.out.println("Total unused keys: " + missingKeys.size() + " from a set of " + keys.size() + " defined keys. " + ((missingKeys.size() * 100.0) / keys.size()) + "%"); // Output a new set outputCleanedProperties(new File(CODE_PATH + PROPERTIES_PATH + PROPERTIES_FILE), new File(CODE_PATH + PROPERTIES_PATH + NEW_PROPERTIES_FILE), missingKeys); // Output the unused file outputUnusedProperties(new File(CODE_PATH + PROPERTIES_PATH + PROPERTIES_FILE), new File(CODE_PATH + PROPERTIES_PATH + UNUSED_PROPERTIES_FILE), missingKeys); } /** * PCGenActionMap and PCGenAction dynamically construct keys. All keys * starting with the pattern used in those classes will be deemed present * and removed from the missing keys set. * * @param missingKeys The list of missing keys */ private void actionWhitelistedKeys(Set<String> missingKeys) { for (Iterator<String> iterator = missingKeys.iterator(); iterator.hasNext();) { String key = iterator.next(); if (key.startsWith("in_mnu") || key.startsWith("in_mn_mnu") || key.startsWith("in_EqBuilder_") || key.startsWith("PrerequisiteOperator.display")) { iterator.remove(); } } } /** * @param file * @param missingKeys * @throws IOException */ private void scanJavaFileForKeys(File file, Set<String> missingKeys) throws IOException { Reader reader = new BufferedReader(new FileReader(file)); List<String> lines = IOUtils.readLines(reader); reader.close(); for (String line : lines) { for (Iterator<String> i = missingKeys.iterator(); i.hasNext();) { String key = i.next(); if (line.contains("\"" + key + "\"")) { i.remove(); } } } } /** * @param inputPropsFile * @param cleanPropsFile * @param unusedKeys * @throws IOException */ private void outputCleanedProperties(File inputPropsFile, File cleanPropsFile, Set<String> unusedKeys) throws IOException { Reader reader = new BufferedReader(new FileReader(inputPropsFile)); List<String> lines = IOUtils.readLines(reader); reader.close(); Writer writer = new BufferedWriter(new PrintWriter(cleanPropsFile, "ISO-8859-1")); writer.write("# " + PROPERTIES_FILE + " with all unused keys removed as at " + DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(new Date()) + "\n"); boolean lastLineBlank = false; for (String line : lines) { boolean found = false; if (lastLineBlank && line.trim().isEmpty()) { continue; } else { for (String key : unusedKeys) { if (line.startsWith(key+"=")) { found = true; break; } } } if (!found) { lastLineBlank = line.trim().isEmpty(); if (!StringUtils.isAsciiPrintable(line)) { System.out.println("Found a non adcii line " + line); } writer.write(line + "\n"); } } writer.close(); } /** * @param inputPropsFile * @param unusedPropsFile * @param unusedKeys * @throws IOException */ private void outputUnusedProperties(File inputPropsFile, File unusedPropsFile, Set<String> unusedKeys) throws IOException { Reader reader = new BufferedReader(new FileReader(inputPropsFile)); List<String> lines = IOUtils.readLines(reader); reader.close(); Writer writer = new BufferedWriter(new FileWriter(unusedPropsFile)); writer.write("# " + PROPERTIES_FILE + " with all used keys removed as at " + DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(new Date()) + "\n"); boolean lastLineBlank = false; for (String line : lines) { boolean found = false; if (lastLineBlank && line.trim().isEmpty()) { continue; } else if (line.trim().startsWith("#") || line.trim().isEmpty()) { found = true; } else { for (String key : unusedKeys) { if (line.startsWith(key+"=")) { found = true; break; } } } if (found) { lastLineBlank = line.trim().isEmpty(); writer.write(line + "\n"); } } writer.close(); } /** * @return * @throws Exception */ private List<File> buildFileList() throws IOException { List<File> allFiles = new ArrayList<>(); JavaFileLister lister = new JavaFileLister(); for (String pkg : PACKAGES) { File folder = new File(CODE_PATH + pkg); allFiles.addAll(lister.getJavaFileList(folder)); } return allFiles; } private static class JavaFileLister extends DirectoryWalker { private List getJavaFileList(File startDirectory) throws IOException { List results = new ArrayList(); walk(startDirectory, results); return results; } @Override protected void handleFile(File file, int depth, Collection results) { if (file.getName().endsWith(".java")) { results.add(file); } } } }