This patch implements support for Bundle.findEntries() minus the support
for fragments, since they haven't been implemented yet. (FELIX-31)
git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@423965 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index 0bf5448..8e83861 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -81,6 +81,11 @@
return m_felix.getBundleEntryPaths(this, path);
}
+ public Enumeration findEntries(String path, String filePattern, boolean recurse)
+ {
+ return m_felix.findBundleEntries(this, path, filePattern, recurse);
+ }
+
public Dictionary getHeaders()
{
return m_felix.getBundleHeaders(this);
@@ -189,12 +194,6 @@
return null;
}
- public Enumeration findEntries(String path, String filePattern, boolean recurse)
- {
- // TODO: Implement Bundle.findEntries()
- return null;
- }
-
public boolean equals(Object obj)
{
if (obj instanceof BundleImpl)
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java
index 44539ba..7ab41a4 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -1068,6 +1068,38 @@
.getContentLoader().getContent().getEntryPaths(path);
}
+ /**
+ * Implementation for findEntries().
+ **/
+ public Enumeration findBundleEntries(
+ BundleImpl bundle, String path, String filePattern, boolean recurse)
+ {
+ // Strip leading '/' if present.
+ if ((path.length() > 0) && (path.charAt(0) == '/'))
+ {
+ path = path.substring(1);
+ }
+
+ // Sanity check the parameters.
+ if (path == null)
+ {
+ throw new IllegalArgumentException("The path for findEntries() cannot be null.");
+ }
+ filePattern = (filePattern == null) ? "*" : filePattern;
+
+
+ // Try to resolve the bundle per the spec.
+ resolveBundles(new Bundle[] { bundle });
+
+ // Get the entry enumeration from the module content.
+ Enumeration enumeration = bundle.getInfo().getCurrentModule()
+ .getContentLoader().getContent().findEntries(path, filePattern, recurse);
+
+ // Return a wrapper that will convert the entry strings to URLs.
+ return (enumeration == null)
+ ? null : new FindEntriesEnumeration(bundle, enumeration);
+ }
+
protected ServiceReference[] getBundleRegisteredServices(BundleImpl bundle)
{
if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
@@ -3746,6 +3778,41 @@
}
//
+ // Miscellaneous inner classes.
+ //
+
+ /**
+ * Used by findBundleEntries() method to wrap the enumeration
+ * returned by IContent.findEntries() to convert the returned
+ * Strings to URLs.
+ */
+ private static class FindEntriesEnumeration implements Enumeration
+ {
+ private BundleImpl m_bundle = null;
+ private Enumeration m_enumeration = null;
+
+ public FindEntriesEnumeration(BundleImpl bundle, Enumeration enumeration)
+ {
+ m_bundle = bundle;
+ m_enumeration = enumeration;
+ }
+
+ public boolean hasMoreElements()
+ {
+ return m_enumeration.hasMoreElements();
+ }
+
+ public Object nextElement()
+ {
+ URL url =
+ ((ContentLoaderImpl) m_bundle.getInfo().getCurrentModule()
+ .getContentLoader()).getResourceFromContent(
+ (String) m_enumeration.nextElement());
+ return url;
+ }
+ }
+
+ //
// Locking related methods.
//
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/ContentDirectoryContent.java b/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/ContentDirectoryContent.java
index f2f870e..267921d 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/ContentDirectoryContent.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/ContentDirectoryContent.java
@@ -127,6 +127,18 @@
return new WrappedEnumeration(m_content.getEntryPaths(m_rootPath + path), m_rootPath);
}
+ public Enumeration findEntries(String path, String filePattern, boolean recurse)
+ {
+ // This IContent method supports Bundle.findEntries(), which is used to
+ // browse the bundle JAR file, not the bundle's class path. However,
+ // this implementation of IContent is used purely to add support for
+ // directories in the bundle class path, thus it will never represent
+ // the actual bundle content, so this method should never be called.
+ // For now we will leave this unimplemented and if it becomes necessary
+ // it can be implemented later.
+ throw new UnsupportedOperationException("Not implemented, since it should not be used.");
+ }
+
public String toString()
{
return "CONTENT DIR " + m_rootPath + " (" + m_content + ")";
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/DirectoryContent.java b/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/DirectoryContent.java
index 880cd7b..9868067 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/DirectoryContent.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/DirectoryContent.java
@@ -17,6 +17,7 @@
package org.apache.felix.moduleloader;
import java.io.*;
+import java.util.*;
import java.util.Enumeration;
import java.util.NoSuchElementException;
@@ -141,18 +142,31 @@
path = path.substring(1);
}
- return new FileEnumeration(m_dir, path);
+ return new GetEntryPathsEnumeration(m_dir, path);
}
+ public Enumeration findEntries(String path, String filePattern, boolean recurse)
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("JarContent is not open");
+ }
- private static class FileEnumeration implements Enumeration
+ // Wrap entries enumeration to filter non-matching entries.
+ Enumeration e = new FindEntriesEnumeration(
+ m_dir, path, filePattern, recurse);
+ // Spec says to return null if there are no entries.
+ return (e.hasMoreElements()) ? e : null;
+ }
+
+ private static class GetEntryPathsEnumeration implements Enumeration
{
private File m_refDir = null;
private File m_listDir = null;
private File[] m_children = null;
private int m_counter = 0;
- public FileEnumeration(File refDir, String path)
+ public GetEntryPathsEnumeration(File refDir, String path)
{
m_refDir = refDir;
m_listDir = new File(refDir, path);
@@ -186,4 +200,243 @@
return sb.toString();
}
}
+
+ private static class FindEntriesEnumeration implements Enumeration
+ {
+ private File m_refDir = null;
+ private File m_listDir = null;
+ private String[] m_filePattern = null;
+ private boolean m_recurse = false;
+ private File[] m_children = null;
+ private int m_counter = 0;
+ private Object m_next = null;
+
+ public FindEntriesEnumeration(File refDir, String path, String filePattern, boolean recurse)
+ {
+ m_refDir = refDir;
+ m_listDir = new File(refDir, path);
+ m_filePattern = parseSubstring(filePattern);
+ m_recurse = recurse;
+ if (m_listDir.isDirectory())
+ {
+ if (m_recurse)
+ {
+ m_children = listFilesRecursive(m_listDir);
+ }
+ else
+ {
+ m_children = m_listDir.listFiles();
+ }
+ }
+ m_next = findNext();
+ }
+
+ public boolean hasMoreElements()
+ {
+ return (m_next != null);
+ }
+
+ public 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()
+ {
+ if ((m_children == null) || (m_counter >= m_children.length))
+ {
+ return null;
+ }
+
+ // NOTE: We assume here that directories are not returned,
+ // unlike getEntryPaths() above, where directories are returned;
+ // this may or may not be the correct spec interpretation.
+
+ // Ignore directories and file that do not match the file pattern.
+ while ((m_counter < m_children.length) &&
+ (m_children[m_counter].isDirectory() ||
+ !checkSubstring(m_filePattern, m_children[m_counter].getName())))
+ {
+ m_counter++;
+ }
+
+ // Return null if there is no more matches.
+ if (m_counter >= m_children.length)
+ {
+ return null;
+ }
+
+ // Remove the leading path of the reference directory, since the
+ // entry paths are supposed to be relative to the root.
+ StringBuffer sb = new StringBuffer(m_children[m_counter].getAbsolutePath());
+ sb.delete(0, m_refDir.getAbsolutePath().length() + 1);
+ m_counter++;
+
+ return sb.toString();
+ }
+
+ public File[] listFilesRecursive(File dir)
+ {
+ File[] children = dir.listFiles();
+ File[] combined = children;
+ for (int i = 0; i < children.length; i++)
+ {
+ if (children[i].isDirectory())
+ {
+ File[] grandchildren = listFilesRecursive(children[i]);
+ if (grandchildren.length > 0)
+ {
+ File[] tmp = new File[combined.length + grandchildren.length];
+ System.arraycopy(combined, 0, tmp, 0, combined.length);
+ System.arraycopy(grandchildren, 0, tmp, combined.length, grandchildren.length);
+ combined = tmp;
+ }
+ }
+ }
+ return combined;
+ }
+ }
+
+ //
+ // The following substring-related code was lifted and modified
+ // from the LDAP parser code.
+ //
+
+ private static String[] parseSubstring(String target)
+ {
+ List pieces = new ArrayList();
+ StringBuffer ss = new StringBuffer();
+ // int kind = SIMPLE; // assume until proven otherwise
+ boolean wasStar = false; // indicates last piece was a star
+ boolean leftstar = false; // track if the initial piece is a star
+ boolean rightstar = false; // track if the final piece is a star
+
+ int idx = 0;
+
+ // We assume (sub)strings can contain leading and trailing blanks
+loop: for (;;)
+ {
+ if (idx >= target.length())
+ {
+ if (wasStar)
+ {
+ // insert last piece as "" to handle trailing star
+ rightstar = true;
+ }
+ else
+ {
+ pieces.add(ss.toString());
+ // accumulate the last piece
+ // note that in the case of
+ // (cn=); this might be
+ // the string "" (!=null)
+ }
+ ss.setLength(0);
+ break loop;
+ }
+
+ char c = target.charAt(idx++);
+ if (c == '*')
+ {
+ if (wasStar)
+ {
+ // encountered two successive stars;
+ // I assume this is illegal
+ throw new IllegalArgumentException("Invalid filter string: " + target);
+ }
+ if (ss.length() > 0)
+ {
+ pieces.add(ss.toString()); // accumulate the pieces
+ // between '*' occurrences
+ }
+ ss.setLength(0);
+ // if this is a leading star, then track it
+ if (pieces.size() == 0)
+ {
+ leftstar = true;
+ }
+ ss.setLength(0);
+ wasStar = true;
+ }
+ else
+ {
+ wasStar = false;
+ ss.append(c);
+ }
+ }
+ if (leftstar || rightstar || pieces.size() > 1)
+ {
+ // insert leading and/or trailing "" to anchor ends
+ if (rightstar)
+ {
+ pieces.add("");
+ }
+ if (leftstar)
+ {
+ pieces.add(0, "");
+ }
+ }
+ return (String[]) pieces.toArray(new String[pieces.size()]);
+ }
+
+ private static boolean checkSubstring(String[] pieces, String s)
+ {
+ // Walk the pieces to match the string
+ // There are implicit stars between each piece,
+ // and the first and last pieces might be "" to anchor the match.
+ // assert (pieces.length > 1)
+ // minimal case is <string>*<string>
+
+ boolean result = false;
+ int len = pieces.length;
+
+loop: for (int i = 0; i < len; i++)
+ {
+ String piece = (String) pieces[i];
+ int index = 0;
+ if (i == len - 1)
+ {
+ // this is the last piece
+ if (s.endsWith(piece))
+ {
+ result = true;
+ }
+ else
+ {
+ result = false;
+ }
+ break loop;
+ }
+ // initial non-star; assert index == 0
+ else if (i == 0)
+ {
+ if (!s.startsWith(piece))
+ {
+ result = false;
+ break loop;
+ }
+ }
+ // assert i > 0 && i < len-1
+ else
+ {
+ // Sure wish stringbuffer supported e.g. indexOf
+ index = s.indexOf(piece, index);
+ if (index < 0)
+ {
+ result = false;
+ break loop;
+ }
+ }
+ // start beyond the matching piece
+ index += piece.length();
+ }
+
+ return result;
+ }
}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/IContent.java b/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/IContent.java
index 7261fdc..c8a5392 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/IContent.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/IContent.java
@@ -29,4 +29,5 @@
public InputStream getEntryAsStream(String name)
throws IOException;
public Enumeration getEntryPaths(String path);
+ public Enumeration findEntries(String path, String filePattern, boolean recurse);
}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/JarContent.java b/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/JarContent.java
index dc2d053..6f7dc0b 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/JarContent.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/moduleloader/JarContent.java
@@ -17,6 +17,7 @@
package org.apache.felix.moduleloader;
import java.io.*;
+import java.util.*;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.zip.ZipEntry;
@@ -245,7 +246,36 @@
}
// Wrap entries enumeration to filter non-matching entries.
- Enumeration e = new FilteredEnumeration(m_jarFile.entries(), path);
+ Enumeration e = new GetEntryPathsEnumeration(m_jarFile.entries(), path);
+ // Spec says to return null if there are no entries.
+ return (e.hasMoreElements()) ? e : null;
+ }
+
+ public synchronized Enumeration findEntries(
+ String path, String filePattern, boolean recurse)
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("JarContent is not open");
+ }
+
+ // Open JAR file if not already opened.
+ if (m_jarFile == null)
+ {
+ try
+ {
+ openJarFile();
+ }
+ catch (IOException ex)
+ {
+ System.err.println("JarContent: " + ex);
+ return null;
+ }
+ }
+
+ // Wrap entries enumeration to filter non-matching entries.
+ Enumeration e = new FindEntriesEnumeration(
+ m_jarFile.entries(), path, filePattern, recurse);
// Spec says to return null if there are no entries.
return (e.hasMoreElements()) ? e : null;
}
@@ -263,13 +293,13 @@
return "JAR " + m_file.getPath();
}
- private static class FilteredEnumeration implements Enumeration
+ private static class GetEntryPathsEnumeration implements Enumeration
{
private Enumeration m_enumeration = null;
private String m_path = null;
private Object m_next = null;
- public FilteredEnumeration(Enumeration enumeration, String path)
+ public GetEntryPathsEnumeration(Enumeration enumeration, String path)
{
m_enumeration = enumeration;
// Add a '/' to the end if not present.
@@ -304,15 +334,16 @@
{
// Get the next zip entry.
ZipEntry entry = (ZipEntry) m_enumeration.nextElement();
- // Check to see if it is a child of the specified path.
+ // Check to see if it is a descendent of the specified path.
if (!entry.getName().equals(m_path) && entry.getName().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 zip entries
+ // This code uses the knowledge that zip entries
// corresponding to directories end in '/'. It checks
// to see if the next occurrence of '/' is also the
- // end of the string or if there are no more occurrences.
+ // end of the string, which means that this entry
+ // represents a child directory of the path.
int idx = entry.getName().indexOf('/', m_path.length());
if ((idx < 0) || (idx == (entry.getName().length() - 1)))
{
@@ -323,4 +354,225 @@
return null;
}
}
+
+ private static class FindEntriesEnumeration implements Enumeration
+ {
+ private Enumeration m_enumeration = null;
+ private String m_path = null;
+ private String[] m_filePattern = null;
+ private boolean m_recurse = false;
+ private Object m_next = null;
+
+ public FindEntriesEnumeration(
+ Enumeration enumeration, String path, String filePattern, boolean recurse)
+ {
+ m_enumeration = enumeration;
+ // Add a '/' to the end if not present.
+ m_path = (path.length() > 0) && (path.charAt(path.length() - 1) != '/')
+ ? path + "/" : path;
+ m_filePattern = parseSubstring(filePattern);
+ m_recurse = recurse;
+ m_next = findNext();
+ }
+
+ public boolean hasMoreElements()
+ {
+ return (m_next != null);
+ }
+
+ public 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 entries of the zip file, 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.
+ while (m_enumeration.hasMoreElements())
+ {
+ // Get the next zip entry.
+ ZipEntry entry = (ZipEntry) m_enumeration.nextElement();
+ String entryName = entry.getName();
+ // Check to see if it is a descendent of the specified path.
+ if (!entryName.equals(m_path) && entryName.startsWith(m_path))
+ {
+ // NOTE: We assume here that directories are not returned,
+ // unlike getEntryPaths() above, where directories are returned;
+ // this may or may not be the correct spec interpretation.
+
+ // If this is recursive, then simply verify that the
+ // entry is not a directory my making sure it does not
+ // end with '/'. If this is not recursive, then verify
+ // that the entry is a child of the path and not a
+ // grandchild by examining its remaining path length.
+ // This code uses the knowledge that zip entries
+ // corresponding to directories end in '/'.
+ int idx = entryName.indexOf('/', m_path.length());
+ if ((m_recurse && (entryName.charAt(entryName.length() - 1) != '/'))
+ || (idx < 0))
+ {
+ // Get the last element of the path.
+ idx = entryName.lastIndexOf('/');
+ String lastElement = entryName;
+ if (idx >= 0)
+ {
+ lastElement = entryName.substring(idx + 1);
+ }
+ // See if the file pattern matches the last element of the path.
+ if (checkSubstring(m_filePattern, lastElement))
+ {
+ return entry.getName();
+ }
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ //
+ // The following substring-related code was lifted and modified
+ // from the LDAP parser code.
+ //
+
+ private static String[] parseSubstring(String target)
+ {
+ List pieces = new ArrayList();
+ StringBuffer ss = new StringBuffer();
+ // int kind = SIMPLE; // assume until proven otherwise
+ boolean wasStar = false; // indicates last piece was a star
+ boolean leftstar = false; // track if the initial piece is a star
+ boolean rightstar = false; // track if the final piece is a star
+
+ int idx = 0;
+
+ // We assume (sub)strings can contain leading and trailing blanks
+loop: for (;;)
+ {
+ if (idx >= target.length())
+ {
+ if (wasStar)
+ {
+ // insert last piece as "" to handle trailing star
+ rightstar = true;
+ }
+ else
+ {
+ pieces.add(ss.toString());
+ // accumulate the last piece
+ // note that in the case of
+ // (cn=); this might be
+ // the string "" (!=null)
+ }
+ ss.setLength(0);
+ break loop;
+ }
+
+ char c = target.charAt(idx++);
+ if (c == '*')
+ {
+ if (wasStar)
+ {
+ // encountered two successive stars;
+ // I assume this is illegal
+ throw new IllegalArgumentException("Invalid filter string: " + target);
+ }
+ if (ss.length() > 0)
+ {
+ pieces.add(ss.toString()); // accumulate the pieces
+ // between '*' occurrences
+ }
+ ss.setLength(0);
+ // if this is a leading star, then track it
+ if (pieces.size() == 0)
+ {
+ leftstar = true;
+ }
+ ss.setLength(0);
+ wasStar = true;
+ }
+ else
+ {
+ wasStar = false;
+ ss.append(c);
+ }
+ }
+ if (leftstar || rightstar || pieces.size() > 1)
+ {
+ // insert leading and/or trailing "" to anchor ends
+ if (rightstar)
+ {
+ pieces.add("");
+ }
+ if (leftstar)
+ {
+ pieces.add(0, "");
+ }
+ }
+ return (String[]) pieces.toArray(new String[pieces.size()]);
+ }
+
+ private static boolean checkSubstring(String[] pieces, String s)
+ {
+ // Walk the pieces to match the string
+ // There are implicit stars between each piece,
+ // and the first and last pieces might be "" to anchor the match.
+ // assert (pieces.length > 1)
+ // minimal case is <string>*<string>
+
+ boolean result = false;
+ int len = pieces.length;
+
+loop: for (int i = 0; i < len; i++)
+ {
+ String piece = (String) pieces[i];
+ int index = 0;
+ if (i == len - 1)
+ {
+ // this is the last piece
+ if (s.endsWith(piece))
+ {
+ result = true;
+ }
+ else
+ {
+ result = false;
+ }
+ break loop;
+ }
+ // initial non-star; assert index == 0
+ else if (i == 0)
+ {
+ if (!s.startsWith(piece))
+ {
+ result = false;
+ break loop;
+ }
+ }
+ // assert i > 0 && i < len-1
+ else
+ {
+ // Sure wish stringbuffer supported e.g. indexOf
+ index = s.indexOf(piece, index);
+ if (index < 0)
+ {
+ result = false;
+ break loop;
+ }
+ }
+ // start beyond the matching piece
+ index += piece.length();
+ }
+
+ return result;
+ }
}
\ No newline at end of file