/**
*
*/
package net.frontlinesms.ui.i18n.legacy;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import net.frontlinesms.ui.i18n.InternationalisationUtils;
import net.frontlinesms.ui.i18n.TextResourceKeyOwner;
/**
* Main class for running the language tools from the CommandLine.
* @author Alex
*/
public class Main {
//> DEFAULT CONFIGURATION PROPERTY CONSTANTS
/** Directory to output generated files to */
private static final String OUTPUT_DIRECTORY = "temp/tools/lang";
/** Directory of the language files, hardcoded for now */
private static final String LANGUAGEBUNDLE_DIRECTORY = "src/main/resources/resources/languages";
/** Java package names containing the {@link TextResourceKeyOwner}s */
private static final String[] DEFAULT_TEXTKEYRESOURCE_PACKAGE_NAMES = {
"net.frontlinesms",
"net.frontlinesms.ui",
"net.frontlinesms.ui.handler",
"net.frontlinesms.ui.handler.contacts",
"net.frontlinesms.ui.handler.core",
"net.frontlinesms.ui.handler.email",
"net.frontlinesms.ui.handler.keyword",
"net.frontlinesms.ui.handler.message"};
/** Directory of the XML files, hardcoded for now */
private static final String[] UI_XML_LAYOUT_DIRECTORIES = {
"src/main/resources/ui/core",
"src/main/resources/ui/dialog",
"src/main/resources/ui/smsdevice",
"src/main/resources/ui/wizard",};
//> MAIN METHOD
/**
* Run the checker and produce a report.
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
if((args.length != 0 && args.length != 4 && args.length != 5)
|| args.length > 0 && args[0].equals("--help")) {
printUsage(System.out);
return;
}
String[] tRKOPackageNames;
String languagebundleDirectoryPath;
String[] uiXmlLayoutDirectories;
String outputDirectoryPath = OUTPUT_DIRECTORY;
String resourceFileBaseName;
if(args.length == 0) {
// Use default values
tRKOPackageNames = DEFAULT_TEXTKEYRESOURCE_PACKAGE_NAMES;
uiXmlLayoutDirectories = UI_XML_LAYOUT_DIRECTORIES;
languagebundleDirectoryPath = LANGUAGEBUNDLE_DIRECTORY;
resourceFileBaseName = "frontlineSMS";
} else {
// Extract values from args
tRKOPackageNames = splitArg(args[0]);
uiXmlLayoutDirectories = splitArg(args[1]);
languagebundleDirectoryPath = args[2];
resourceFileBaseName = args[3];
if(args.length > 4) outputDirectoryPath = args[4];
}
HashSet<Class<?>> tRKOSet = new HashSet<Class<?>>();
for(String packageName : tRKOPackageNames) {
tRKOSet.addAll(getAnnotatedClasses(TextResourceKeyOwner.class, getClasses(getClassNames(packageName))));
}
Class<?>[] uiJavaControllerClasses =
//UI_JAVA_CONTROLLER_CLASS_NAMES;
tRKOSet.toArray(new Class<?>[0]);
doReport(uiJavaControllerClasses, uiXmlLayoutDirectories, languagebundleDirectoryPath, resourceFileBaseName, outputDirectoryPath);
}
/**
* Splits a single commandline argument value into separate strings.
* @param arg
* @return
*/
private static String[] splitArg(String arg) {
return arg.split("\\s");
}
private static void doReport(Class<?>[] uiJavaControllerClasses, String[] uiXmlLayoutDirectories, String languagebundleDirectoryPath, String resourceFileBaseName, String outputDirectoryPath) throws Exception {
LanguageChecker checker = new LanguageChecker(
uiJavaControllerClasses,
uiXmlLayoutDirectories);
output(System.out, checker);
File outputDirectory = new File(outputDirectoryPath);
outputDirectory.mkdirs();
String extraTextResourcePath = languagebundleDirectoryPath + '/' + resourceFileBaseName + ".properties";
Map<String, String> extraTextResources = InternationalisationUtils.loadTextResources(extraTextResourcePath, new FileInputStream(extraTextResourcePath));
String baseTranslationFileName = "src/main/resources/resources/languages/" + InternationalisationUtils.DEFAULT_LANGUAGE_BUNDLE_FILENAME;
Map<String, String> baseTextResources = null;
if(!baseTranslationFileName.equals(extraTextResourcePath)) {
baseTextResources = InternationalisationUtils.loadTextResources(baseTranslationFileName, new FileInputStream(baseTranslationFileName));
Map<String, String> newMap = new HashMap<String, String>();
InternationalisationUtils.mergeMaps(newMap, baseTextResources);
InternationalisationUtils.mergeMaps(newMap, extraTextResources);
extraTextResources = newMap;
}
TranslationEmitter emitter = new TranslationEmitter(extraTextResources, outputDirectory);
for(File languageBundle : new File(languagebundleDirectoryPath).listFiles(getTextResourceFileFilter(resourceFileBaseName))) {
I18nReport report = checker.produceReport(baseTextResources, languageBundle);
report.output(System.out, true);
emitter.processBundle(languageBundle, report);
}
}
/**
* Gets a list of all classes in the supplied package.
* @param packageName
* @return list of fully-qualified java class names
*/
private static Collection<String> getClassNames(String packageName) {
FilenameFilter javaSourceFileFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".java");
}
};
String[] classFileNames = new File("src/main/java/" + packageName.replace('.', '/')).list(
javaSourceFileFilter);
HashSet<String> classNames = new HashSet<String>();
for (String classFileName : classFileNames) {
classNames.add(packageName + "." + classFileName.substring(0, classFileName.length() - ".java".length()));
}
return classNames;
}
/**
* Gets the {@link Class} objects for the supplied list of class names.
* @param classNames
* @return A list of classes.
* @throws ClassNotFoundException if there was a problem getting a reference to one of the classes.
*/
private static Collection<Class<?>> getClasses(Collection<String> classNames) throws ClassNotFoundException {
Set<Class<?>> classes = new HashSet<Class<?>>();
for(String className : classNames) {
classes.add(Class.forName(className));
}
return classes;
}
/**
* @param <T>
* @param annotation
* @param potentialSubclasses
* @return All classes in the supplied list who have the requested annotation.
*/
@SuppressWarnings("unchecked")
private static <T extends Annotation> Collection<Class<T>> getAnnotatedClasses(Class<T> annotation, Collection<Class<?>> potentialSubclasses) {
Set<Class<T>> annotated = new HashSet<Class<T>>();
for (Iterator iterator = potentialSubclasses.iterator(); iterator.hasNext();) {
Class<?> potentialSubclass = (Class<?>) iterator.next();
if(potentialSubclass.isAnnotationPresent(annotation)) {
annotated.add((Class<T>) potentialSubclass);
}
}
return annotated;
}
/**
* Show the usage of the {@link Main} class.
* @param out
*/
private static void printUsage(PrintStream out) {
out.println("FrontlineSMS i18n Tools - USAGE");
System.out.println("\tuse with no args for processing FrontlineSMS core");
System.out.println("\targs\t <packages containing text key resources> <directories containing thinlet xml layouts> <language bundle directory path> <language resource file basename> <[optional]output directory>");
}
/**
* Print the details of a report
* @param out
* @param checker
*/
private static void output(PrintStream out, LanguageChecker checker) {
out.println(checker.getClass().getName() + " REPORT START ----------");
out.println("\tin code: " + checker.getI18nKeysInCode().size());
out.println("\tin XML : " + checker.getI18nKeysInXml().size());
out.println("---------- REPORT START " + checker.getClass().getName());
}
//> INSTANCE PROPERTIES
//> CONSTRUCTORS
//> ACCESSORS
//> INSTANCE HELPER METHODS
//> STATIC FACTORIES
//> STATIC HELPER METHODS
/**
* @param resourceFileBaseName
* @return file filter for getting text resource files
*/
private static FileFilter getTextResourceFileFilter(final String resourceFileBaseName) {
/** Filter for sorting language files */
return new FileFilter() {
public boolean accept(File file) {
return file.getName().startsWith(resourceFileBaseName) && file.getName().endsWith(".properties");
}
};
}
}