Line 23... |
Line 23... |
23 |
import java.util.List;
|
23 |
import java.util.List;
|
24 |
import java.util.Locale;
|
24 |
import java.util.Locale;
|
25 |
import java.util.Map;
|
25 |
import java.util.Map;
|
26 |
import java.util.ResourceBundle.Control;
|
26 |
import java.util.ResourceBundle.Control;
|
27 |
|
27 |
|
- |
|
28 |
import javax.xml.XMLConstants;
|
28 |
import javax.xml.parsers.DocumentBuilder;
|
29 |
import javax.xml.parsers.DocumentBuilder;
|
29 |
import javax.xml.parsers.DocumentBuilderFactory;
|
30 |
import javax.xml.parsers.DocumentBuilderFactory;
|
30 |
|
31 |
|
31 |
import net.jcip.annotations.GuardedBy;
|
- |
|
32 |
import net.jcip.annotations.ThreadSafe;
|
- |
|
33 |
|
- |
|
34 |
import org.w3c.dom.Document;
|
32 |
import org.w3c.dom.Document;
|
35 |
import org.w3c.dom.Element;
|
33 |
import org.w3c.dom.Element;
|
36 |
import org.w3c.dom.NodeList;
|
34 |
import org.w3c.dom.NodeList;
|
37 |
|
35 |
|
- |
|
36 |
import net.jcip.annotations.GuardedBy;
|
- |
|
37 |
import net.jcip.annotations.ThreadSafe;
|
- |
|
38 |
|
38 |
@ThreadSafe
|
39 |
@ThreadSafe
|
39 |
public class TranslationManager {
|
40 |
public class TranslationManager {
|
40 |
private static final Locale FALLBACK_LOCALE = Locale.ENGLISH;
|
41 |
private static final Locale FALLBACK_LOCALE = Locale.ENGLISH;
|
41 |
|
42 |
|
42 |
private static final Control CONTROL = new I18nUtils.SameLanguageControl() {
|
43 |
private static final Control CONTROL = new I18nUtils.SameLanguageControl() {
|
Line 62... |
Line 63... |
62 |
@GuardedBy("classes")
|
63 |
@GuardedBy("classes")
|
63 |
private final List<Class<?>> classes;
|
64 |
private final List<Class<?>> classes;
|
64 |
@GuardedBy("classes")
|
65 |
@GuardedBy("classes")
|
65 |
private Locale locale;
|
66 |
private Locale locale;
|
66 |
|
67 |
|
67 |
private final Object trMutex = new String("translations mutex");
|
68 |
private final Object trMutex = new Object();
|
68 |
@GuardedBy("trMutex")
|
69 |
@GuardedBy("trMutex")
|
69 |
private Map<String, String> menuTranslation;
|
70 |
private Map<String, String> menuTranslation;
|
70 |
@GuardedBy("trMutex")
|
71 |
@GuardedBy("trMutex")
|
71 |
private Map<String, String> itemTranslation;
|
72 |
private Map<String, String> itemTranslation;
|
72 |
@GuardedBy("trMutex")
|
73 |
@GuardedBy("trMutex")
|
73 |
private Map<String, String> actionTranslation;
|
74 |
private Map<String, String> actionTranslation;
|
74 |
|
75 |
|
75 |
private TranslationManager() {
|
76 |
private TranslationManager() {
|
76 |
this.classes = new ArrayList<Class<?>>();
|
77 |
this.classes = new ArrayList<>();
|
77 |
}
|
78 |
}
|
78 |
|
79 |
|
79 |
public void addTranslationStreamFromClass(Class<?> c) {
|
80 |
public void addTranslationStreamFromClass(Class<?> c) {
|
80 |
synchronized (this.classes) {
|
81 |
synchronized (this.classes) {
|
81 |
this.classes.add(c);
|
82 |
this.classes.add(c);
|
Line 162... |
Line 163... |
162 |
}
|
163 |
}
|
163 |
}
|
164 |
}
|
164 |
|
165 |
|
165 |
private void loadAllTranslation() {
|
166 |
private void loadAllTranslation() {
|
166 |
synchronized (this.trMutex) {
|
167 |
synchronized (this.trMutex) {
|
167 |
this.menuTranslation = new HashMap<String, String>();
|
168 |
this.menuTranslation = new HashMap<>();
|
168 |
this.itemTranslation = new HashMap<String, String>();
|
169 |
this.itemTranslation = new HashMap<>();
|
169 |
this.actionTranslation = new HashMap<String, String>();
|
170 |
this.actionTranslation = new HashMap<>();
|
170 |
if (this.classes.size() == 0) {
|
171 |
if (this.classes.isEmpty()) {
|
171 |
Log.get().warning("TranslationManager has no resources to load (" + this.getLocale() + ")");
|
172 |
Log.get().warning("TranslationManager has no resources to load (" + this.getLocale() + ")");
|
172 |
}
|
173 |
}
|
173 |
for (Class<?> c : this.classes) {
|
174 |
for (Class<?> c : this.classes) {
|
174 |
boolean loaded = loadTranslation(this.getLocale(), c);
|
175 |
boolean loaded = loadTranslation(this.getLocale(), c);
|
175 |
if (!loaded) {
|
176 |
if (!loaded) {
|
Line 180... |
Line 181... |
180 |
}
|
181 |
}
|
181 |
|
182 |
|
182 |
// return all existing (e.g fr_CA only specify differences with fr)
|
183 |
// return all existing (e.g fr_CA only specify differences with fr)
|
183 |
private List<InputStream> findStream(final Locale locale, final Class<?> c, final boolean rootLast) {
|
184 |
private List<InputStream> findStream(final Locale locale, final Class<?> c, final boolean rootLast) {
|
184 |
final Control cntrl = CONTROL;
|
185 |
final Control cntrl = CONTROL;
|
185 |
final List<InputStream> res = new ArrayList<InputStream>();
|
186 |
final List<InputStream> res = new ArrayList<>();
|
186 |
final String baseName = c.getPackage().getName() + "." + BASENAME;
|
187 |
final String baseName = c.getPackage().getName() + "." + BASENAME;
|
187 |
|
188 |
|
188 |
// test emptiness to not mix languages
|
189 |
// test emptiness to not mix languages
|
189 |
for (Locale targetLocale = locale; targetLocale != null && res.isEmpty(); targetLocale = cntrl.getFallbackLocale(baseName, targetLocale)) {
|
190 |
for (Locale targetLocale = locale; targetLocale != null && res.isEmpty(); targetLocale = cntrl.getFallbackLocale(baseName, targetLocale)) {
|
190 |
for (final Locale candidate : cntrl.getCandidateLocales(baseName, targetLocale)) {
|
191 |
for (final Locale candidate : cntrl.getCandidateLocales(baseName, targetLocale)) {
|
Line 201... |
Line 202... |
201 |
private boolean loadTranslation(final Locale l, Class<?> c) {
|
202 |
private boolean loadTranslation(final Locale l, Class<?> c) {
|
202 |
boolean translationLoaded = false;
|
203 |
boolean translationLoaded = false;
|
203 |
// we want more specific translations to replace general ones, i.e. root Locale first
|
204 |
// we want more specific translations to replace general ones, i.e. root Locale first
|
204 |
for (final InputStream input : findStream(l, c, false)) {
|
205 |
for (final InputStream input : findStream(l, c, false)) {
|
205 |
// create new instances to check if there's no duplicates in each resource
|
206 |
// create new instances to check if there's no duplicates in each resource
|
206 |
final Map<String, String> menuTranslation = new HashMap<String, String>(), itemTranslation = new HashMap<String, String>(), actionTranslation = new HashMap<String, String>();
|
207 |
final Map<String, String> menuTranslation = new HashMap<>(), itemTranslation = new HashMap<>(), actionTranslation = new HashMap<>();
|
207 |
loadTranslation(input, menuTranslation, itemTranslation, actionTranslation);
|
208 |
loadTranslation(input, menuTranslation, itemTranslation, actionTranslation);
|
208 |
// on the other hand, it's OK for one resource to override another
|
209 |
// on the other hand, it's OK for one resource to override another
|
209 |
this.menuTranslation.putAll(menuTranslation);
|
210 |
this.menuTranslation.putAll(menuTranslation);
|
210 |
this.itemTranslation.putAll(itemTranslation);
|
211 |
this.itemTranslation.putAll(itemTranslation);
|
211 |
this.actionTranslation.putAll(actionTranslation);
|
212 |
this.actionTranslation.putAll(actionTranslation);
|
212 |
translationLoaded = true;
|
213 |
translationLoaded = true;
|
213 |
}
|
214 |
}
|
214 |
return translationLoaded;
|
215 |
return translationLoaded;
|
215 |
}
|
216 |
}
|
216 |
|
217 |
|
217 |
static private void loadTranslation(final InputStream input, final Map<String, String> menuTranslation, final Map<String, String> itemTranslation, final Map<String, String> actionTranslation) {
|
218 |
private static void loadTranslation(final InputStream input, final Map<String, String> menuTranslation, final Map<String, String> itemTranslation, final Map<String, String> actionTranslation) {
|
- |
|
219 |
// FIXME : l'implementation de Java est lente
|
- |
|
220 |
// com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl : 60 ms!
|
- |
|
221 |
// On pourrait passer à 1ms avec Piccolo...
|
218 |
final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
222 |
final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
219 |
|
- |
|
220 |
try {
|
223 |
try {
|
- |
|
224 |
dbFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
- |
|
225 |
dbFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
221 |
final DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
226 |
final DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
222 |
final Document doc = dBuilder.parse(input);
|
227 |
final Document doc = dBuilder.parse(input);
|
223 |
// Menus
|
228 |
// Menus
|
224 |
loadTranslation(doc, "menu", menuTranslation);
|
229 |
loadTranslation(doc, "menu", menuTranslation);
|
225 |
// Items (title, labels not related to fields...)
|
230 |
// Items (title, labels not related to fields...)
|
Line 237... |
Line 242... |
237 |
e.printStackTrace();
|
242 |
e.printStackTrace();
|
238 |
}
|
243 |
}
|
239 |
}
|
244 |
}
|
240 |
}
|
245 |
}
|
241 |
|
246 |
|
242 |
static private void loadTranslation(final Document doc, final String tagName, final Map<String, String> m) {
|
247 |
private static void loadTranslation(final Document doc, final String tagName, final Map<String, String> m) {
|
243 |
final NodeList menuChildren = doc.getElementsByTagName(tagName);
|
248 |
final NodeList menuChildren = doc.getElementsByTagName(tagName);
|
244 |
final int size = menuChildren.getLength();
|
249 |
final int size = menuChildren.getLength();
|
245 |
for (int i = 0; i < size; i++) {
|
250 |
for (int i = 0; i < size; i++) {
|
246 |
final Element element = (Element) menuChildren.item(i);
|
251 |
final Element element = (Element) menuChildren.item(i);
|
247 |
final String id = element.getAttributeNode("id").getValue();
|
252 |
final String id = element.getAttributeNode("id").getValue();
|