| /* |
| * 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.util.Util; |
| import org.osgi.framework.wiring.BundleRevision; |
| |
| class EntryFilterEnumeration implements Enumeration |
| { |
| private final BundleRevision m_revision; |
| private final List<Enumeration> m_enumerations; |
| private final List<BundleRevision> m_revisions; |
| private int m_revisionIndex = 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( |
| BundleRevision revision, boolean includeFragments, String path, |
| String filePattern, boolean recurse, boolean isURLValues) |
| { |
| m_revision = revision; |
| List<BundleRevision> fragments = Util.getFragments(revision.getWiring()); |
| if (includeFragments && !fragments.isEmpty()) |
| { |
| m_revisions = fragments; |
| } |
| else |
| { |
| m_revisions = new ArrayList(1); |
| } |
| m_revisions.add(0, m_revision); |
| m_enumerations = new ArrayList(m_revisions.size()); |
| for (int i = 0; i < m_revisions.size(); i++) |
| { |
| m_enumerations.add(((BundleRevisionImpl) m_revisions.get(i)).getContent() != null ? |
| ((BundleRevisionImpl) m_revisions.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.isEmpty(); |
| } |
| |
| public synchronized Object nextElement() |
| { |
| if (m_nextEntries.isEmpty()) |
| { |
| 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_revisionIndex < m_enumerations.size()) && m_nextEntries.isEmpty()) |
| { |
| while (m_enumerations.get(m_revisionIndex) != null |
| && m_enumerations.get(m_revisionIndex).hasMoreElements() |
| && m_nextEntries.isEmpty()) |
| { |
| // Get the current entry to determine if it should be filtered or not. |
| String entryName = (String) m_enumerations.get(m_revisionIndex).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 revision |
| // 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 determine the directory for this entry |
| // and see if we've already seen an entry for for it. If not, |
| // synthesize an entry for it. 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 add it to the list of available next entries. If |
| // the original entry is actually a directory, then it will |
| // be added to next entries like normal if it matches, but if |
| // it is not a directory, its parent directory entries may be |
| // synthesized depending on the matching filter and recursion. |
| if (!m_dirEntries.contains(dir)) |
| { |
| // Record the directory entry. |
| m_dirEntries.add(dir); |
| // Add the directory to the list of |
| if (SimpleFilter.compareSubstring( |
| m_filePattern, getLastPathElement(dir))) |
| { |
| // Add synthesized directory entry to the next |
| // entries list in the correct form. |
| if (m_isURLValues) |
| { |
| entryURL = (entryURL == null) |
| ? ((BundleRevisionImpl) m_revisions. |
| get(m_revisionIndex)).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 we've already added |
| // or synthesized the directory entry, then we can ignore it. |
| if (!m_dirEntries.contains(entryName) |
| && (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) |
| ? ((BundleRevisionImpl) |
| m_revisions.get(m_revisionIndex)).getEntry(entryName) |
| : entryURL; |
| m_nextEntries.add(entryURL); |
| } |
| else |
| { |
| m_nextEntries.add(entryName); |
| } |
| } |
| } |
| } |
| } |
| if (m_nextEntries.isEmpty()) |
| { |
| m_revisionIndex++; |
| // Reset directory entries, since fragments may |
| // have overlapping directory entries that need |
| // to be returned. |
| m_dirEntries.clear(); |
| } |
| } |
| } |
| |
| 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); |
| } |
| } |