mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
Javadocs and unit tests.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
package net.sf.briar.api.db;
|
package net.sf.briar.api.db;
|
||||||
|
|
||||||
/** Uniquely identifies a contact. */
|
/** Type-safe wrapper for an integer that uniquely identifies a contact. */
|
||||||
public class ContactId {
|
public class ContactId {
|
||||||
|
|
||||||
private final int id;
|
private final int id;
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ public interface DatabaseComponent {
|
|||||||
/** Adds a locally generated message to the database. */
|
/** Adds a locally generated message to the database. */
|
||||||
void addLocallyGeneratedMessage(Message m) throws DbException;
|
void addLocallyGeneratedMessage(Message m) throws DbException;
|
||||||
|
|
||||||
/** Generates a bundle of messages for the given contact. */
|
/**
|
||||||
|
* Generates a bundle of acknowledgements, subscriptions, and batches of
|
||||||
|
* messages for the given contact.
|
||||||
|
*/
|
||||||
void generateBundle(ContactId c, Bundle b) throws DbException;
|
void generateBundle(ContactId c, Bundle b) throws DbException;
|
||||||
|
|
||||||
/** Returns the user's rating for the given author. */
|
/** Returns the user's rating for the given author. */
|
||||||
@@ -45,8 +48,9 @@ public interface DatabaseComponent {
|
|||||||
Set<GroupId> getSubscriptions() throws DbException;
|
Set<GroupId> getSubscriptions() throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a bundle of messages received from the given contact. Some
|
* Processes a bundle of acknowledgements, subscriptions, and batches of
|
||||||
* or all of the messages in the bundle may be stored.
|
* messages received from the given contact. Some or all of the messages
|
||||||
|
* in the bundle may be stored.
|
||||||
*/
|
*/
|
||||||
void receiveBundle(ContactId c, Bundle b) throws DbException;
|
void receiveBundle(ContactId c, Bundle b) throws DbException;
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
package net.sf.briar.api.i18n;
|
package net.sf.briar.api.i18n;
|
||||||
|
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public interface FontManager {
|
public interface FontManager {
|
||||||
|
|
||||||
/** Initializes the FontManager for the given locale. */
|
/** Initializes the FontManager for the given locale. */
|
||||||
void initialize(Locale locale) throws IOException;
|
void initialize(Locale locale);
|
||||||
|
|
||||||
/** Returns the appropriate font for the given language. */
|
/** Returns the appropriate font for the given language. */
|
||||||
Font getFontForLanguage(String language);
|
Font getFontForLanguage(String language);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package net.sf.briar.api.protocol;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/** Uniquely identifies a pseudonymous author. */
|
/** Type-safe wrapper for a byte array that uniquely identifies an author. */
|
||||||
public class AuthorId {
|
public class AuthorId {
|
||||||
|
|
||||||
public static final int LENGTH = 32;
|
public static final int LENGTH = 32;
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ package net.sf.briar.api.protocol;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/** Uniquely identifies a batch of messages. */
|
/**
|
||||||
|
* Type-safe wrapper for a byte array that uniquely identifies a batch of
|
||||||
|
* messages.
|
||||||
|
*/
|
||||||
public class BatchId {
|
public class BatchId {
|
||||||
|
|
||||||
public static final int LENGTH = 32;
|
public static final int LENGTH = 32;
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ package net.sf.briar.api.protocol;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type-safe wrapper for a byte array that uniquely identifies a bundle of
|
||||||
|
* acknowledgements, subscriptions, and batches of messages.
|
||||||
|
*/
|
||||||
public class BundleId {
|
public class BundleId {
|
||||||
|
|
||||||
public static final BundleId NONE = new BundleId(new byte[] {
|
public static final BundleId NONE = new BundleId(new byte[] {
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ package net.sf.briar.api.protocol;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/** Uniquely identifies a group to which a user may subscribe. */
|
/**
|
||||||
|
* Type-safe wrapper for a byte array that uniquely identifies a group to which
|
||||||
|
* users may subscribe.
|
||||||
|
*/
|
||||||
public class GroupId {
|
public class GroupId {
|
||||||
|
|
||||||
public static final int LENGTH = 32;
|
public static final int LENGTH = 32;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package net.sf.briar.api.protocol;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/** Uniquely identifies a message. */
|
/** Type-safe wrapper for a byte array that uniquely identifies a message. */
|
||||||
public class MessageId {
|
public class MessageId {
|
||||||
|
|
||||||
/** Used to indicate that the first message in a thread has no parent. */
|
/** Used to indicate that the first message in a thread has no parent. */
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ import net.sf.briar.api.protocol.MessageId;
|
|||||||
|
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract superclass containing code shared by ReadWriteLockDatabaseComponent
|
||||||
|
* and SynchronizedDatabaseComponent.
|
||||||
|
*/
|
||||||
abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
|
abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
@@ -35,9 +39,17 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
|
|||||||
startCleaner();
|
startCleaner();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the oldest messages from the database, with a total size less
|
||||||
|
* than or equal to the given size.
|
||||||
|
*/
|
||||||
protected abstract void expireMessages(long size) throws DbException;
|
protected abstract void expireMessages(long size) throws DbException;
|
||||||
|
|
||||||
// Locking: messages write
|
/**
|
||||||
|
* Calculates and returns the sendability score of a message.
|
||||||
|
* <p>
|
||||||
|
* Locking: messages write.
|
||||||
|
*/
|
||||||
private int calculateSendability(Txn txn, Message m) throws DbException {
|
private int calculateSendability(Txn txn, Message m) throws DbException {
|
||||||
int sendability = 0;
|
int sendability = 0;
|
||||||
// One point for a good rating
|
// One point for a good rating
|
||||||
@@ -51,6 +63,12 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
|
|||||||
return sendability;
|
return sendability;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks how much free storage space is available to the database, and if
|
||||||
|
* necessary expires old messages until the free space is at least
|
||||||
|
* MIN_FREE_SPACE. While the free space is less than CRITICAL_FREE_SPACE,
|
||||||
|
* operations that attempt to store messages in the database will block.
|
||||||
|
*/
|
||||||
private void checkFreeSpaceAndClean() throws DbException {
|
private void checkFreeSpaceAndClean() throws DbException {
|
||||||
long freeSpace = db.getFreeSpace();
|
long freeSpace = db.getFreeSpace();
|
||||||
while(freeSpace < MIN_FREE_SPACE) {
|
while(freeSpace < MIN_FREE_SPACE) {
|
||||||
@@ -74,7 +92,11 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: contacts read
|
/**
|
||||||
|
* Returns true iff the database contains the given contact.
|
||||||
|
* <p>
|
||||||
|
* Locking: contacts read.
|
||||||
|
*/
|
||||||
protected boolean containsContact(ContactId c) throws DbException {
|
protected boolean containsContact(ContactId c) throws DbException {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
@@ -87,14 +109,24 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: contacts read, messages write, messageStatuses write
|
/**
|
||||||
|
* Removes the given message (and all associated state) from the database.
|
||||||
|
* <p>
|
||||||
|
* Locking: contacts read, messages write, messageStatuses write.
|
||||||
|
*/
|
||||||
protected void removeMessage(Txn txn, MessageId id) throws DbException {
|
protected void removeMessage(Txn txn, MessageId id) throws DbException {
|
||||||
Integer sendability = db.getSendability(txn, id);
|
Integer sendability = db.getSendability(txn, id);
|
||||||
assert sendability != null;
|
assert sendability != null;
|
||||||
|
// If the message is sendable, deleting it may affect its ancestors'
|
||||||
|
// sendability (backward inclusion)
|
||||||
if(sendability > 0) updateAncestorSendability(txn, id, false);
|
if(sendability > 0) updateAncestorSendability(txn, id, false);
|
||||||
db.removeMessage(txn, id);
|
db.removeMessage(txn, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true iff the amount of free storage space available to the
|
||||||
|
* database should be checked.
|
||||||
|
*/
|
||||||
private boolean shouldCheckFreeSpace() {
|
private boolean shouldCheckFreeSpace() {
|
||||||
synchronized(spaceLock) {
|
synchronized(spaceLock) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
@@ -117,6 +149,12 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a new thread to monitor the amount of free storage space
|
||||||
|
* available to the database and expire old messages as necessary.
|
||||||
|
* <p>
|
||||||
|
* FIXME: The thread implementation should be factored out.
|
||||||
|
*/
|
||||||
private void startCleaner() {
|
private void startCleaner() {
|
||||||
Runnable cleaner = new Runnable() {
|
Runnable cleaner = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -140,7 +178,14 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
|
|||||||
new Thread(cleaner).start();
|
new Thread(cleaner).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: contacts read, messages write, messageStatuses write
|
/**
|
||||||
|
* If the given message is already in the database, marks it as seen by the
|
||||||
|
* sender and returns false. Otherwise stores the message, updates the
|
||||||
|
* sendability of its ancestors if necessary, marks the message as seen by
|
||||||
|
* the sender and unseen by all other contacts, and returns true.
|
||||||
|
* <p>
|
||||||
|
* Locking: contacts read, messages write, messageStatuses write.
|
||||||
|
*/
|
||||||
protected boolean storeMessage(Txn txn, Message m, ContactId sender)
|
protected boolean storeMessage(Txn txn, Message m, ContactId sender)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
boolean added = db.addMessage(txn, m);
|
boolean added = db.addMessage(txn, m);
|
||||||
@@ -164,7 +209,15 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
|
|||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: messages write
|
/**
|
||||||
|
* Iteratively updates the sendability of a message's ancestors to reflect
|
||||||
|
* a change in the message's sendability. Returns the number of ancestors
|
||||||
|
* that have changed from sendable to not sendable, or vice versa.
|
||||||
|
* <p>
|
||||||
|
* Locking: messages write.
|
||||||
|
* @param increment True if the message's sendability has changed from 0 to
|
||||||
|
* greater than 0, or false if it has changed from greater than 0 to 0.
|
||||||
|
*/
|
||||||
private int updateAncestorSendability(Txn txn, MessageId m,
|
private int updateAncestorSendability(Txn txn, MessageId m,
|
||||||
boolean increment) throws DbException {
|
boolean increment) throws DbException {
|
||||||
int affected = 0;
|
int affected = 0;
|
||||||
@@ -191,7 +244,14 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
|
|||||||
return affected;
|
return affected;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: messages write
|
/**
|
||||||
|
* Updates the sendability of all messages written by the given author, and
|
||||||
|
* the ancestors of those messages if necessary.
|
||||||
|
* <p>
|
||||||
|
* Locking: messages write.
|
||||||
|
* @param increment True if the user's rating for the author has changed
|
||||||
|
* from not good to good, or false if it has changed from good to not good.
|
||||||
|
*/
|
||||||
protected void updateAuthorSendability(Txn txn, AuthorId a,
|
protected void updateAuthorSendability(Txn txn, AuthorId a,
|
||||||
boolean increment) throws DbException {
|
boolean increment) throws DbException {
|
||||||
int direct = 0, indirect = 0;
|
int direct = 0, indirect = 0;
|
||||||
@@ -217,6 +277,11 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
|
|||||||
+ indirect + " indirectly");
|
+ indirect + " indirectly");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks until messages are allowed to be stored in the database. The
|
||||||
|
* storage of messages is not allowed while the amount of free storage
|
||||||
|
* space available to the database is less than CRITICAL_FREE_SPACE.
|
||||||
|
*/
|
||||||
protected void waitForPermissionToWrite() {
|
protected void waitForPermissionToWrite() {
|
||||||
synchronized(writeLock) {
|
synchronized(writeLock) {
|
||||||
while(!writesAllowed) {
|
while(!writesAllowed) {
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ import net.sf.briar.api.protocol.MessageId;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of DatabaseComponent using reentrant read-write locks.
|
||||||
|
* This implementation can allow writers to starve.
|
||||||
|
*/
|
||||||
class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
@@ -28,8 +32,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Locks must always be acquired in alphabetical order. See the Database
|
* Locks must always be acquired in alphabetical order. See the Database
|
||||||
* interface to find out which calls require which locks. Note: this
|
* interface to find out which calls require which locks.
|
||||||
* implementation can allow writers to starve.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private final ReentrantReadWriteLock contactLock =
|
private final ReentrantReadWriteLock contactLock =
|
||||||
@@ -112,6 +115,8 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
try {
|
try {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
|
// Don't store the message if the user has
|
||||||
|
// unsubscribed from the group
|
||||||
if(db.containsSubscription(txn, m.getGroup())) {
|
if(db.containsSubscription(txn, m.getGroup())) {
|
||||||
boolean added = storeMessage(txn, m, null);
|
boolean added = storeMessage(txn, m, null);
|
||||||
assert added;
|
assert added;
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ import net.sf.briar.api.protocol.MessageId;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of DatabaseComponent using Java synchronization. This
|
||||||
|
* implementation does not distinguish between readers and writers.
|
||||||
|
*/
|
||||||
class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
@@ -80,6 +84,8 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
synchronized(subscriptionLock) {
|
synchronized(subscriptionLock) {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
|
// Don't store the message if the user has
|
||||||
|
// unsubscribed from the group
|
||||||
if(db.containsSubscription(txn, m.getGroup())) {
|
if(db.containsSubscription(txn, m.getGroup())) {
|
||||||
boolean added = storeMessage(txn, m, null);
|
boolean added = storeMessage(txn, m, null);
|
||||||
assert added;
|
assert added;
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import java.util.HashMap;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
@@ -20,19 +22,32 @@ import net.sf.briar.util.FileUtils;
|
|||||||
|
|
||||||
public class FontManagerImpl implements FontManager {
|
public class FontManagerImpl implements FontManager {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(FontManagerImpl.class.getName());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each bundled font is associated with a size, which is meant to occupy
|
||||||
|
* roughly the same amount of space as the default font (12 point sans),
|
||||||
|
* and a list of languages for which the bundled font should be used.
|
||||||
|
*/
|
||||||
private static final BundledFont[] BUNDLED_FONTS = {
|
private static final BundledFont[] BUNDLED_FONTS = {
|
||||||
|
// Use TibetanMachineUni for Tibetan
|
||||||
new BundledFont("TibetanMachineUni.ttf", 14f, new String[] { "bo" }),
|
new BundledFont("TibetanMachineUni.ttf", 14f, new String[] { "bo" }),
|
||||||
|
// Use Padauk for Burmese
|
||||||
new BundledFont("Padauk.ttf", 14f, new String[] { "my" }),
|
new BundledFont("Padauk.ttf", 14f, new String[] { "my" }),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Map from languages to fonts
|
||||||
private final Map<String, Font> fonts = new TreeMap<String, Font>();
|
private final Map<String, Font> fonts = new TreeMap<String, Font>();
|
||||||
|
|
||||||
private volatile Font defaultFont = null, uiFont = null;
|
private volatile Font defaultFont = null, uiFont = null;
|
||||||
|
|
||||||
public void initialize(Locale locale) throws IOException {
|
public void initialize(Locale locale) {
|
||||||
try {
|
// Look for bundled fonts in the jar and the filesystem. If any fonts
|
||||||
ClassLoader loader = getClass().getClassLoader();
|
// are missing or fail to load, fall back to the default font.
|
||||||
for(BundledFont bf : BUNDLED_FONTS) {
|
ClassLoader loader = getClass().getClassLoader();
|
||||||
|
for(BundledFont bf : BUNDLED_FONTS) {
|
||||||
|
try {
|
||||||
InputStream in = loader.getResourceAsStream(bf.filename);
|
InputStream in = loader.getResourceAsStream(bf.filename);
|
||||||
if(in == null) {
|
if(in == null) {
|
||||||
File root = FileUtils.getBriarDirectory();
|
File root = FileUtils.getBriarDirectory();
|
||||||
@@ -42,9 +57,13 @@ public class FontManagerImpl implements FontManager {
|
|||||||
Font font = Font.createFont(Font.TRUETYPE_FONT, in);
|
Font font = Font.createFont(Font.TRUETYPE_FONT, in);
|
||||||
font = font.deriveFont(bf.size);
|
font = font.deriveFont(bf.size);
|
||||||
for(String language : bf.languages) fonts.put(language, font);
|
for(String language : bf.languages) fonts.put(language, font);
|
||||||
|
} catch(FontFormatException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning("Could not load font: " + e.getMessage());
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(LOG.isLoggable(Level.WARNING))
|
||||||
|
LOG.warning("Could not load font: " + e.getMessage());
|
||||||
}
|
}
|
||||||
} catch(FontFormatException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
}
|
||||||
defaultFont = getFont("Sans", 12f);
|
defaultFont = getFont("Sans", 12f);
|
||||||
assert defaultFont != null; // FIXME: This is failing on Windows
|
assert defaultFont != null; // FIXME: This is failing on Windows
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ import net.sf.briar.util.FileUtils;
|
|||||||
|
|
||||||
public class I18nImpl implements I18n {
|
public class I18nImpl implements I18n {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property keys for strings used in the JRE's built-in dialogs. Values
|
||||||
|
* assigned to these keys in i18n properties files will override the
|
||||||
|
* built-in values.
|
||||||
|
*/
|
||||||
private static final String[] uiManagerKeys = {
|
private static final String[] uiManagerKeys = {
|
||||||
"FileChooser.acceptAllFileFilterText",
|
"FileChooser.acceptAllFileFilterText",
|
||||||
"FileChooser.cancelButtonText",
|
"FileChooser.cancelButtonText",
|
||||||
@@ -87,6 +92,7 @@ public class I18nImpl implements I18n {
|
|||||||
synchronized(bundleLock) {
|
synchronized(bundleLock) {
|
||||||
if(bundle == null) {
|
if(bundle == null) {
|
||||||
bundle = ResourceBundle.getBundle("i18n", locale, loader);
|
bundle = ResourceBundle.getBundle("i18n", locale, loader);
|
||||||
|
assert bundle != null;
|
||||||
for(String key : uiManagerKeys) {
|
for(String key : uiManagerKeys) {
|
||||||
try {
|
try {
|
||||||
UIManager.put(key, bundle.getString(key));
|
UIManager.put(key, bundle.getString(key));
|
||||||
@@ -106,6 +112,7 @@ public class I18nImpl implements I18n {
|
|||||||
Font uiFont = fontManager.getUiFont();
|
Font uiFont = fontManager.getUiFont();
|
||||||
synchronized(bundleLock) {
|
synchronized(bundleLock) {
|
||||||
this.locale = locale;
|
this.locale = locale;
|
||||||
|
Locale.setDefault(locale);
|
||||||
bundle = null;
|
bundle = null;
|
||||||
synchronized(listeners) {
|
synchronized(listeners) {
|
||||||
for(Listener l : listeners) l.localeChanged(uiFont);
|
for(Listener l : listeners) l.localeChanged(uiFont);
|
||||||
@@ -116,7 +123,7 @@ public class I18nImpl implements I18n {
|
|||||||
public void loadLocale() throws IOException {
|
public void loadLocale() throws IOException {
|
||||||
File root = FileUtils.getBriarDirectory();
|
File root = FileUtils.getBriarDirectory();
|
||||||
Scanner s = new Scanner(new File(root, "Data/locale.cfg"));
|
Scanner s = new Scanner(new File(root, "Data/locale.cfg"));
|
||||||
if(s.hasNextLine()) locale = new Locale(s.nextLine());
|
if(s.hasNextLine()) setLocale(new Locale(s.nextLine()));
|
||||||
s.close();
|
s.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,18 +41,11 @@ class InvitationWorker implements Runnable {
|
|||||||
List<File> files = new ArrayList<File>();
|
List<File> files = new ArrayList<File>();
|
||||||
try {
|
try {
|
||||||
if(callback.isCancelled()) return;
|
if(callback.isCancelled()) return;
|
||||||
File invitationDat = createInvitationDat(dir);
|
files.add(createInvitationDat(dir));
|
||||||
files.add(invitationDat);
|
|
||||||
if(callback.isCancelled()) return;
|
if(callback.isCancelled()) return;
|
||||||
if(parameters.shouldCreateExe()) {
|
if(parameters.shouldCreateExe()) files.add(createBriarExe(dir));
|
||||||
File briarExe = createBriarExe(dir);
|
|
||||||
files.add(briarExe);
|
|
||||||
}
|
|
||||||
if(callback.isCancelled()) return;
|
if(callback.isCancelled()) return;
|
||||||
if(parameters.shouldCreateJar()) {
|
if(parameters.shouldCreateJar()) files.add(createBriarJar(dir));
|
||||||
File briarJar = createBriarJar(dir);
|
|
||||||
files.add(briarJar);
|
|
||||||
}
|
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
callback.error(e.getMessage());
|
callback.error(e.getMessage());
|
||||||
return;
|
return;
|
||||||
@@ -69,8 +62,7 @@ class InvitationWorker implements Runnable {
|
|||||||
// FIXME: Create a real invitation
|
// FIXME: Create a real invitation
|
||||||
try {
|
try {
|
||||||
Thread.sleep(2000);
|
Thread.sleep(2000);
|
||||||
} catch(InterruptedException ignored) {
|
} catch(InterruptedException ignored) {}
|
||||||
}
|
|
||||||
Arrays.fill(password, (char) 0);
|
Arrays.fill(password, (char) 0);
|
||||||
FileOutputStream out = new FileOutputStream(invitationDat);
|
FileOutputStream out = new FileOutputStream(invitationDat);
|
||||||
byte[] buf = new byte[1024];
|
byte[] buf = new byte[1024];
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import net.sf.briar.api.protocol.Batch;
|
|||||||
import net.sf.briar.api.protocol.BatchId;
|
import net.sf.briar.api.protocol.BatchId;
|
||||||
import net.sf.briar.api.protocol.Message;
|
import net.sf.briar.api.protocol.Message;
|
||||||
|
|
||||||
|
/** A simple in-memory implementation of a batch. */
|
||||||
class BatchImpl implements Batch {
|
class BatchImpl implements Batch {
|
||||||
|
|
||||||
private final List<Message> messages = new ArrayList<Message>();
|
private final List<Message> messages = new ArrayList<Message>();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<project name='test' default='test'>
|
<project name='test' default='test'>
|
||||||
<import file='../build-common.xml'/>
|
<import file='../build-common.xml'/>
|
||||||
<target name='test' depends='depend'>
|
<target name='test' depends='depend'>
|
||||||
<junit printsummary='on'>
|
<junit printsummary='on' showoutput='true'>
|
||||||
<classpath>
|
<classpath>
|
||||||
<fileset refid='bundled-jars'/>
|
<fileset refid='bundled-jars'/>
|
||||||
<fileset refid='test-jars'/>
|
<fileset refid='test-jars'/>
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
<path refid='test-classes'/>
|
<path refid='test-classes'/>
|
||||||
<path refid='util-classes'/>
|
<path refid='util-classes'/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
<test name='net.sf.briar.i18n.FontManagerImplTest'/>
|
||||||
|
<test name='net.sf.briar.invitation.InvitationWorkerTest'/>
|
||||||
<test name='net.sf.briar.setup.SetupWorkerTest'/>
|
<test name='net.sf.briar.setup.SetupWorkerTest'/>
|
||||||
<test name='net.sf.briar.util.FileUtilsTest'/>
|
<test name='net.sf.briar.util.FileUtilsTest'/>
|
||||||
<test name='net.sf.briar.util.StringUtilsTest'/>
|
<test name='net.sf.briar.util.StringUtilsTest'/>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class TestUtils {
|
|||||||
private static final AtomicInteger nextTestDir =
|
private static final AtomicInteger nextTestDir =
|
||||||
new AtomicInteger((int) (Math.random() * 1000 * 1000));
|
new AtomicInteger((int) (Math.random() * 1000 * 1000));
|
||||||
|
|
||||||
public static void delete(File f) throws IOException {
|
public static void delete(File f) {
|
||||||
if(f.isDirectory()) for(File child : f.listFiles()) delete(child);
|
if(f.isDirectory()) for(File child : f.listFiles()) delete(child);
|
||||||
f.delete();
|
f.delete();
|
||||||
}
|
}
|
||||||
@@ -29,4 +29,8 @@ public class TestUtils {
|
|||||||
File testDir = new File("test.tmp/" + name);
|
File testDir = new File("test.tmp/" + name);
|
||||||
return testDir;
|
return testDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void deleteTestDirectories() {
|
||||||
|
delete(new File("test.tmp"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
69
test/net/sf/briar/i18n/FontManagerImplTest.java
Normal file
69
test/net/sf/briar/i18n/FontManagerImplTest.java
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package net.sf.briar.i18n;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import net.sf.briar.i18n.FontManagerImpl;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class FontManagerImplTest extends TestCase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBundledFontsAreLoaded() {
|
||||||
|
FontManagerImpl fontManager = new FontManagerImpl();
|
||||||
|
fontManager.initialize(Locale.UK);
|
||||||
|
|
||||||
|
Font font = fontManager.getFontForLanguage("en"); // English
|
||||||
|
assertEquals(12, font.getSize());
|
||||||
|
|
||||||
|
font = fontManager.getFontForLanguage("bo"); // Tibetan
|
||||||
|
assertEquals("Tibetan Machine Uni", font.getFamily());
|
||||||
|
assertEquals(14, font.getSize());
|
||||||
|
|
||||||
|
font = fontManager.getFontForLanguage("my"); // Burmese
|
||||||
|
assertEquals("Padauk", font.getFamily());
|
||||||
|
assertEquals(14, font.getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInternationalCharactersCanBeDisplayed() {
|
||||||
|
FontManagerImpl fontManager = new FontManagerImpl();
|
||||||
|
fontManager.initialize(Locale.UK);
|
||||||
|
|
||||||
|
Font font = fontManager.getFontForLanguage("en"); // English
|
||||||
|
assertTrue(font.canDisplay('a'));
|
||||||
|
|
||||||
|
font = fontManager.getFontForLanguage("ar"); // Arabic
|
||||||
|
assertTrue(font.canDisplay('\u0627')); // An Arabic character
|
||||||
|
|
||||||
|
font = fontManager.getFontForLanguage("bo"); // Tibetan
|
||||||
|
assertTrue(font.canDisplay('\u0f00')); // A Tibetan character
|
||||||
|
|
||||||
|
font = fontManager.getFontForLanguage("fa"); // Persian
|
||||||
|
assertTrue(font.canDisplay('\ufb56')); // A Persian character
|
||||||
|
|
||||||
|
font = fontManager.getFontForLanguage("my"); // Burmese
|
||||||
|
assertTrue(font.canDisplay('\u1000')); // A Burmese character
|
||||||
|
|
||||||
|
font = fontManager.getFontForLanguage("zh"); // Chinese
|
||||||
|
assertTrue(font.canDisplay('\u4e00')); // A Chinese character
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAndGetUiFont() {
|
||||||
|
FontManagerImpl fontManager = new FontManagerImpl();
|
||||||
|
fontManager.initialize(Locale.UK);
|
||||||
|
Font font = fontManager.getUiFont();
|
||||||
|
assertEquals(12, font.getSize());
|
||||||
|
|
||||||
|
fontManager.setUiFontForLanguage("bo");
|
||||||
|
font = fontManager.getUiFont();
|
||||||
|
assertEquals("Tibetan Machine Uni", font.getFamily());
|
||||||
|
assertEquals(14, font.getSize());
|
||||||
|
|
||||||
|
fontManager.setUiFontForLanguage("en");
|
||||||
|
font = fontManager.getUiFont();
|
||||||
|
assertEquals(12, font.getSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -147,7 +147,7 @@ public class InvitationWorkerTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws IOException {
|
public void tearDown() {
|
||||||
TestUtils.delete(testDir);
|
TestUtils.deleteTestDirectories();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public class SetupWorkerTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws IOException {
|
public void tearDown() {
|
||||||
TestUtils.delete(testDir);
|
TestUtils.deleteTestDirectories();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ public class FileUtilsTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws IOException {
|
public void tearDown() {
|
||||||
TestUtils.delete(testDir);
|
TestUtils.deleteTestDirectories();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ public class ZipUtilsTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws IOException {
|
public void tearDown() {
|
||||||
TestUtils.delete(testDir);
|
TestUtils.deleteTestDirectories();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,19 +18,12 @@ public class FileUtils {
|
|||||||
assert f.exists();
|
assert f.exists();
|
||||||
if(f.isFile()) {
|
if(f.isFile()) {
|
||||||
// Running from a jar - return the jar's grandparent
|
// Running from a jar - return the jar's grandparent
|
||||||
try {
|
f = f.getParentFile().getParentFile();
|
||||||
f = f.getCanonicalFile().getParentFile().getParentFile();
|
|
||||||
} catch(IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Running from Eclipse
|
// Running from Eclipse or ant
|
||||||
try {
|
f = new File(f.getParentFile(), "Briar");
|
||||||
f = new File(f.getCanonicalFile().getParentFile(), "Briar");
|
if(!f.exists())
|
||||||
} catch(IOException e) {
|
f = new File(f.getParentFile().getParentFile(), "Briar"); // Ant
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
f.mkdir();
|
|
||||||
}
|
}
|
||||||
assert f.exists();
|
assert f.exists();
|
||||||
assert f.isDirectory();
|
assert f.isDirectory();
|
||||||
|
|||||||
Reference in New Issue
Block a user