package com.limegroup.gnutella.i18n;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* @author Admin
*/
@SuppressWarnings("unchecked")
public class CountPercent {
private static final int ACTION_STATISTICS = 0;
private static final int ACTION_HTML = 1;
private static final int ACTION_CHECK = 2;
private static final int ACTION_UPDATE = 3;
private static final int ACTION_RELEASE = 4;
private static final int ACTION_NSIS = 5;
private static final double RELEASE_PERCENTAGE = .65;
/**
* Launched from the console with command-line parameters.<br />
* Usage: java CountPercent [html|check|update [<code>]|release]
* @param args a possibly empty array of command-line parameters.
* @throws IOException
*/
public static void main(String[] args) throws java.io.IOException {
final int action;
String code = null;
if (args != null && args.length > 0) {
if (args[0].equals("html")) { //$NON-NLS-1$
action = ACTION_HTML;
} else if (args[0].equals("check")) { //$NON-NLS-1$
action = ACTION_CHECK;
} else if (args[0].equals("update")) { //$NON-NLS-1$
action = ACTION_UPDATE;
if (args.length > 1)
code = args[1];
} else if (args[0].equals("release")) { //$NON-NLS-1$
action = ACTION_RELEASE;
} else if (args[0].equals("nsis")) { //$NON-NLS-1$
action = ACTION_NSIS;
} else {
System.err
.println("Usage: java CountPercent [html|check|update [<code>]|release|nsis]"); //$NON-NLS-1$
return;
}
} else
action = ACTION_STATISTICS;
new CountPercent(action, code);
}
private final DateFormat df;
private final NumberFormat rc;
private final NumberFormat pc;
private final Map/* <String, LanguageInfo> */langs;
private final Set/* <String> */basicKeys, advancedKeys;
private final int basicTotal;
/**
* @param action
* @param code
* @throws IOException
*/
CountPercent(int action, String code) throws java.io.IOException {
this.df = DateFormat.getDateInstance(DateFormat.LONG, Locale.US);
this.rc = NumberFormat.getNumberInstance(Locale.US);
this.rc.setMinimumIntegerDigits(4);
this.rc.setMaximumIntegerDigits(4);
this.rc.setGroupingUsed(false);
this.pc = NumberFormat.getPercentInstance(Locale.US);
this.pc.setMinimumFractionDigits(2);
this.pc.setMaximumFractionDigits(2);
this.pc.setMaximumIntegerDigits(3);
final File root = new File("."); //$NON-NLS-1$
final LanguageLoader loader = new LanguageLoader(root);
final Properties defaultProps = loader.getDefaultProperties();
this.advancedKeys = loader.getAdvancedKeys();
defaultProps.keySet().removeAll(this.advancedKeys);
this.basicKeys = defaultProps.keySet();
this.basicTotal = this.basicKeys.size();
this.langs = loader.loadLanguages();
switch (action) {
case ACTION_CHECK:
checkBadKeys();
break;
case ACTION_STATISTICS:
loader.extendVariantLanguages();
loader.retainKeys(this.basicKeys);
this.pc.setMinimumIntegerDigits(3);
printStatistics();
break;
case ACTION_HTML:
loader.extendVariantLanguages();
loader.retainKeys(this.basicKeys);
HTMLOutput html = new HTMLOutput(this.df, this.pc, this.langs,
this.basicTotal);
html.printHTML(System.out);
break;
case ACTION_RELEASE:
// release does an update, ensuring bad keys are gone
// & native2ascii is done.
case ACTION_NSIS:
// nsis does an update too.
case ACTION_UPDATE:
loader.extendVariantLanguages();
final Set/* <String> */validKeys = new HashSet/* <String> */();
validKeys.addAll(this.basicKeys);
validKeys.addAll(this.advancedKeys);
loader.retainKeys(validKeys);
final List/* <Line> */lines = loader.getEnglishLines();
final LanguageUpdater updater = new LanguageUpdater(root,
this.langs, lines);
if (action == ACTION_RELEASE || action == ACTION_NSIS)
updater.setSilent(true);
if (code == null)
updater.updateAllLanguages();
else {
LanguageInfo info = (LanguageInfo)this.langs.get(code);
updater.updateLanguage(info);
}
if (action == ACTION_RELEASE) {
loader.retainKeys(this.basicKeys);
release(root);
} else if (action == ACTION_NSIS) {
loader.retainKeys(this.basicKeys);
nsis();
}
break;
}
}
/**
* Check and list extra or badly names names found in resources. Use the
* default (English) basic and extended resource keys.
*/
private void checkBadKeys() {
System.out.println("List of extra or badly named resource keys:"); //$NON-NLS-1$
System.out.println("-------------------------------------------"); //$NON-NLS-1$
for (final Iterator/* <Map.Entry<String, LanguageInfo>> */i = this.langs
.entrySet().iterator(); i.hasNext();) {
final Map.Entry entry = (Map.Entry)i.next();
final String code = (String)entry.getKey();
final LanguageInfo li = (LanguageInfo)entry.getValue();
final Properties props = li.getProperties();
props.keySet().removeAll(this.basicKeys);
props.keySet().removeAll(this.advancedKeys);
if (props.size() != 0) {
System.out.println("(" + code + ") " + li.getName() + ": " //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+ li.getFileName());
props.list(System.out);
System.out
.println("-------------------------------------------"); //$NON-NLS-1$
}
}
}
/**
* Prints statistics about the number of translated resources.
*/
private void printStatistics() {
System.out.println("Total Number of Resources: " //$NON-NLS-1$
+ this.basicTotal);
System.out.println("---------------------------------"); //$NON-NLS-1$
System.out.println();
for (final Iterator/* <Map.Entry<String, LanguageInfo>> */i = this.langs
.entrySet().iterator(); i.hasNext();) {
final Map.Entry entry = (Map.Entry)i.next();
final String code = (String)entry.getKey();
final LanguageInfo li = (LanguageInfo)entry.getValue();
final Properties props = li.getProperties();
final int count = props.size();
final double percentage = (double)count / (double)this.basicTotal;
System.out.print("(" + code + ") " //$NON-NLS-1$//$NON-NLS-2$
+ this.pc.format(percentage)
+ ", count: " + this.rc.format(count) //$NON-NLS-1$
+ " [" + li.getName() + ": "); //$NON-NLS-1$//$NON-NLS-2$
try {
final byte[] langName = li.toString().getBytes("UTF-8"); //$NON-NLS-1$
System.out.write(langName, 0, langName.length);
} catch (java.io.UnsupportedEncodingException uee) {
// shouldn't occur
}
System.out.println("]"); //$NON-NLS-1$
}
}
/**
* Releases the properties.
*
* @param root
* the parent directory of the "release" directory into which the
* released properties files will be created.
*/
private void release(final File root) {
// First gather statistics on which languages have a suitable %
// translated.
List/* <LanguageInfo> */validLangs = new LinkedList/* <LanguageInfo> */();
for (final Iterator/* <LanguageInfo> */i = this.langs.values()
.iterator(); i.hasNext();) {
LanguageInfo li = (LanguageInfo)i.next();
int count = li.getProperties().size();
double percentage = (double)count / (double)this.basicTotal;
if (percentage >= RELEASE_PERCENTAGE)
validLangs.add(li);
}
// Now that we've got a list of valid languages, go through
// and copy'm to a release dir.
File release = new File(root, "release"); //$NON-NLS-1$
deleteAll(release);
copy(root, release, new ReleaseFilter(validLangs));
}
/**
* Lists all the languages that are of release-quality & have an NSIS name.
*/
private void nsis() {
System.out.println("English"); //$NON-NLS-1$
for (final Iterator/* <LanguageInfo> */i = this.langs.values()
.iterator(); i.hasNext();) {
final LanguageInfo li = (LanguageInfo)i.next();
final int count = li.getProperties().size();
final double percentage = (double)count / (double)this.basicTotal;
if (percentage >= RELEASE_PERCENTAGE) {
final String name = li.getNSISName();
if (!name.equals("")) //$NON-NLS-1$
System.out.println(name);
}
}
}
/**
* Recursively copies all files in root to dir that match Filter.
*
* @param root
* @param dir
* @param filter
*/
private void copy(final File root, final File dir, final FileFilter filter) {
final File[] files = root.listFiles(filter);
for (int i = 0; i < files.length; i++) {
final File f = files[i];
if (f.isDirectory())
copy(f, new File(dir, f.getName()), filter);
else
copy(f, dir);
}
}
/**
* Recursively deletes all files in a directory.
*
* @param f
*/
private void deleteAll(File f) {
if (f.isDirectory()) {
final File[] files = f.listFiles();
for (int i = 0; i < files.length; i++)
deleteAll(files[i]);
}
f.delete();
}
/**
* Copies file to dir, ignoring any lines that are comments.
*
* @param src
* @param dstDir
*/
private void copy(final File src, final File dstDir) {
dstDir.mkdirs();
final File dst = new File(dstDir, src.getName());
BufferedReader reader = null;
BufferedWriter writer = null;
try {
reader = new BufferedReader(new InputStreamReader(
new FileInputStream(src), "ISO-8859-1")); //$NON-NLS-1$
writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(dst), "ISO-8859-1")); //$NON-NLS-1$
String read;
while ((read = reader.readLine()) != null) {
Line line = new Line(read);
if (line.isComment())
continue;
writer.write(read);
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null)
try {
reader.close();
} catch (IOException e) {/* ignored */}
if (writer != null) {
try {
writer.flush();
} catch (IOException e) {/* ignored */}
try {
writer.close();
} catch (IOException e) {/* ignored */}
}
}
}
/**
* Filter for releasing files.
*/
private static class ReleaseFilter implements FileFilter {
/**
* Comment for <code>validLangs</code>
*/
final List validLangs;
/**
* @param valid
*/
ReleaseFilter(final List valid) {
this.validLangs = valid;
}
/**
* @see java.io.FileFilter#accept(java.io.File)
*/
public boolean accept(File f) {
if (f.isDirectory())
return true;
final String name = f.getName();
if (!name.endsWith(".properties")) //$NON-NLS-1$
return false;
final int idxU;
if ((idxU = name.indexOf('_')) == -1)
return true; // base resource.
final String code = name.substring(idxU + 1, name.indexOf(".")); //$NON-NLS-1$
if (code.equals("en")) //$NON-NLS-1$ // always valid.
return true;
// check to see if the code is in the list of valid codes.
for (final Iterator i = this.validLangs.iterator(); i.hasNext();) {
LanguageInfo li = (LanguageInfo)i.next();
// if its the base of a variant or the variant itself,
// accept it. (need bases so the variant works.)
if (code.equals(li.getBaseCode()) || code.equals(li.getCode()))
return true;
}
return false;
}
}
}