Apply patch (FELIX-3447) to introduce specific immutable collections.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1311317 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
index ba0ea97..13a323b 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -40,7 +40,6 @@
 import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.TreeSet;
-
 import org.apache.felix.framework.cache.Content;
 import org.apache.felix.framework.cache.JarContent;
 import org.apache.felix.framework.capabilityset.SimpleFilter;
@@ -48,6 +47,7 @@
 import org.apache.felix.framework.resolver.ResourceNotFoundException;
 import org.apache.felix.framework.util.CompoundEnumeration;
 import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.ImmutableList;
 import org.apache.felix.framework.util.SecurityManagerEx;
 import org.apache.felix.framework.util.Util;
 import org.apache.felix.framework.util.manifestparser.ManifestParser;
@@ -161,7 +161,7 @@
         m_revision = revision;
         m_importedPkgs = importedPkgs;
         m_requiredPkgs = requiredPkgs;
-        m_wires = Collections.unmodifiableList(wires);
+        m_wires = ImmutableList.newInstance(wires);
 
         // We need to sort the fragments and add ourself as a dependent of each one.
         // We also need to create an array of fragment contents to attach to our
@@ -246,7 +246,7 @@
                 }
             }
         }
-        m_resolvedReqs = Collections.unmodifiableList(reqList);
+        m_resolvedReqs = ImmutableList.newInstance(reqList);
 
         // Calculate resolved list of capabilities, which includes:
         // 1. All capabilities from host and any fragments except for exported
@@ -372,7 +372,7 @@
             }
         }
 
-        m_resolvedCaps = Collections.unmodifiableList(capList);
+        m_resolvedCaps = ImmutableList.newInstance(capList);
         m_includedPkgFilters = (includedPkgFilters.isEmpty())
             ? Collections.EMPTY_MAP : includedPkgFilters;
         m_excludedPkgFilters = (excludedPkgFilters.isEmpty())
@@ -400,7 +400,7 @@
         // could not be found when resolving the bundle.
         m_resolvedNativeLibs = (libList.isEmpty())
             ? null
-            : Collections.unmodifiableList(libList);
+            : ImmutableList.newInstance(libList);
 
         ClassLoader bootLoader = m_defBootClassLoader;
         if (revision.getBundle().getBundleId() != 0)
@@ -599,7 +599,7 @@
         // Technically, there is a window here where readers won't see
         // both values updates at the same time, but it seems unlikely
         // to cause any issues.
-        m_wires = Collections.unmodifiableList(wires);
+        m_wires = ImmutableList.newInstance(wires);
         m_importedPkgs = importedPkgs;
     }
 
@@ -673,7 +673,7 @@
                 {
                     entries.add(e.nextElement());
                 }
-                return Collections.unmodifiableList(entries);
+                return ImmutableList.newInstance(entries);
             }
             return Collections.EMPTY_LIST;
         }
diff --git a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
index 5981be4..ff328b2 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -24,7 +24,6 @@
 import java.net.URL;
 import java.net.URLConnection;
 import java.net.URLStreamHandler;
-import java.security.AccessControlException;
 import java.security.AllPermission;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -37,8 +36,8 @@
 import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 import java.util.Set;
-
 import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.ImmutableList;
 import org.apache.felix.framework.util.StringMap;
 import org.apache.felix.framework.util.Util;
 import org.apache.felix.framework.util.manifestparser.ManifestParser;
@@ -494,7 +493,7 @@
             new ArrayList<BundleCapability>(m_capabilities.size() + caps.size());
         newCaps.addAll(m_capabilities);
         newCaps.addAll(caps);
-        m_capabilities = Collections.unmodifiableList(newCaps);
+        m_capabilities = ImmutableList.newInstance(newCaps);
         m_headerMap.put(Constants.EXPORT_PACKAGE, convertCapabilitiesToHeaders(m_headerMap));
     }
 
@@ -598,6 +597,7 @@
             };
     }
 
+    @Override
     protected InetAddress getHostAddress(URL u)
     {
         // the extension URLs do not address real hosts
diff --git a/framework/src/main/java/org/apache/felix/framework/WovenClassImpl.java b/framework/src/main/java/org/apache/felix/framework/WovenClassImpl.java
index 8402d80..2f44e5a 100644
--- a/framework/src/main/java/org/apache/felix/framework/WovenClassImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/WovenClassImpl.java
@@ -21,14 +21,12 @@
 import java.security.ProtectionDomain;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
+import org.apache.felix.framework.util.ImmutableList;
 import org.apache.felix.framework.util.manifestparser.ManifestParser;
-import org.apache.felix.framework.util.manifestparser.ParsedHeaderClause;
 import org.osgi.framework.AdminPermission;
-import org.osgi.framework.Constants;
 import org.osgi.framework.hooks.weaving.WovenClass;
 import org.osgi.framework.wiring.BundleRequirement;
 import org.osgi.framework.wiring.BundleWiring;
@@ -55,8 +53,8 @@
         m_definedClass = definedClass;
         m_bytes = (bytes == null) ? m_bytes : bytes;
         m_imports = (imports == null)
-            ? Collections.unmodifiableList(m_imports)
-            : Collections.unmodifiableList(imports);
+            ? ImmutableList.newInstance(m_imports)
+            : ImmutableList.newInstance(imports);
     }
 
     public synchronized byte[] getBytes()
@@ -365,7 +363,7 @@
         return m_imports.subList(i, i1);
     }
 
-    byte[] _getBytes() 
+    byte[] _getBytes()
     {
         byte[] bytes = m_bytes;
         if (m_isComplete)
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/WrappedRevision.java b/framework/src/main/java/org/apache/felix/framework/resolver/WrappedRevision.java
index 81c41ee..bdd2a2d 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/WrappedRevision.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/WrappedRevision.java
@@ -19,8 +19,8 @@
 package org.apache.felix.framework.resolver;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
+import org.apache.felix.framework.util.ImmutableList;
 import org.apache.felix.framework.wiring.BundleCapabilityImpl;
 import org.apache.felix.framework.wiring.BundleRequirementImpl;
 import org.osgi.framework.Bundle;
@@ -88,7 +88,7 @@
                     }
                 }
             }
-            m_cachedCapabilities = Collections.unmodifiableList(caps);
+            m_cachedCapabilities = ImmutableList.newInstance(caps);
         }
         return m_cachedCapabilities;
     }
@@ -119,7 +119,7 @@
                     }
                 }
             }
-            m_cachedRequirements = Collections.unmodifiableList(reqs);
+            m_cachedRequirements = ImmutableList.newInstance(reqs);
         }
         return m_cachedRequirements;
     }
@@ -139,6 +139,7 @@
         return m_host.getBundle();
     }
 
+    @Override
     public String toString()
     {
         return m_host.toString();
diff --git a/framework/src/main/java/org/apache/felix/framework/util/ImmutableList.java b/framework/src/main/java/org/apache/felix/framework/util/ImmutableList.java
new file mode 100644
index 0000000..f0a1a9c
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/util/ImmutableList.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.AbstractList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.RandomAccess;
+
+public class ImmutableList<E> extends AbstractList<E> implements RandomAccess
+{
+    final Object[] elements;
+
+    public static <E> ImmutableList<E> newInstance(E... elements)
+    {
+        return new ImmutableList<E>(elements);
+    }
+
+    public static <E> ImmutableList<E> newInstance(Collection<? extends E> elements)
+    {
+        if (elements instanceof ImmutableList)
+        {
+            return (ImmutableList<E>) elements;
+        }
+        else
+        {
+            return new ImmutableList<E>(elements);
+        }
+    }
+
+    protected ImmutableList(E... elements)
+    {
+        this.elements = elements.clone();
+    }
+
+    protected ImmutableList(Collection<? extends E> elements)
+    {
+        this.elements = elements.toArray();
+    }
+
+    public E get(int index)
+    {
+        return (E) elements[index];
+    }
+
+    public int size()
+    {
+        return elements.length;
+    }
+
+    @Override
+    public boolean remove(Object o)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> clctn)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Iterator<E> iterator()
+    {
+        return listIterator();
+    }
+
+    @Override
+    public ListIterator<E> listIterator(int index)
+    {
+        return new ListItr(index);
+    }
+
+    private class ListItr implements ListIterator<E>
+    {
+        int cursor;
+
+        private ListItr(int cursor)
+        {
+            this.cursor = cursor;
+        }
+
+        public boolean hasNext()
+        {
+            return cursor != size();
+        }
+
+        public E next()
+        {
+            return (E) elements[cursor++];
+        }
+
+        public boolean hasPrevious()
+        {
+            return cursor != 0;
+        }
+
+        public E previous()
+        {
+            return (E) elements[--cursor];
+        }
+
+        public int nextIndex()
+        {
+            return cursor;
+        }
+
+        public int previousIndex()
+        {
+            return cursor - 1;
+        }
+
+        public void remove()
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        public void set(E e)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        public void add(E e)
+        {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/util/ImmutableMap.java b/framework/src/main/java/org/apache/felix/framework/util/ImmutableMap.java
new file mode 100644
index 0000000..1ee3140
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/util/ImmutableMap.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class ImmutableMap<K, V> extends AbstractMap<K, V>
+{
+    final Entry<K, V>[] entries;
+
+    public static <K, V> ImmutableMap<K, V> newInstance(Entry<K, V>... entries)
+    {
+        return new ImmutableMap<K, V>(entries);
+    }
+
+    public static <K, V> ImmutableMap<K, V> newInstance(Map<K, V> entries)
+    {
+        if (entries instanceof ImmutableMap)
+        {
+            return (ImmutableMap<K, V>) entries;
+        }
+        else
+        {
+            return new ImmutableMap<K, V>(entries);
+        }
+    }
+
+    protected ImmutableMap(Entry<K, V>[] entries)
+    {
+        this.entries = entries.clone();
+    }
+
+    protected ImmutableMap(Map<K, V> map)
+    {
+        this.entries = map.entrySet().toArray(new Entry[map.size()]);
+    }
+
+    @Override
+    public V get(Object key)
+    {
+        if (key == null)
+        {
+            for (int i = 0; i < entries.length; i++)
+            {
+                if (entries[i].getKey() == null)
+                {
+                    return entries[i].getValue();
+                }
+            }
+        }
+        else
+        {
+            for (int i = 0; i < entries.length; i++)
+            {
+                if (key.equals(entries[i].getKey()))
+                {
+                    return entries[i].getValue();
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Set<Entry<K, V>> entrySet()
+    {
+        return new EntrySet();
+    }
+
+    private class EntrySet extends AbstractSet<Entry<K, V>>
+    {
+        @Override
+        public Iterator<Entry<K, V>> iterator()
+        {
+            return new EntryItr(0);
+        }
+
+        @Override
+        public int size()
+        {
+            return entries.length;
+        }
+    }
+
+    private class EntryItr implements Iterator<Entry<K, V>>
+    {
+        int cursor;
+
+        private EntryItr(int cursor)
+        {
+            this.cursor = cursor;
+        }
+
+        public boolean hasNext()
+        {
+            return cursor != size();
+        }
+
+        public Entry<K, V> next()
+        {
+            return entries[cursor++];
+        }
+
+        public void remove()
+        {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/util/StringMap.java b/framework/src/main/java/org/apache/felix/framework/util/StringMap.java
index 7ad7f78..dd040fd 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/StringMap.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/StringMap.java
@@ -27,108 +27,141 @@
  * map will be converted to a <tt>String</tt> using the
  * <tt>toString()</tt> method, since it is only intended to
  * compare strings.
-**/
-public class StringMap implements Map
+ **/
+public class StringMap extends AbstractMap<String, Object>
 {
-    private TreeMap m_map;
-
-    public StringMap()
-    {
-        this(true);
-    }
+    private final TreeMap<String, KeyValueEntry> m_map = new TreeMap<String, KeyValueEntry>();
 
     public StringMap(boolean caseSensitive)
     {
-        m_map = new TreeMap(new StringComparator(caseSensitive));
     }
 
     public StringMap(Map map, boolean caseSensitive)
     {
-        this(caseSensitive);
         putAll(map);
     }
 
-    public boolean isCaseSensitive()
-    {
-        return ((StringComparator) m_map.comparator()).isCaseSensitive();
-    }
-
-    public void setCaseSensitive(boolean b)
-    {
-        if (isCaseSensitive() != b)
-        {
-            TreeMap map = new TreeMap(new StringComparator(b));
-            map.putAll(m_map);
-            m_map = map;
-        }
-    }
-
+    @Override
     public int size()
     {
         return m_map.size();
     }
 
+    @Override
     public boolean isEmpty()
     {
         return m_map.isEmpty();
     }
 
+    @Override
     public boolean containsKey(Object arg0)
     {
-        return m_map.containsKey(arg0);
+        return m_map.containsKey(arg0.toString().toUpperCase());
     }
 
+    @Override
     public boolean containsValue(Object arg0)
     {
         return m_map.containsValue(arg0);
     }
 
+    @Override
     public Object get(Object arg0)
     {
-        return m_map.get(arg0);
+        KeyValueEntry kve = m_map.get(arg0.toString().toUpperCase());
+        return (kve != null) ? kve.value : null;
     }
 
-    public Object put(Object key, Object value)
+    @Override
+    public Object put(String key, Object value)
     {
-        return m_map.put(key.toString(), value);
+        KeyValueEntry kve = (KeyValueEntry) m_map.put(key.toUpperCase(), new KeyValueEntry(key, value));
+        return (kve != null) ? kve.value : null;
     }
 
-    public void putAll(Map map)
+    @Override
+    public void putAll(Map<? extends String, ? extends Object> map)
     {
-        for (Iterator it = map.entrySet().iterator(); it.hasNext(); )
+        for (Map.Entry<? extends String, ? extends Object> e : map.entrySet())
         {
-            Map.Entry entry = (Map.Entry) it.next();
-            put(entry.getKey(), entry.getValue());
+            put(e.getKey(), e.getValue());
         }
     }
 
+    @Override
     public Object remove(Object arg0)
     {
-        return m_map.remove(arg0);
+        KeyValueEntry kve = m_map.remove(arg0.toString().toUpperCase());
+        return (kve != null) ? kve.value : null;
     }
 
+    @Override
     public void clear()
     {
         m_map.clear();
     }
 
-    public Set keySet()
+    public Set<Entry<String, Object>> entrySet()
     {
-        return m_map.keySet();
+        return new AbstractSet<Entry<String, Object>>()
+        {
+            @Override
+            public Iterator<Entry<String, Object>> iterator()
+            {
+                return new Iterator<Entry<String, Object>>()
+                {
+                    Iterator<Entry<String, KeyValueEntry>> it = m_map.entrySet().iterator();
+
+                    public boolean hasNext()
+                    {
+                        return it.hasNext();
+                    }
+
+                    public Entry<String, Object> next()
+                    {
+                        return it.next().getValue();
+                    }
+
+                    public void remove()
+                    {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+
+            @Override
+            public int size()
+            {
+                return m_map.size();
+            }
+        };
     }
 
-    public Collection values()
+    private class KeyValueEntry implements Entry<String, Object>
     {
-        return m_map.values();
-    }
+        private KeyValueEntry(String key, Object value)
+        {
+            this.key = key;
+            this.value = value;
+        }
 
-    public Set entrySet()
-    {
-        return m_map.entrySet();
-    }
+        public String getKey()
+        {
+            return key;
+        }
 
-    public String toString()
-    {
-        return m_map.toString();
+        public Object getValue()
+        {
+            return value;
+        }
+
+        public Object setValue(Object value)
+        {
+            Object v = this.value;
+            this.value = value;
+            return v;
+        }
+        String key;
+        Object value;
     }
 }
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java b/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
index ba36535..dd08ba5 100644
--- a/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
@@ -26,6 +26,7 @@
 import java.util.List;
 import java.util.StringTokenizer;
 import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.util.ImmutableMap;
 import org.apache.felix.framework.util.Util;
 import org.apache.felix.framework.util.manifestparser.ManifestParser;
 import org.osgi.framework.Constants;
@@ -50,8 +51,8 @@
     {
         m_namespace = namespace;
         m_revision = revision;
-        m_dirs = Collections.unmodifiableMap(dirs);
-        m_attrs = Collections.unmodifiableMap(attrs);
+        m_dirs = ImmutableMap.newInstance(dirs);
+        m_attrs = ImmutableMap.newInstance(attrs);
 
         // Find all export directives: uses, mandatory, include, and exclude.
 
@@ -187,6 +188,7 @@
         return included && !excluded;
     }
 
+    @Override
     public String toString()
     {
         if (m_revision == null)
diff --git a/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java b/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
index eabd5bc..0477c70 100644
--- a/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
@@ -18,14 +18,11 @@
  */
 package org.apache.felix.framework.wiring;
 
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import org.apache.felix.framework.capabilityset.CapabilitySet;
 import org.apache.felix.framework.capabilityset.SimpleFilter;
-import org.apache.felix.framework.util.VersionRange;
+import org.apache.felix.framework.util.ImmutableMap;
 import org.osgi.framework.Constants;
 import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleRequirement;
@@ -46,8 +43,8 @@
     {
         m_revision = revision;
         m_namespace = namespace;
-        m_dirs = Collections.unmodifiableMap(dirs);
-        m_attrs = Collections.unmodifiableMap(attrs);
+        m_dirs = ImmutableMap.newInstance(dirs);
+        m_attrs = ImmutableMap.newInstance(attrs);
         m_filter = filter;
 
         // Find resolution import directives.
@@ -102,6 +99,7 @@
         return m_filter;
     }
 
+    @Override
     public String toString()
     {
         return "[" + m_revision + "] " + m_namespace + "; " + getFilter().toString();