/*******************************************************************************
* Jimm - Mobile Messaging - J2ME ICQ clone
* Copyright (C) 2003-06 Jimm Project
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
********************************************************************************
* File: util/langs/src/langs/LangsTask.java
* Version: ###VERSION### Date: ###DATE###
* Author(s): Artyomov Denis, Andreas Rossbacher
*******************************************************************************/
package langs;
import java.io.*;
import java.util.*;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
//class BuildException extends Exception {public BuildException(Exception e) {}; public BuildException(String e) {};}
//class Task extends Object {}
class ShortKeyword {
private String shortKeyword;
boolean used;
public ShortKeyword(String shortKeyword) {
this.shortKeyword = shortKeyword;
used = false;
}
public String getShortKeyword() {
return shortKeyword;
}
public void wasUsed() {
used = true;
}
public boolean isUsed() {
return used;
}
}
class WordsToShortWordsMap {
private Hashtable shortKeys = new Hashtable();
private int shortKeywordCounter = 0;
private static final char[] shortKeyElems = {
'_', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z',
};
private static boolean isSpecialKey(String keyword) {
return keyword.startsWith("error_") || keyword.startsWith("lang_");
}
public String keyToShortKey(String keyword) {
if ( isSpecialKey(keyword) ) {
return keyword;
}
ShortKeyword tableKeyword = (ShortKeyword)shortKeys.get(keyword);
if (tableKeyword != null) {
return tableKeyword.getShortKeyword();
}
StringBuffer shortKeyword = new StringBuffer();
int value = shortKeywordCounter;
do {
shortKeyword.append(shortKeyElems[value % shortKeyElems.length]);
value /= shortKeyElems.length;
} while (value > 0);
String newKey = shortKeyword.toString();
shortKeys.put(keyword, new ShortKeyword(newKey));
shortKeywordCounter++;
return newKey;
}
public ShortKeyword get(String normalKey) {
return (ShortKeyword)shortKeys.get(normalKey);
}
public Enumeration keys() {
return shortKeys.keys();
}
public void clear() {
shortKeys.clear();
}
}
class Warnings {
private static Vector warnings = new Vector();
public void add(String str) {
warnings.add(str);
}
public void show() {
int size = warnings.size();
if (size == 0) return;
System.out.println("*** WARNINGS ***");
for (int i = 0; i < size; i++) {
System.out.println((String)warnings.elementAt(i));
}
clear();
}
public void clear() {
warnings.clear();
}
}
class Lang {
private Vector pairs;
private String name;
private String fullname;
public Lang(String name) {
this.name = name;
fullname = name;
}
public String getName() {
return name;
}
public String getFullName() {
return fullname;
}
private String[] devideToWords(String text) throws Exception {
String[] resultArray = new String[2];
try {
resultArray[0] = text.substring(1,text.indexOf('"',1));
resultArray[1] = text.substring(text.indexOf('"',text.indexOf('"',1)+1)+1,text.lastIndexOf('"'));
} catch (Exception e) {
throw new Exception();
}
return resultArray;
}
public void readLangFile(String inFile) {
pairs = new Vector();
String line;
try {
File srcFile = new File(inFile);
InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile), "UTF-8");
BufferedReader reader = new BufferedReader(isr);
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.length() == 0) continue;
if (line.charAt(0) == '/') continue;
StringBuffer strBuf = new StringBuffer();
int lineLen = line.length();
for (int i = 0; i < lineLen; i++) {
char chr = line.charAt(i);
String addText = null;
if ((chr == '\\') && (i != (lineLen-1))) {
char chr2 = line.charAt(i+1);
if (chr2 == 'n') addText = "\n";
if (chr2 == 'r') addText = "\r";
if (chr2 == 't') addText = "\t";
if (chr2 == '"') addText = "\"";
}
if (addText != null) {
strBuf.append(addText);
i++;
} else {
if (chr < ' ') chr = ' ';
strBuf.append(chr);
}
}
String[] words;
try {
words = devideToWords(strBuf.toString());
if ("lang".equals(words[0])) {
fullname = words[1];
continue;
}
} catch (Exception e) {
throw new BuildException("Wrong lang line '" + line + "' in file '" + inFile + "'");
}
if (words[0].length() == 0) {
throw new BuildException("Wrong lang line '" + line + "' in file '" + inFile + "'");
}
pairs.addElement(words);
}
} catch (Exception e) {
throw new BuildException(e);
}
}
public void writeLngFile(WordsToShortWordsMap shortKeys, String outFile) throws BuildException {
try {
FileOutputStream ostream = new FileOutputStream(outFile);
DataOutputStream dos = new DataOutputStream(ostream);
int size = pairs.size();
dos.writeShort(size);
for (int i = 0; i < size; i++) {
String[] pair = (String[])pairs.elementAt(i);
dos.writeUTF(shortKeys.keyToShortKey(pair[0]));
dos.writeUTF(pair[1]);
}
dos.flush();
ostream.close();
} catch (Exception e) {
throw new BuildException(e);
}
}
private Hashtable pairsToTable() {
Hashtable result = new Hashtable();
for (int i = 0; i < pairs.size(); i++) {
String[] pair = (String[])pairs.elementAt(i);
result.put(pair[0], pair[1]);
}
return result;
}
public int size() {
return pairs.size();
}
public String[] elementAt(int i) {
return (String[])pairs.elementAt(i);
}
public void correctToIdealLang(Warnings warnings, WordsToShortWordsMap shortKeys, Lang idealLang) {
// Place normal keys to hashset for quick finding
Hashtable normalData = pairsToTable();
Hashtable idealPairs = idealLang.pairsToTable();
// Which pairs absent?
Enumeration idealKeys = idealPairs.keys();
while (idealKeys.hasMoreElements()) {
String idealKey = (String)idealKeys.nextElement();
if ( !normalData.containsKey(idealKey) ) {
String value = (String)idealPairs.get(idealKey);
normalData.put(idealKey, value);
warnings.add(getName() +".lang: missed \""+idealKey+"\" ("+value+")");
}
}
// Which keys are unnecessary?
Enumeration normalKeys = normalData.keys();
while (normalKeys.hasMoreElements()) {
String normalKey = (String)normalKeys.nextElement();
if ( !idealPairs.containsKey(normalKey) ) {
warnings.add(getName() +".lang: unnecessary \""+normalKey+"\"");
normalData.remove(normalKey);
normalKeys = normalData.keys();
}
}
// Whick keys are not in sources?
normalKeys = normalData.keys();
while (normalKeys.hasMoreElements()) {
String normalKey = (String)normalKeys.nextElement();
ShortKeyword keyObj = (ShortKeyword)shortKeys.get(normalKey);
if (keyObj == null) continue;
if ( !keyObj.isUsed() ) {
warnings.add(getName() +".lang: not present at sources \""+normalKey+"\"");
normalData.remove(normalKey);
normalKeys = normalData.keys();
}
}
// Save corrcted data to pairs
pairs.clear();
normalKeys = normalData.keys();
while (normalKeys.hasMoreElements()) {
String normalKey = (String)normalKeys.nextElement();
pairs.add( new String[] {normalKey, (String)normalData.get(normalKey)} );
}
}
}
public class LangsTask extends Task {
private String languages, inDir, outDir, srcDir, idealLang;
private static WordsToShortWordsMap shortKeys = new WordsToShortWordsMap();
private static Warnings warnings = new Warnings();
static String[] readFile(String fileName) throws Exception {
Vector lines = new Vector();
String line;
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
BufferedReader reader = new BufferedReader(isr);
while ((line = reader.readLine()) != null) lines.add(line);
reader.close();
isr.close();
String[] result = new String[lines.size()];
lines.copyInto(result);
return result;
}
static private void addKeysToSpecialKeys(Lang lang) {
int size = lang.size();
for (int i = 0; i < size; i++) {
String[] pair = lang.elementAt(i);
shortKeys.keyToShortKey(pair[0]);
}
}
// Scans the given directory (srcDir/srcDirExt) for Java source files
static private String[] scanDir(File srcDir, String srcDirExt) {
// Initalize vector
Vector filenames = new Vector();
// Get all Java source file in the current directory
File[] files = (new File(srcDir, srcDirExt)).listFiles();
for (int i = 0; i < files.length; i++) {
if (files[i].isFile() && (files[i].getName().endsWith(".java"))) {
filenames.add(srcDir + srcDirExt + File.separator + files[i].getName());
} else if (files[i].isDirectory()) {
filenames.addAll(Arrays.asList(scanDir(srcDir, srcDirExt + File.separator + files[i].getName())));
}
}
// Return Vector as array
String[] ret = new String[filenames.size()];
filenames.copyInto(ret);
return (ret);
}
static private String replaceString(String str, String from, String to) {
for (;;) {
int index = str.indexOf(from);
if (index == -1) break;
str = str.substring(0, index)+to+str.substring(index+from.length(), str.length());
}
return str;
}
static private void replaceLangKeysInSources(String fileName) {
String line;
try {
// load source file
line = readAndPrepareSrcFile(fileName);
// replase old keys to new ones
Enumeration allKeys = shortKeys.keys();
while (allKeys.hasMoreElements()) {
String oldKey = (String)allKeys.nextElement();
ShortKeyword shortKeyword = shortKeys.get(oldKey);
String newKey = shortKeyword.getShortKeyword();
String textToReplace = "\""+oldKey+"\"";
boolean contains = (line.indexOf(textToReplace) != -1);
if (contains) shortKeyword.wasUsed();
line = replaceString(line, textToReplace, "\""+newKey+"\"");
}
// Write source lines with new lang keys
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(fileName), "UTF-8");
BufferedWriter writer = new BufferedWriter(osw);
writer.write(line);
writer.newLine();
writer.close();
osw.close();
} catch (Exception e) {
throw new BuildException(e);
}
}
// Load and prepare file (remove comments)
private final static int PP_STATE_DIRECT_COPY = 1;
private final static int PP_STATE_WAIT_FOR_COMMENT = 2;
private final static int PP_STATE_SIMPLE_COMMENT = 3;
private final static int PP_STATE_MULTILINE_COMMENT = 4;
private final static int PP_STATE_WAIT_FOR_MLC_END = 5; // wait for multiline comment end
private final static int PP_STATE_STRING = 6;
static String readAndPrepareSrcFile(String fileName) throws BuildException {
StringBuffer buffer = new StringBuffer();
int state = PP_STATE_DIRECT_COPY;
try {
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName));
BufferedReader reader = new BufferedReader(isr);
for (;;) {
int curByte = reader.read();
if (curByte == -1) break;
switch (state) {
case PP_STATE_DIRECT_COPY:
if (curByte == '/') state = PP_STATE_WAIT_FOR_COMMENT;
else if (curByte == '\"') {
state = PP_STATE_STRING;
buffer.append((char)curByte);
} else buffer.append((char)curByte);
break;
case PP_STATE_WAIT_FOR_COMMENT:
if (curByte == '*') state = PP_STATE_MULTILINE_COMMENT;
else if (curByte == '/') state = PP_STATE_SIMPLE_COMMENT;
else {
buffer.append('/');
buffer.append((char)curByte);
state = PP_STATE_DIRECT_COPY;
}
break;
case PP_STATE_SIMPLE_COMMENT:
if ((curByte == '\r') || (curByte == '\n')) state = PP_STATE_DIRECT_COPY;
break;
case PP_STATE_MULTILINE_COMMENT:
if (curByte == '*') state = PP_STATE_WAIT_FOR_MLC_END;
break;
case PP_STATE_WAIT_FOR_MLC_END:
if (curByte == '/') state = PP_STATE_DIRECT_COPY;
else if (curByte == '*') state = PP_STATE_WAIT_FOR_MLC_END;
else state = PP_STATE_MULTILINE_COMMENT;
break;
case PP_STATE_STRING:
buffer.append((char)curByte);
if (curByte == '\"') state = PP_STATE_DIRECT_COPY;
break;
}
}
reader.close();
isr.close();
} catch (Exception e) {
throw new BuildException(e);
}
return buffer.toString();
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
public void setLanguages(String value) {
languages = value;
}
public void setInDir(String value) {
inDir = value;
}
public void setOutDir(String value) {
outDir = value;
}
public void setSrcDir(String value) {
srcDir = value;
}
public void setIdealLang(String value) {
idealLang = value;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
private void saveLanglist(Vector langs) throws BuildException {
/*
try {
FileOutputStream ostream = new FileOutputStream(outDir+"/langlist.lng");
DataOutputStream dos = new DataOutputStream(ostream);
dos.writeShort(langs.size());
for (int i = 0; i < langs.size(); i++) {
Lang lang = (Lang)langs.elementAt(i);
dos.writeUTF(lang.getName());
}
dos.flush();
ostream.close();
} catch (Exception e) {
throw new BuildException(e);
}
*/
try {
PrintWriter pw = new PrintWriter(
new OutputStreamWriter(
new FileOutputStream(outDir+"/langlist.txt"), "Utf8"));
for (int i = 0; i < langs.size(); i++) {
Lang lang = (Lang)langs.elementAt(i);
pw.println(lang.getName() + "=" + lang.getFullName());
}
pw.close();
} catch (Exception e) {
throw new BuildException(e);
}
}
public void execute() throws BuildException {
shortKeys.clear();
warnings.clear();
Vector langs = new Vector();
StringTokenizer strTok = new StringTokenizer(languages, ",");
while (strTok.hasMoreTokens()) {
String langName = strTok.nextToken();
langs.add(new Lang(langName));
}
int size = langs.size();
if (size == 0) new BuildException("No language specified");
System.out.println("Loading lang file[s]... ");
Lang idealLang = new Lang(this.idealLang);
idealLang.readLangFile(inDir+"/"+idealLang.getName()+".lang");
for (int i = 0; i < size; i++) {
Lang lang = (Lang)langs.elementAt(i);
lang.readLangFile(inDir+"/"+lang.getName()+".lang");
addKeysToSpecialKeys(lang);
}
saveLanglist(langs);
// Show warnings
warnings.show();
// Replace keys in sources
System.out.println("Preprocessing java sources... ");
String[] files = scanDir(new File(srcDir), "");
for (int i = 0; i < files.length; i++) {
replaceLangKeysInSources(files[i]);
}
// Save LNG files
System.out.println("Saving lng file[s]... ");
for (int i = 0; i < size; i++) {
Lang lang = (Lang)langs.elementAt(i);
lang.correctToIdealLang(warnings, shortKeys, idealLang);
lang.writeLngFile(shortKeys, outDir+"/"+lang.getName()+".lng");
}
warnings.show();
}
}