Synthesize missing directories in JAR files for entry-related methods. (FELIX-1210)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@939795 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/EntryFilterEnumeration.java b/framework/src/main/java/org/apache/felix/framework/EntryFilterEnumeration.java
new file mode 100644
index 0000000..ae5f8aa
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/EntryFilterEnumeration.java
@@ -0,0 +1,239 @@
+/*
+ * 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;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.resolver.Module;
+
+class EntryFilterEnumeration implements Enumeration
+{
+ private final BundleImpl m_bundle;
+ private final List<Enumeration> m_enumerations;
+ private final List<Module> m_modules;
+ private int m_moduleIndex = 0;
+ private final String m_path;
+ private final List<String> m_filePattern;
+ private final boolean m_recurse;
+ private final boolean m_isURLValues;
+ private final Set<String> m_dirEntries = new HashSet();
+ private final List<Object> m_nextEntries = new ArrayList(2);
+
+ public EntryFilterEnumeration(
+ BundleImpl bundle, boolean includeFragments, String path,
+ String filePattern, boolean recurse, boolean isURLValues)
+ {
+ m_bundle = bundle;
+ Module bundleModule = m_bundle.getCurrentModule();
+ List<Module> fragmentModules = ((ModuleImpl) bundleModule).getFragments();
+ if (includeFragments && (fragmentModules != null))
+ {
+ m_modules = new ArrayList(fragmentModules.size() + 1);
+ m_modules.addAll(fragmentModules);
+ }
+ else
+ {
+ m_modules = new ArrayList(1);
+ }
+ m_modules.add(0, bundleModule);
+ m_enumerations = new ArrayList(m_modules.size());
+ for (int i = 0; i < m_modules.size(); i++)
+ {
+ m_enumerations.add(m_modules.get(i).getContent() != null ?
+ m_modules.get(i).getContent().getEntries() : null);
+ }
+ m_recurse = recurse;
+ m_isURLValues = isURLValues;
+
+ // Sanity check the parameters.
+ if (path == null)
+ {
+ throw new IllegalArgumentException("The path for findEntries() cannot be null.");
+ }
+ // Strip leading '/' if present.
+ if ((path.length() > 0) && (path.charAt(0) == '/'))
+ {
+ path = path.substring(1);
+ }
+ // Add a '/' to the end if not present.
+ if ((path.length() > 0) && (path.charAt(path.length() - 1) != '/'))
+ {
+ path = path + "/";
+ }
+ m_path = path;
+
+ // File pattern defaults to "*" if not specified.
+ filePattern = (filePattern == null) ? "*" : filePattern;
+
+ m_filePattern = SimpleFilter.parseSubstring(filePattern);
+
+ findNext();
+ }
+
+ public synchronized boolean hasMoreElements()
+ {
+ return (m_nextEntries.size() != 0);
+ }
+
+ public synchronized Object nextElement()
+ {
+ if (m_nextEntries.size() == 0)
+ {
+ throw new NoSuchElementException("No more entries.");
+ }
+ Object last = m_nextEntries.remove(0);
+ findNext();
+ return last;
+ }
+
+ private void findNext()
+ {
+ // This method filters the content entry enumeration, such that
+ // it only displays the contents of the directory specified by
+ // the path argument either recursively or not; much like using
+ // "ls -R" or "ls" to list the contents of a directory, respectively.
+ if (m_enumerations == null)
+ {
+ return;
+ }
+ while ((m_moduleIndex < m_enumerations.size()) && (m_nextEntries.size() == 0))
+ {
+ while (m_enumerations.get(m_moduleIndex) != null
+ && m_enumerations.get(m_moduleIndex).hasMoreElements()
+ && m_nextEntries.size() == 0)
+ {
+ // Get the current entry to determine if it should be filtered or not.
+ String entryName = (String) m_enumerations.get(m_moduleIndex).nextElement();
+ // Check to see if the current entry is a descendent of the specified path.
+ if (!entryName.equals(m_path) && entryName.startsWith(m_path))
+ {
+ // Cached entry URL. If we are returning URLs, we use this
+ // cached URL to avoid doing multiple URL lookups from a module
+ // when synthesizing directory URLs.
+ URL entryURL = null;
+
+ // If the current entry is in a subdirectory of the specified path,
+ // get the index of the slash character.
+ int dirSlashIdx = entryName.indexOf('/', m_path.length());
+
+ // JAR files are supposed to contain entries for directories,
+ // but not all do. So calculate the directory for this entry
+ // and see if we've already seen an entry for the directory.
+ // If not, synthesize an entry for the directory. If we are
+ // doing a recursive match, we need to synthesize each matching
+ // subdirectory of the entry.
+ if (dirSlashIdx >= 0)
+ {
+ // Start synthesizing directories for the current entry
+ // at the subdirectory after the initial path.
+ int subDirSlashIdx = dirSlashIdx;
+ String dir;
+ do
+ {
+ // Calculate the subdirectory name.
+ dir = entryName.substring(0, subDirSlashIdx + 1);
+ // If we have not seen this directory before, then record
+ // it and potentially synthesize an entry for it.
+ if (!m_dirEntries.contains(dir))
+ {
+ // Record it.
+ m_dirEntries.add(dir);
+ // If the entry is actually a directory entry (i.e.,
+ // it ends with a slash), then we don't need to
+ // synthesize an entry since it exists; otherwise,
+ // synthesize an entry if it matches the file pattern.
+ if (entryName.length() != (subDirSlashIdx + 1))
+ {
+ // See if the file pattern matches the last
+ // element of the path.
+ if (SimpleFilter.compareSubstring(
+ m_filePattern, getLastPathElement(dir)))
+ {
+ if (m_isURLValues)
+ {
+ entryURL = (entryURL == null)
+ ? m_modules.get(m_moduleIndex).getEntry(entryName)
+ : entryURL;
+ try
+ {
+ m_nextEntries.add(new URL(entryURL, "/" + dir));
+ }
+ catch (MalformedURLException ex)
+ {
+ }
+ }
+ else
+ {
+ m_nextEntries.add(dir);
+ }
+ }
+ }
+ }
+ // Now prepare to synthesize the next subdirectory
+ // if we are matching recursively.
+ subDirSlashIdx = entryName.indexOf('/', dir.length());
+ }
+ while (m_recurse && (subDirSlashIdx >= 0));
+ }
+
+ // Now we actually need to check if the current entry itself should
+ // be filtered or not. If we are recursive or the current entry
+ // is a child (not a grandchild) of the initial path, then we need
+ // to check if it matches the file pattern.
+ if (m_recurse || (dirSlashIdx < 0) || (dirSlashIdx == entryName.length() - 1))
+ {
+ // See if the file pattern matches the last element of the path.
+ if (SimpleFilter.compareSubstring(
+ m_filePattern, getLastPathElement(entryName)))
+ {
+ if (m_isURLValues)
+ {
+ entryURL = (entryURL == null)
+ ? m_modules.get(m_moduleIndex).getEntry(entryName)
+ : entryURL;
+ m_nextEntries.add(entryURL);
+ }
+ else
+ {
+ m_nextEntries.add(entryName);
+ }
+ }
+ }
+ }
+ }
+ if (m_nextEntries.size() == 0)
+ {
+ m_moduleIndex++;
+ }
+ }
+ }
+
+ private static String getLastPathElement(String entryName)
+ {
+ int endIdx = (entryName.charAt(entryName.length() - 1) == '/')
+ ? entryName.length() - 1
+ : entryName.length();
+ int startIdx = (entryName.charAt(entryName.length() - 1) == '/')
+ ? entryName.lastIndexOf('/', endIdx - 1) + 1
+ : entryName.lastIndexOf('/', endIdx) + 1;
+ return entryName.substring(startIdx, endIdx);
+ }
+}
\ No newline at end of file
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 590cc56..8134ed4 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -1466,7 +1466,34 @@
{
throw new IllegalStateException("The bundle is uninstalled.");
}
- return bundle.getCurrentModule().getEntry(name);
+
+ URL url = bundle.getCurrentModule().getEntry(name);
+
+ // Some JAR files do not contain directory entries, so if
+ // the entry wasn't found and is a directory, scan the entries
+ // to see if we should synthesize an entry for it.
+ if ((url == null) && name.endsWith("/") && !name.equals("/"))
+ {
+ // Use the entry filter enumeration to search the bundle content
+ // recursively for matching entries and return URLs to them.
+ Enumeration enumeration =
+ new EntryFilterEnumeration(bundle, false, name, "*", true, true);
+ // If the enumeration has elements, then that means we need
+ // to synthesize the directory entry.
+ if (enumeration.hasMoreElements())
+ {
+ URL entryURL = (URL) enumeration.nextElement();
+ try
+ {
+ url = new URL(entryURL, ((name.charAt(0) == '/') ? name : "/" + name));
+ }
+ catch (MalformedURLException ex)
+ {
+ url = null;
+ }
+ }
+ }
+ return url;
}
/**
@@ -1481,14 +1508,15 @@
// Get the entry enumeration from the module content and
// create a wrapper enumeration to filter it.
- Enumeration enumeration = new GetEntryPathsEnumeration(bundle, path);
+ Enumeration enumeration =
+ new EntryFilterEnumeration(bundle, false, path, "*", false, false);
// Return the enumeration if it has elements.
return (!enumeration.hasMoreElements()) ? null : enumeration;
}
/**
- * Implementation for findEntries().
+ * Implementation for Bundle.findEntries().
**/
Enumeration findBundleEntries(
BundleImpl bundle, String path, String filePattern, boolean recurse)
@@ -1499,7 +1527,7 @@
// Get the entry enumeration from the module content and
// create a wrapper enumeration to filter it.
Enumeration enumeration =
- new FindEntriesEnumeration(bundle, path, filePattern, recurse);
+ new EntryFilterEnumeration(bundle, true, path, filePattern, recurse, true);
// Return the enumeration if it has elements.
return (!enumeration.hasMoreElements()) ? null : enumeration;
diff --git a/framework/src/main/java/org/apache/felix/framework/FindEntriesEnumeration.java b/framework/src/main/java/org/apache/felix/framework/FindEntriesEnumeration.java
deleted file mode 100644
index b0b13f9..0000000
--- a/framework/src/main/java/org/apache/felix/framework/FindEntriesEnumeration.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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;
-
-import java.util.*;
-import org.apache.felix.framework.capabilityset.SimpleFilter;
-import org.apache.felix.framework.resolver.Module;
-
-
-class FindEntriesEnumeration implements Enumeration
-{
- private final BundleImpl m_bundle;
- private final List<Enumeration> m_enumerations;
- private final List<Module> m_modules;
- private int m_moduleIndex = 0;
- private final String m_path;
- private final List<String> m_filePattern;
- private final boolean m_recurse;
- private Object m_next = null;
-
- public FindEntriesEnumeration(
- BundleImpl bundle, String path, String filePattern, boolean recurse)
- {
- m_bundle = bundle;
- Module bundleModule = m_bundle.getCurrentModule();
- List<Module> fragmentModules = ((ModuleImpl) bundleModule).getFragments();
- if (fragmentModules == null)
- {
- fragmentModules = new ArrayList<Module>(0);
- }
- m_modules = new ArrayList<Module>(fragmentModules.size() + 1);
- m_modules.add(bundleModule);
- m_modules.addAll(fragmentModules);
- m_enumerations = new ArrayList<Enumeration>(m_modules.size());
- for (int i = 0; i < m_modules.size(); i++)
- {
- m_enumerations.add(m_modules.get(i).getContent() != null ?
- m_modules.get(i).getContent().getEntries() : null);
- }
- m_recurse = recurse;
-
- // Sanity check the parameters.
- if (path == null)
- {
- throw new IllegalArgumentException("The path for findEntries() cannot be null.");
- }
- // Strip leading '/' if present.
- if ((path.length() > 0) && (path.charAt(0) == '/'))
- {
- path = path.substring(1);
- }
- // Add a '/' to the end if not present.
- if ((path.length() > 0) && (path.charAt(path.length() - 1) != '/'))
- {
- path = path + "/";
- }
- m_path = path;
-
- // File pattern defaults to "*" if not specified.
- filePattern = (filePattern == null) ? "*" : filePattern;
-
- m_filePattern = SimpleFilter.parseSubstring(filePattern);
-
- m_next = findNext();
- }
-
- public synchronized boolean hasMoreElements()
- {
- return (m_next != null);
- }
-
- public synchronized Object nextElement()
- {
- if (m_next == null)
- {
- throw new NoSuchElementException("No more entry paths.");
- }
- Object last = m_next;
- m_next = findNext();
- return last;
- }
-
- private Object findNext()
- {
- // This method filters the content entry enumeration, such that
- // it only displays the contents of the directory specified by
- // the path argument either recursively or not; much like using
- // "ls -R" or "ls" to list the contents of a directory, respectively.
- if (m_enumerations == null)
- {
- return null;
- }
- while (m_moduleIndex < m_enumerations.size())
- {
- while (m_enumerations.get(m_moduleIndex) != null
- && m_enumerations.get(m_moduleIndex).hasMoreElements())
- {
- // Get the next entry name.
- String entryName = (String) m_enumerations.get(m_moduleIndex).nextElement();
- // Check to see if it is a descendent of the specified path.
- if (!entryName.equals(m_path) && entryName.startsWith(m_path))
- {
- // If this is recursive search, then try to match any
- // entry path that starts with the specified path;
- // otherwise, only try to match children of the specified
- // path and not any grandchild. This code uses the knowledge
- // that content entries corresponding to directories end in '/'.
- int idx = entryName.indexOf('/', m_path.length());
- if (m_recurse || (idx < 0) || (idx == (entryName.length() - 1)))
- {
- // Get the last element of the entry path, not including
- // the '/' if it is a directory.
- int endIdx = (entryName.charAt(entryName.length() - 1) == '/')
- ? entryName.length() - 1
- : entryName.length();
- int startIdx = (entryName.charAt(entryName.length() - 1) == '/')
- ? entryName.lastIndexOf('/', endIdx - 1) + 1
- : entryName.lastIndexOf('/', endIdx) + 1;
- String lastElement = entryName.substring(startIdx, endIdx);
-
- // See if the file pattern matches the last element of the path.
- if (SimpleFilter.compareSubstring(m_filePattern, lastElement))
- {
- // Convert entry name into an entry URL.
- return m_modules.get(m_moduleIndex).getEntry(entryName);
- }
- }
- }
- }
- m_moduleIndex++;
- }
-
- return null;
- }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/GetEntryPathsEnumeration.java b/framework/src/main/java/org/apache/felix/framework/GetEntryPathsEnumeration.java
deleted file mode 100644
index 5398708..0000000
--- a/framework/src/main/java/org/apache/felix/framework/GetEntryPathsEnumeration.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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;
-
-import java.util.Enumeration;
-import java.util.NoSuchElementException;
-
-class GetEntryPathsEnumeration implements Enumeration
-{
- private final BundleImpl m_bundle;
- private final Enumeration m_enumeration;
- private final String m_path;
- private Object m_next = null;
-
- public GetEntryPathsEnumeration(BundleImpl bundle, String path)
- {
- m_bundle = bundle;
- m_enumeration = m_bundle.getCurrentModule().getContent().getEntries();
-
- // Sanity check the parameters.
- if (path == null)
- {
- throw new IllegalArgumentException("The path for findEntries() cannot be null.");
- }
- // Strip leading '/' if present.
- if ((path.length() > 0) && (path.charAt(0) == '/'))
- {
- path = path.substring(1);
- }
- // Add a '/' to the end if not present.
- if ((path.length() > 0) && (path.charAt(path.length() - 1) != '/'))
- {
- path = path + "/";
- }
- m_path = path;
-
- m_next = findNext();
- }
-
- public synchronized boolean hasMoreElements()
- {
- return (m_next != null);
- }
-
- public synchronized Object nextElement()
- {
- if (m_next == null)
- {
- throw new NoSuchElementException("No more entry paths.");
- }
- Object last = m_next;
- m_next = findNext();
- return last;
- }
-
- private Object findNext()
- {
- // This method filters the content entry enumeration, such that
- // it only displays the contents of the directory specified by
- // the path argument; much like using "ls" to list the contents
- // of a directory.
- while (m_enumeration.hasMoreElements())
- {
- // Get the next entry name.
- String entryName = (String) m_enumeration.nextElement();
- // Check to see if it is a descendent of the specified path.
- if (!entryName.equals(m_path) && entryName.startsWith(m_path))
- {
- // Verify that it is a child of the path and not a
- // grandchild by examining its remaining path length.
- // This code uses the knowledge that content entries
- // corresponding to directories end in '/'. It checks
- // to see if the next occurrence of '/' is also the
- // end of the string, which means that this entry
- // represents a child directory of the path.
- int idx = entryName.indexOf('/', m_path.length());
- if ((idx < 0) || (idx == (entryName.length() - 1)))
- {
- return entryName;
- }
- }
- }
- return null;
- }
-}
\ No newline at end of file