/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
// www.projectforge.org
//
// Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition 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; version 3 of the License.
//
// This community edition 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, see http://www.gnu.org/licenses/.
//
/////////////////////////////////////////////////////////////////////////////
package org.projectforge.core;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.projectforge.address.FormOfAddress;
import org.projectforge.calendar.DayHolder;
import org.projectforge.calendar.MonthHolder;
import org.projectforge.fibu.AuftragFilter;
import org.projectforge.fibu.AuftragsStatus;
import org.projectforge.web.MenuItemDef;
import org.projectforge.web.MenuItemDefId;
import org.projectforge.web.wicket.WebConstants;
/**
* Tries to get all used i18n keys from the sources (java and html). As result a file is written which will be checked by
* AdminAction.checkI18nProperties. Unused i18n keys should be detected.
* @author Kai Reinhard (k.reinhard@micromata.de)
*
*/
public class CreateI18nKeys
{
// TODO: I18nEnums
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(CreateI18nKeys.class);
private static final String PATH = "src/main/";
private static final String PATH_DAYHOLDER = getPathForClass(DayHolder.class);
private static final String PATH_MONTHHOLDER = getPathForClass(MonthHolder.class);
private static final String PATH_FORM_OF_ADDRESS = getPathForClass(FormOfAddress.class);
private static final String PATH_MENU_ITEM_DEF = getPathForClass(MenuItemDef.class);
private static final String PATH_AUFTRAG_FILTER = getPathForClass(AuftragFilter.class);
private static final String PATH_AUFTRAG_STATUS = getPathForClass(AuftragsStatus.class);
private static final String PATH_PRIORITY = getPathForClass(Priority.class);
private static final String I18N_KEYS_FILE = "src/main/resources/" + WebConstants.FILE_I18N_KEYS;
public static void main(final String[] args) throws IOException
{
new CreateI18nKeys().run();
}
public void run() throws IOException
{
log.info("Create file with all detected i18n keys.");
final Map<String, Set<String>> i18nKeyUsage = new HashMap<String, Set<String>>();
parseHtml(i18nKeyUsage);
parseJava(i18nKeyUsage);
final FileWriter writer = new FileWriter(I18N_KEYS_FILE);
writer
.append("# Don't edit this file. This file is only for developers for checking i18n keys and detecting missed and unused ones.\n");
final Set<String> i18nKeys = new TreeSet<String>(i18nKeyUsage.keySet());
for (final String i18nKey : i18nKeys) {
writer.append(i18nKey).append("=");
final Set<String> set = i18nKeyUsage.get(i18nKey);
boolean first = true;
for (final String filename : set) {
if (first == false) {
writer.append(',');
} else {
first = false;
}
writer.append(filename);
}
writer.append("\n");
}
IOUtils.closeQuietly(writer);
log.info("Creation of file of found i18n keys done: " + I18N_KEYS_FILE);
}
private void parseHtml(final Map<String, Set<String>> i18nKeyUsage) throws IOException
{
final Collection<File> files = listFiles(PATH + "java", "html");
for (final File file : files) {
final String content = getContent(file);
find(file, i18nKeyUsage, content, "<wicket:message\\s+key=\"([a-zA-Z0-9\\.]+)\"\\s/>");
}
}
private void parseJava(final Map<String, Set<String>> i18nKeyUsage) throws IOException
{
final Collection<File> files = listFiles(PATH, "java");
for (final File file : files) {
final String content = getContent(file);
find(file, i18nKeyUsage, content, "getString\\(\"([a-zA-Z0-9\\.]+)\"\\)"); // getString("i18nKey")
find(file, i18nKeyUsage, content, "getLocalizedString\\(\"([a-zA-Z0-9\\.]+)\"\\)"); // getLocalizedString("i18nkey")
find(file, i18nKeyUsage, content, "getLocalizedMessage\\(\"([a-zA-Z0-9\\.]+)\""); // getLocalizedMessage("i18nkey"
find(file, i18nKeyUsage, content, "addError\\(\"([a-zA-Z0-9\\.]+)\"\\)"); // addError("i18nkey")
find(file, i18nKeyUsage, content, "addError\\([a-zA-Z0-9_\"\\.]+,\\s+\"([a-zA-Z0-9\\.]+)\""); // addError(..., "i18nkey"
find(file, i18nKeyUsage, content, "addGlobalError\\(\"([a-zA-Z0-9\\.]+)\"\\)"); // addGlobalError("i18nkey")
find(file, i18nKeyUsage, content, "resolveMessage\\(\"([a-zA-Z0-9\\.]+)\"\\)"); // resolveMessage("i18nkey")
find(file, i18nKeyUsage, content, "throw new UserException\\(\"([a-zA-Z0-9\\.]+)\""); // throw new UserException("i18nkey"
find(file, i18nKeyUsage, content, "throw new AccessException\\(\"([a-zA-Z0-9\\.]+)\""); // throw new AccessException("i18nkey"
find(file, i18nKeyUsage, content, "I18N_KEY_[A-Z0-9_]+ = \"([a-zA-Z0-9\\.]+)\""); // I18N_KEY_... = "i18nKey"
find(file, i18nKeyUsage, content, "new Holiday\\(\"([a-zA-Z0-9\\.]+)\""); // new Holiday("i18nKey"
find(file, i18nKeyUsage, content, "MessageAction.getForwardResolution\\([a-zA-Z0-9_\"\\.]+,\\s+\"([a-zA-Z0-9\\.]+)\"\\)"); // MessageAction.getForwardResolution(...,
// "i18nkey");
if (file.getPath().endsWith(PATH_DAYHOLDER) == true) {
for (final String key : DayHolder.DAY_KEYS) {
add(i18nKeyUsage, "calendar.day." + key, file);
add(i18nKeyUsage, "calendar.shortday." + key, file);
}
} else if (file.getPath().endsWith(PATH_MONTHHOLDER) == true) {
for (final String key : MonthHolder.MONTH_KEYS) {
add(i18nKeyUsage, "calendar.month." + key, file);
}
} else if (file.getPath().endsWith(PATH_FORM_OF_ADDRESS) == true) {
for (final FormOfAddress form : FormOfAddress.values()) {
add(i18nKeyUsage, form.getI18nKey(), file);
}
} else if (file.getPath().endsWith(PATH_AUFTRAG_FILTER) == true) {
for (final String key : AuftragFilter.LIST) {
add(i18nKeyUsage, "fibu.auftrag.filter.type." + key, file);
}
} else if (file.getPath().endsWith(PATH_AUFTRAG_STATUS) == true) {
for (final AuftragsStatus status : AuftragsStatus.values()) {
add(i18nKeyUsage, status.getI18nKey(), file);
}
} else if (file.getPath().endsWith(PATH_PRIORITY) == true) {
for (final Priority priority : Priority.values()) {
add(i18nKeyUsage, "priority." + priority.getKey(), file);
}
} else if (file.getPath().endsWith(PATH_MENU_ITEM_DEF) == true) {
for (final MenuItemDefId menuItem : MenuItemDefId.values()) {
add(i18nKeyUsage, menuItem.getI18nKey(), file);
}
} else if (file.getName().endsWith("Page.java") == true
&& (content.contains("extends AbstractListPage") == true || content.contains("extends AbstractEditPage") == true)) { // Wicket
// Page
List<String> list = find(file, content, "super\\(parameters, \"([a-zA-Z0-9\\.]+)\"\\);"); // super(parameters, "i18nKey");
for (final String entry : list) {
add(i18nKeyUsage, entry + ".title.edit", file);
add(i18nKeyUsage, entry + ".title.list", file);
add(i18nKeyUsage, entry + ".title.list.select", file);
}
list = find(file, content, "super\\(caller, selectProperty, \"([a-zA-Z0-9\\.]+)\"\\);"); // super(caller, selectProperty,
// "i18nKey");
for (final String entry : list) {
add(i18nKeyUsage, entry + ".title.edit", file);
add(i18nKeyUsage, entry + ".title.list", file);
add(i18nKeyUsage, entry + ".title.list.select", file);
}
}
}
}
private static String getPathForClass(final Class< ? > clazz)
{
return clazz.getName().replace(".", "/") + ".java";
}
private void find(final File file, final Map<String, Set<String>> i18nKeyUsage, final String content, final String regexp)
{
find(file, i18nKeyUsage, content, regexp, null);
}
private void find(final File file, final Map<String, Set<String>> i18nKeyUsage, final String content, final String regexp,
final String prefix)
{
final List<String> list = find(file, content, regexp);
for (final String entry : list) {
final String key = prefix != null ? prefix + entry : entry;
add(i18nKeyUsage, key, file);
}
}
private List<String> find(final File file, final String content, final String regexp)
{
final List<String> result = new ArrayList<String>();
final Pattern p = Pattern.compile(regexp, Pattern.MULTILINE); // Compiles regular expression into Pattern.
final Matcher m = p.matcher(content);
while (m.find()) {
result.add(m.group(1));
}
return result;
}
private void add(final Map<String, Set<String>> i18nKeyUsage, final String key, final File file)
{
Set<String> set = i18nKeyUsage.get(key);
if (set == null) {
set = new TreeSet<String>();
i18nKeyUsage.put(key, set);
}
set.add(file.getPath());
}
private Collection<File> listFiles(final String path, final String suffix)
{
return FileUtils.listFiles(new File(path), new String[] { suffix}, true);
}
private String getContent(final File file) throws IOException
{
return FileUtils.readFileToString(file, "UTF-8");
}
}