Applied patch (FELIX-27) to add support for bundle header localization.


git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@497281 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index c3c724b..51779bb 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -23,6 +23,7 @@
 import java.net.URL;
 import java.util.*;
 
+import org.apache.felix.framework.util.MapToDictionary;
 import org.osgi.framework.*;
 
 class BundleImpl implements Bundle
@@ -123,6 +124,11 @@
 
     public Dictionary getHeaders()
     {
+        return getHeaders(Locale.getDefault().toString());
+    }
+
+    public Dictionary getHeaders(String locale)
+    {
         Object sm = System.getSecurityManager();
 
         if (sm != null)
@@ -130,7 +136,7 @@
             ((SecurityManager) sm).checkPermission(new AdminPermission(this,
                 AdminPermission.METADATA));
         }
-        return m_felix.getBundleHeaders(this);
+        return m_felix.getBundleHeaders(this, locale);
     }
 
     public long getLastModified()
@@ -303,8 +309,7 @@
 
     public String getSymbolicName()
     {
-        return (String) m_felix.getBundleHeaders(this).get(
-            Constants.BUNDLE_SYMBOLICNAME);
+        return (String) m_felix.getBundleSymbolicName(this);
     }
 
     public boolean hasPermission(Object obj)
@@ -394,25 +399,6 @@
         return "[" + getBundleId() +"]";
     }
 
-    //
-    // PLACE FOLLOWING METHODS INTO PROPER LOCATION ONCE IMPLEMENTED.
-    //
-
-    public Dictionary getHeaders(String locale)
-    {
-        // TODO: Implement Bundle.getHeaders(String locale)
-        // Should be done after [#FELIX-27] resolution
-        Object sm = System.getSecurityManager();
-
-        if (sm != null)
-        {
-            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
-                AdminPermission.METADATA));
-        }
-
-        return null;
-    }
-
     public boolean equals(Object obj)
     {
         if (obj instanceof BundleImpl)
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleInfo.java b/framework/src/main/java/org/apache/felix/framework/BundleInfo.java
index 60a7ec3..ca7737c 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleInfo.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleInfo.java
@@ -18,9 +18,12 @@
  */
 package org.apache.felix.framework;
 
-import java.util.Map;
+import java.io.IOException;
+import java.net.URL;
+import java.util.*;
 
 import org.apache.felix.framework.cache.BundleArchive;
+import org.apache.felix.moduleloader.IContentLoader;
 import org.apache.felix.moduleloader.IModule;
 import org.osgi.framework.*;
 
@@ -32,6 +35,9 @@
     private int m_state = 0;
     private BundleActivator m_activator = null;
     private BundleContext m_context = null;
+    private Map m_cachedHeaders = new HashMap();
+    private long m_cachedHeadersTimestamp;
+
     // Indicates whether the bundle is stale, meaning that it has
     // been refreshed and completely removed from the framework.
     private boolean m_stale = false;
@@ -201,6 +207,143 @@
         }
     }
 
+    public Map getCurrentLocalizedHeader(String locale)
+    {
+        synchronized (m_cachedHeaders)
+        {
+            // If the bundle has been updated, clear the cached headers
+            if (getLastModified() > m_cachedHeadersTimestamp)
+            {
+                m_cachedHeaders.clear();
+            }
+            else
+            {
+                // Check if headers for this locale have already been resolved
+                if (m_cachedHeaders.containsKey(locale))
+                {
+                    return (Map) m_cachedHeaders.get(locale);
+                }
+            }
+        }
+
+        Map headers;
+        try
+        {
+            Map rawHeaders = m_archive.getRevision(m_archive.getRevisionCount() - 1).getManifestHeader();
+            headers = new HashMap(rawHeaders.size());
+            headers.putAll(rawHeaders);
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Error reading manifest from bundle archive.",
+                ex);
+            return null;
+        }
+
+        // Check to see if we actually need to localize anything
+        boolean needsLocalization = false;
+        for (Iterator it = headers.values().iterator(); it.hasNext(); )
+        {
+            if (((String) it.next()).startsWith("%"))
+            {
+                needsLocalization = true;
+                break;
+            }
+        }
+
+        if (!needsLocalization)
+        {
+            // If localization is not needed, just cache the headers and return them as-is
+            // Not sure if this is useful
+            updateHeaderCache(locale, headers);
+            return headers;
+        }
+
+        // Do localization here and return the localized headers
+        IContentLoader loader = this.getCurrentModule().getContentLoader();
+
+        String basename = (String) headers.get(Constants.BUNDLE_LOCALIZATION);
+        if (basename == null)
+        {
+            basename = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
+        }
+
+        // Create ordered list of files to load properties from
+        List resourceList = createResourceList(basename, locale);
+
+        // Create a merged props file with all available props for this locale
+        Properties mergedProperties = new Properties();
+        for (Iterator it = resourceList.iterator(); it.hasNext(); )
+        {
+            URL temp = loader.getResource(it.next() + ".properties");
+            if (temp == null)
+            {
+                continue;
+            }
+            try
+            {
+                mergedProperties.load(temp.openConnection().getInputStream());
+            }
+            catch (IOException ex)
+            {
+                // File doesn't exist, just continue loop
+            }
+        }
+
+        // Resolve all localized header entries
+        for (Iterator it = headers.entrySet().iterator(); it.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) it.next();
+            String value = (String) entry.getValue();
+            if (value.startsWith("%"))
+            {
+                String newvalue;
+                String key = value.substring(value.indexOf("%") + 1);
+                newvalue = mergedProperties.getProperty(key);
+                if (newvalue==null)
+                {
+                    newvalue = key;
+                }
+                entry.setValue(newvalue);
+            }
+        }
+
+        updateHeaderCache(locale, headers);
+        return headers;
+    }
+
+    private void updateHeaderCache(String locale, Map localizedHeaders)
+    {
+        synchronized(m_cachedHeaders)
+        {
+            m_cachedHeaders.put(locale, localizedHeaders);
+            m_cachedHeadersTimestamp = System.currentTimeMillis();
+        }
+    }
+
+    private List createResourceList(String basename, String locale)
+    {
+        List result = new ArrayList(4);
+
+        StringTokenizer tokens;
+        StringBuffer tempLocale = new StringBuffer(basename);
+
+        result.add(tempLocale.toString());
+
+        if (locale.length() > 0)
+        {
+            tokens = new StringTokenizer(locale, "_");
+            while (tokens.hasMoreTokens())
+            {
+                tempLocale.append("_").append(tokens.nextToken());
+                result.add(tempLocale.toString());
+            }
+        }
+        return result;
+    }
+    
     public int getState()
     {
         return m_state;
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index f4cf15b..24aaab2 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -24,15 +24,13 @@
 import java.security.CodeSource;
 import java.security.ProtectionDomain;
 import java.util.*;
-import org.apache.felix.framework.cache.BundleArchive;
-import org.apache.felix.framework.cache.BundleCache;
-import org.apache.felix.framework.cache.SystemBundleArchive;
+
+import org.apache.felix.framework.cache.*;
 import org.apache.felix.framework.searchpolicy.*;
 import org.apache.felix.framework.util.*;
 import org.apache.felix.framework.util.manifestparser.*;
 import org.apache.felix.moduleloader.*;
 import org.osgi.framework.*;
-
 import org.osgi.service.packageadmin.ExportedPackage;
 import org.osgi.service.startlevel.StartLevel;
 
@@ -1028,11 +1026,22 @@
     //
 
     /**
-     * Implementation for Bundle.getHeaders().
+     * Implementation for Bundle.getSymbolicName().
     **/
-    protected Dictionary getBundleHeaders(BundleImpl bundle)
+    protected String getBundleSymbolicName(BundleImpl bundle)
     {
-        return new MapToDictionary(bundle.getInfo().getCurrentHeader());
+        return (String) bundle.getInfo().getCurrentHeader().get(Constants.BUNDLE_SYMBOLICNAME);
+    }
+
+    /**
+     * Get bundle headers and resolve any localized strings from resource bundles.
+     * @param bundle
+     * @param locale
+     * @return localized bundle headers dictionary.
+    **/
+    protected Dictionary getBundleHeaders(BundleImpl bundle, String locale)
+    {
+        return new MapToDictionary(bundle.getInfo().getCurrentLocalizedHeader(locale));
     }
 
     /**