Java tutorial
/* * Adito * * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ package com.adito.language; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.adito.boot.ContextHolder; import com.adito.boot.SystemProperties; import com.adito.boot.Util; /** * Singleton responsible for managing registered <i>Language Packs</i> and * translation categories. * <p> * Internally, two lists of categories are maintained, one from resources called * <i>ApplicationResources.properties</i> automatically detected via the class * path and two from those registered via * {@link #registerCategory(LanguageCategory)}. * * @see LanguagePackDefinition * @see LanguageCategory */ public class LanguagePackManager { final static Log log = LogFactory.getLog(LanguagePackManager.class); // Private instance variables private Map<String, LanguagePackDefinition> languagePackDefinitions; private List<LanguageCategory> detectedCategories; private Hashtable<String, LanguageCategory> detectedHaCategories; private List<LanguageCategory> categories; private Hashtable<String, LanguageCategory> haCategories; private URL[] classpath; // Private statics private static LanguagePackManager instance; /** * Although this call is used as a singleton, it has this package * protected constructor for junit tests. */ public LanguagePackManager() { super(); languagePackDefinitions = new HashMap<String, LanguagePackDefinition>(); categories = new ArrayList<LanguageCategory>(); haCategories = new Hashtable<String, LanguageCategory>(); } /** * Register a new category. This will always be available regardless of * whether or not it is found on the classpath. This can be used by plugins * to add categories for resource files that do not use the Adito * wide <i>ApplicationResources.properties</i> resource name (e.g. * resources for audit reports). * * @param category category */ public void registerCategory(LanguageCategory category) { categories.add(category); haCategories.put(category.getId(), category); } /** * Add a new language pack definition * * @param def language pack definition */ public void addLanguagePackDefinition(LanguagePackDefinition def) { log.info("Adding new language pack '" + def.getName() + "'"); if (def.getExtensionDescriptor() != null) { log.info("Pack requires host version " + def.getExtensionDescriptor().getApplicationBundle().getRequiredHostVersion()); } languagePackDefinitions.put(def.getName(), def); } /** * Remove a language pack definition * * @param def language pack definition */ public void removeLanguagePack(LanguagePackDefinition def) { log.info("Removing language pack '" + def.getName() + "'"); languagePackDefinitions.remove(def.getName()); } /** * Get an instance of the language manager. * * @return language manager */ public static LanguagePackManager getInstance() { if (instance == null) { instance = new LanguagePackManager(); } return instance; } /** * Get an iteration of all available {@link LanguagePackDefinition} objects * registered. * * @return registered language pack definitions */ public Iterator packDefinitions() { return languagePackDefinitions.values().iterator(); } /** * Get an iteration of all available {@link Language} objects * If systemPropertiesFiler is true * the system check if some language are wanted to be hiddend. * * @param systemPropertiesFiler * @return languagePackDefinitions */ public Iterator languages(boolean systemPropertiesFiler) { List<String> hiddenLanguagesList = new ArrayList<String>(); if (systemPropertiesFiler) { String hiddenLanguages = SystemProperties.get("adito.hiddenLanguages", ""); StringTokenizer t = new StringTokenizer(hiddenLanguages, ","); while (t.hasMoreTokens()) { hiddenLanguagesList.add(t.nextToken()); } } List<Language> l = new ArrayList<Language>(); for (Iterator i = languagePackDefinitions.values().iterator(); i.hasNext();) { LanguagePackDefinition def = (LanguagePackDefinition) i.next(); for (Iterator j = def.languages(); j.hasNext();) { Language lang = (Language) j.next(); if (!l.contains(lang)) { if (!systemPropertiesFiler || !hiddenLanguagesList .contains(def.getExtensionDescriptor() != null ? def.getName() : lang.getCode())) { l.add(lang); } } } } Collections.sort(l); return l.iterator(); } /** * Get an iteration of all available {@link Language} objects * * @return languagePackDefinitions */ public Iterator languages() { return languages(false); } /** * Get all internationalisation categories. This includes all categories * registered using {@link #registerCategory(LanguageCategory)} and all * those found by looking for ApplicationResources.properties files in the * classpath. * <p> * This method returns a list of objects of type {@link LanguageCategory}. * <p> * The first call to this method may take some time as all JARS and class * directories will be scanned. * * @return language category object * @throws IOException */ public List<LanguageCategory> getCategories() throws IOException { checkCategories(); List<LanguageCategory> l = new ArrayList<LanguageCategory>(); l.addAll(detectedCategories); l.addAll(categories); return l; } synchronized void checkCategories() throws IOException { boolean classpathChanged = false; if (classpath != null) { URL[] newClasspath = ContextHolder.getContext().getContextLoaderClassPath(); if (classpathDiffer(classpath, newClasspath)) { classpath = newClasspath; classpathChanged = true; } } else { classpath = ContextHolder.getContext().getContextLoaderClassPath(); } if (detectedCategories == null || classpathChanged) { log.info("Scanning classpath for default message resources"); detectedCategories = new ArrayList<LanguageCategory>(); detectedHaCategories = new Hashtable<String, LanguageCategory>(); for (int i = 0; i < classpath.length; i++) { log.debug("Scanning " + classpath[i]); if (classpath[i].getProtocol().equals("file")) { if (classpath[i].getPath().endsWith(".jar")) { addFileJarCategory(classpath[i]); } else { addFileDirectoryCategory(classpath[i]); } } } } } boolean classpathDiffer(URL[] classpath, URL[] newClasspath) { if (classpath.length != newClasspath.length) { return true; } for (int i = 0; i < classpath.length; i++) { if (!classpath[i].equals(newClasspath[i])) { return true; } } return false; } /** * Get all internationalisation categories. This includes all categories * registered using {@link #registerCategory(LanguageCategory)} and all * those found by looking for ApplicationResources.properties files in the * classpath. * <p> * This method returns a Hashtable of objects of type * {@link LanguageCategory}. * <p> * The first call to this method may take some time as all JARS and class * directories will be scanned. * * @return language category object * @throws IOException */ public Hashtable getHaCategories() throws IOException { checkCategories(); Hashtable<String, LanguageCategory> all = new Hashtable<String, LanguageCategory>(); all.putAll(detectedHaCategories); all.putAll(haCategories); return all; } void addFileDirectoryCategory(URL url) throws IOException { File p = new File(Util.urlDecode(url.getPath())); List<File> files = new ArrayList<File>(); findFile(p, "ApplicationResources.properties", files); for (Iterator it = files.iterator(); it.hasNext();) { File f = (File) it.next(); String path = f.getAbsolutePath().substring(p.getAbsolutePath().length() + 1).replace("\\", "/"); InputStream in = null; try { String name = path; String resourceBundleId = path; int idx = name.lastIndexOf('/'); if (idx != -1) { resourceBundleId = name.substring(0, idx).replace('/', '.'); name = name.substring(idx + 1); } in = new FileInputStream(f); LanguageCategory category = new LanguageCategory(in, url, path, resourceBundleId); if (!detectedHaCategories.containsKey(category.getId())) { detectedCategories.add(category); detectedHaCategories.put(category.getId(), category); } } finally { Util.closeStream(in); } } } void addFileJarCategory(URL url) throws IOException { InputStream in = null; try { File f = new File(Util.urlDecode(url.getPath())); if (!f.exists()) { return; } in = new FileInputStream(Util.urlDecode(url.getPath())); ZipInputStream zin = new ZipInputStream(in); while (true) { ZipEntry entry = zin.getNextEntry(); if (entry == null) { break; } String path = entry.getName(); String name = path; String resourceBundleId = path; int idx = name.lastIndexOf('/'); if (idx != -1) { resourceBundleId = name.substring(0, idx).replace('/', '.'); ; name = name.substring(idx + 1); } if (name.equals("ApplicationResources.properties")) { LanguageCategory category = new LanguageCategory(zin, url, path, resourceBundleId); if (!detectedHaCategories.containsKey(category.getId())) { detectedCategories.add(category); detectedHaCategories.put(category.getId(), category); } } } } finally { Util.closeStream(in); } } /** * Get a language pack given its name (e.g. adito-language-fr). * <code>null</code> will be returned if no such language pack exists. * * @param name name * @return language pack */ public LanguagePackDefinition getLanguagePack(String name) { return (LanguagePackDefinition) languagePackDefinitions.get(name); } void findFile(File dir, String name, List<File> files) { File[] l = dir.listFiles(); for (int i = 0; l != null && i < l.length; i++) { if (l[i].isDirectory()) { findFile(l[i], name, files); } else if (l[i].isFile() && l[i].getName().equals(name)) { files.add(l[i]); } } } }