Skip to content

Commit

Permalink
Merge pull request #83 from elvisisking/i18n-work
Browse files Browse the repository at this point in the history
Added an i1n framework to komodo-common. Localized the strings that needed localization in all projects except komodo-shell.
  • Loading branch information
elvisisking committed Jan 17, 2013
2 parents 317bc6d + 3874ff9 commit c69f553
Show file tree
Hide file tree
Showing 22 changed files with 510 additions and 3 deletions.
10 changes: 10 additions & 0 deletions komodo-common/.classpath
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
<attributes>
<attribute name="maven.pomderived" value="true"/>
Expand Down
23 changes: 23 additions & 0 deletions komodo-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,27 @@
<packaging>jar</packaging>
<name>Komodo Common</name>
<description>Komodo common utilities module</description>

<dependencies>
<!--
Testing dependencies
-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>

<!--
Logging dependencies
-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
</dependencies>
</project>
36 changes: 36 additions & 0 deletions komodo-common/src/main/java/org/komodo/common/CommonI18n.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.komodo.common;

import org.komodo.common.i18n.I18n;

/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/

/**
* Localized messages of the komodo-common module.
*/
@SuppressWarnings( {"javadoc"} )
public class CommonI18n extends I18n {

public static String missingI18Field;
public static String missingPropertiesKey;
public static String problemAccessingI18Field;
public static String problemLoadingI18nClass;
public static String problemLoadingI18nProperties;

static {
final CommonI18n i18n = new CommonI18n();
i18n.initialize();
}

/**
* Don't allow public construction.
*/
private CommonI18n() {
// nothing to do
}
}
165 changes: 165 additions & 0 deletions komodo-common/src/main/java/org/komodo/common/i18n/I18n.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.komodo.common.i18n;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.komodo.common.CommonI18n;
import org.komodo.common.util.CollectionUtil;
import org.komodo.common.util.Precondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A class that can be subclassed and used to internationalize property files. All public, static, and non-final strings
* in the subclass will be set to the default locale's value in the associated property file value. The placeholders used in
* the messages are those that are used by {@link Formatter}.
*/
public abstract class I18n {

class I18nProperties extends Properties {

private static final long serialVersionUID = 297271848138379264L;

private final Class<I18n> clazz;
private final Collection<String> errors;
private final Map<String, Field> fields;

I18nProperties(final Map<String, Field> fields,
final Class<I18n> clazz,
final Collection<String> errors) {
this.clazz = clazz;
this.fields = fields;
this.errors = errors;
}

/**
* {@inheritDoc}
*
* @see java.util.Hashtable#put(java.lang.Object, java.lang.Object)
*/
@Override
public synchronized Object put(final Object key,
final Object value) {
if (this.fields.containsKey(key)) {
try {
final Field field = this.clazz.getDeclaredField((String)key);
field.set(null, value);
this.fields.remove(key);
} catch (final Exception e) {
this.errors.add(I18n.bind(CommonI18n.problemAccessingI18Field, key, this.clazz.getName()));
}
} else {
this.errors.add(I18n.bind(CommonI18n.missingI18Field, key, this.clazz.getName()));
}

return super.put(key, value);
}

}

private final Logger logger = LoggerFactory.getLogger(getClass());

/**
* @param pattern the message pattern (cannot be <code>null</code> or empty)
* @param args the arguments being used to replace placeholders in the message (can be <code>null</code> or empty)
* @return the localized message (never <code>null</code>)
*/
public static String bind(final String pattern,
final Object... args) {
Precondition.notEmpty(pattern, "pattern"); //$NON-NLS-1$
return String.format(pattern, args);
}

/**
* Should be called in a <code>static</code> block to load the properties file and assign values to the class string fields.
*
* @throws IllegalStateException if there is a problem reading the I8n class file or properties file
*/
protected void initialize() {
final Map<String, Field> fields = new HashMap<String, Field>();

// collect all public, static, non-final, string fields
try {
for (final Field field : getClass().getDeclaredFields()) {
final int modifiers = field.getModifiers();

if ((field.getType() == String.class) && ((modifiers & Modifier.PUBLIC) == Modifier.PUBLIC)
&& ((modifiers & Modifier.STATIC) == Modifier.STATIC) && ((modifiers & Modifier.FINAL) != Modifier.FINAL)) {
fields.put(field.getName(), field);
}
}
} catch (final Exception e) {
throw new IllegalStateException(I18n.bind(CommonI18n.problemLoadingI18nClass, getClass().getName()), e);
}

// return if nothing to do
if (CollectionUtil.isEmpty(fields)) {
return;
}

// load properties file
InputStream stream = null;
IllegalStateException problem = null;

try {
final Class thisClass = getClass();
final String bundleName = thisClass.getName().replaceAll("\\.", "/").concat(".properties"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
final URL url = thisClass.getClassLoader().getResource(bundleName);
stream = url.openStream();

final Collection<String> errors = new ArrayList<String>();
final Properties props = new I18nProperties(fields, thisClass, errors);
props.load(stream);

// log errors for any properties keys that don't have fields
for (String error : errors) {
if (problem == null) {
problem = new IllegalStateException(error);
}

this.logger.error(error);
}

// log errors for any fields that don't have properties
for (final String fieldName : fields.keySet()) {
final String error = I18n.bind(CommonI18n.missingPropertiesKey, fieldName, getClass().getName());

if (problem == null) {
problem = new IllegalStateException(error);
}

this.logger.error(error);
}
} catch (final Exception e) {
throw new IllegalStateException(I18n.bind(CommonI18n.problemLoadingI18nProperties, getClass().getName()), e);
} finally {
if (stream != null) {
try {
stream.close();
} catch (final Exception e) {
} finally {
stream = null;
}
}

if (problem != null) {
throw problem;
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
*/
public class CollectionUtil {

/**
* @param map the map being checked (can be <code>null</code> or empty)
* @return <code>true</code> if <code>null</code> or empty
*/
public static boolean isEmpty(final Map<?, ?> map) {
return ((map == null) || map.isEmpty());
}

/**
* @param collection the collection being checked (can be <code>null</code> or empty)
* @return <code>true</code> if <code>null</code> or empty
Expand Down
14 changes: 14 additions & 0 deletions komodo-common/src/main/resources/log4j.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %m%n

# Root logger option
log4j.rootLogger=INFO, stdout

# Set up the default logging to be INFO level, then override specific units
log4j.logger.org.komodo.common=INFO

# Uncomment to turn debug logging on
#log4j.logger.org.komodo.common.I18n=DEBUG
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# 1 = name of key, 2 = class name of I18n subclass
missingI18Field = The properties key '%s' does not have a corresponding field in I18n '%s'

# 1 = I18n field name, 2 = class name of I18n subclass
missingPropertiesKey = The field '%s' in I18n '%s' does not have a corresponding entry in the properties file

# 1 = I18n field name, 2 = class name of I18n subclass
problemAccessingI18Field = There was a problem accessing field '%s' in I18n '%s'

# 1 = class name of I18n subclass
problemLoadingI18nClass = There was a problem loading I18n '%s' field values

# 1 = class name of I18n subclass
problemLoadingI18nProperties = There was a problem loading properties file for I18n '%s'
42 changes: 42 additions & 0 deletions komodo-common/src/test/java/org/komodo/common/GoodI18nTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.komodo.common;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import org.junit.BeforeClass;
import org.junit.Test;
import org.komodo.common.i18n.I18n;

/**
* A test class for {@link I18n}.
*/
@SuppressWarnings( {"javadoc", "nls"} )
public class GoodI18nTest extends I18n {

public static String field;
public static String patternOneArg;

@BeforeClass
public static void setup() {
final GoodI18nTest i18n = new GoodI18nTest();
i18n.initialize();
}

@Test
public void shouldSetFieldWithNoArg() {
assertThat(GoodI18nTest.field, is("test field"));
}

@Test
public void shouldSetFieldWithOneArg() {
final String arg = "argOne";
assertThat(I18n.bind(GoodI18nTest.patternOneArg, arg), is("test pattern " + arg));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.komodo.common;

import org.junit.Test;
import org.komodo.common.i18n.I18n;

/**
* A test class for {@link I18n}.
*/
@SuppressWarnings( {"javadoc"} )
public class MissingFieldI18nTest extends I18n {

public static String field;

@Test( expected = IllegalStateException.class )
public void shouldNotInitialize() {
final MissingFieldI18nTest i18n = new MissingFieldI18nTest();
i18n.initialize();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.komodo.common;

import org.junit.Test;
import org.komodo.common.i18n.I18n;

/**
* A test class for {@link I18n}.
*/
@SuppressWarnings( {"javadoc"} )
public class MissingKeyI18nTest extends I18n {

public static String field;
public static String missingKeyField;

@Test( expected = IllegalStateException.class )
public void shouldNotInitialize() {
final MissingKeyI18nTest i18n = new MissingKeyI18nTest();
i18n.initialize();
}

}

0 comments on commit c69f553

Please sign in to comment.