/**
 * 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.xbean.finder.archive;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

/**
 * @version $Rev$ $Date$
 */
public class JarArchive implements Archive {

    private final ClassLoader loader;
    private final URL url;

    public JarArchive(ClassLoader loader, URL url) {
        if (!"jar".equals(url.getProtocol())) throw new IllegalArgumentException("not a file url: " + url);
        this.loader = loader;
        this.url = url;
    }

    @Override
    public InputStream getBytecode(String className) throws IOException, ClassNotFoundException {
        int pos = className.indexOf("<");
        if (pos > -1) {
            className = className.substring(0, pos);
        }
        pos = className.indexOf(">");
        if (pos > -1) {
            className = className.substring(0, pos);
        }
        if (!className.endsWith(".class")) {
            className = className.replace('.', '/') + ".class";
        }

        URL resource = loader.getResource(className);
        if (resource != null) return resource.openStream();

        throw new ClassNotFoundException(className);
    }


    @Override
    public Class<?> loadClass(String className) throws ClassNotFoundException {
        return loader.loadClass(className);
    }

    @Override
    public Iterator<String> iterator() {
        try {
            return jar(url).iterator();
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    private List<String> jar(URL location) throws IOException {
        String jarPath = location.getFile();
        if (jarPath.indexOf("!") > -1){
            jarPath = jarPath.substring(0, jarPath.indexOf("!"));
        }
        URL url = new URL(jarPath);
        InputStream in = url.openStream();
        try {
            JarInputStream jarStream = new JarInputStream(in);
            return jar(jarStream);
        } finally {
            in.close();
        }
    }

    private List<String> jar(JarInputStream jarStream) throws IOException {
        List<String> classNames = new ArrayList<String>();

        JarEntry entry;
        while ((entry = jarStream.getNextJarEntry()) != null) {
            if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
                continue;
            }
            String className = entry.getName();
            className = className.replaceFirst(".class$", "");
            if (className.contains(".")) continue;
            className = className.replace(File.separatorChar, '.');
            classNames.add(className);
        }

        return classNames;
    }

}

@@ -965,24 +965,54 @@
 .ArrayList;%0A
+import java.util.Enumeration;%0A
 import java.
@@ -1080,16 +1080,46 @@
 rEntry;%0A
+import java.util.jar.JarFile;%0A
 import j
@@ -2791,16 +2791,237 @@
 rPath);%0A
+        if (%22file%22.equals(url.getProtocol())) %7B // ZipFile is faster than ZipInputStream%0A            JarFile jarFile = new JarFile(url.getFile().replace(%22%2520%22, %22 %22));%0A            return jar(jarFile);%0A        %7D else %7B%0A    
         
@@ -3055,38 +3055,46 @@
 ream();%0A        
+ 
+   
 try %7B%0A
+    
             JarI
@@ -3144,32 +3144,36 @@
 n);%0A            
+    
 return jar(jarSt
@@ -3187,16 +3187,20 @@
         
+   
+ 
 %7D finall
@@ -3199,24 +3199,28 @@
 %7D finally %7B%0A
+    
             
@@ -3227,24 +3227,38 @@
 in.close();%0A
+            %7D%0A
         %7D%0A  
@@ -3254,32 +3254,403 @@
        %7D%0A    %7D%0A%0A
+    private List%3CString%3E jar(JarFile jarFile) %7B%0A        List%3CString%3E classNames = new ArrayList%3CString%3E();%0A%0A        Enumeration%3C? extends JarEntry%3E jarEntries =jarFile.entries();%0A        while (jarEntries.hasMoreElements()) %7B%0A            JarEntry entry = jarEntries.nextElement();%0A            addClassName(classNames, entry);%0A        %7D%0A%0A        return classNames;%0A    %7D%0A%0A
     private List
@@ -3853,32 +3853,191 @@
  != null) %7B%0A    
+        addClassName(classNames, entry);%0A        %7D%0A%0A        return classNames;%0A    %7D%0A%0A    private void addClassName(List%3CString%3E classNames, JarEntry entry) %7B%0A
         if (entr
@@ -4110,20 +4110,14 @@
     
-    continue
+return
 ;%0A  
@@ -4126,24 +4126,18 @@
     
- 
-   %7D%0A  
+%7D%0A
         
   St
@@ -4128,26 +4128,24 @@
   %7D%0A        
-  
 String class
@@ -4164,28 +4164,24 @@
 .getName();%0A
-    
         clas
@@ -4231,28 +4231,24 @@
 %22);%0A        
- 
-   
 if (classNam
@@ -4268,22 +4268,40 @@
 %22)) 
-continue;%0A    
+%7B%0A            return;%0A        %7D%0A
     
@@ -4360,36 +4360,32 @@
 , '.');%0A        
-    
 classNames.add(c
@@ -4403,47 +4403,9 @@
     
-    %7D%0A%0A        return classNames;%0A    
+%7D%0A
 %7D%0A%0A
-%7D%0A

Patch from Romain Manni-Bucau XBEAN-176: use JarFile when possible in JarArchive for example Dramatically speeds up iterating over the jar. Excellent patch, Romain!