001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017package org.apache.xbean.finder.archive;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.net.URL;
022import java.util.Enumeration;
023import java.util.Iterator;
024import java.util.List;
025import java.util.NoSuchElementException;
026import java.util.jar.JarEntry;
027import java.util.jar.JarFile;
028import java.util.zip.ZipEntry;
029
030/**
031 * @version $Rev$ $Date$
032 */
033public class JarArchive implements Archive {
034
035    private final ClassLoader loader;
036    private final URL url;
037    private List<String> list;
038    private final JarFile jar;
039
040    public JarArchive(ClassLoader loader, URL url) {
041//        if (!"jar".equals(url.getProtocol())) throw new IllegalArgumentException("not a jar url: " + url);
042
043        try {
044            this.loader = loader;
045            this.url = url;
046            URL u = url;
047
048            String jarPath = url.getFile();
049            if (jarPath.contains("!")) {
050                jarPath = jarPath.substring(0, jarPath.indexOf("!"));
051                u = new URL(jarPath);
052            }
053            jar = new JarFile(FileArchive.decode(u.getFile())); // no more an url
054        } catch (IOException e) {
055            throw new IllegalStateException(e);
056        }
057    }
058
059    public URL getUrl() {
060        return url;
061    }
062
063    public InputStream getBytecode(String className) throws IOException, ClassNotFoundException {
064        int pos = className.indexOf("<");
065        if (pos > -1) {
066            className = className.substring(0, pos);
067        }
068        pos = className.indexOf(">");
069        if (pos > -1) {
070            className = className.substring(0, pos);
071        }
072        if (!className.endsWith(".class")) {
073            className = className.replace('.', '/') + ".class";
074        }
075
076        ZipEntry entry = jar.getEntry(className);
077        if (entry == null) throw new ClassNotFoundException(className);
078
079        return jar.getInputStream(entry);
080    }
081
082
083    public Class<?> loadClass(String className) throws ClassNotFoundException {
084        return loader.loadClass(className);
085    }
086
087    public Iterator<Entry> iterator() {
088        return new JarIterator();
089    }
090
091    private class JarIterator implements Iterator<Entry> {
092
093        private final Enumeration<JarEntry> stream;
094        private Entry next;
095
096        private JarIterator() {
097            stream = jar.entries();
098        }
099
100        private boolean advance() {
101            if (next != null) return true;
102
103            if (!stream.hasMoreElements()) return false;
104
105            final JarEntry entry = stream.nextElement();
106
107            if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
108                return advance();
109            }
110
111            final String className = entry.getName().replaceFirst(".class$", "");
112
113            if (className.contains(".")) {
114                return advance();
115            }
116
117            next = new ClassEntry(entry, className.replace('/', '.'));
118
119            return true;
120        }
121
122        public boolean hasNext() {
123            return advance();
124        }
125
126        public Entry next() {
127            if (!hasNext()) throw new NoSuchElementException();
128            Entry entry = next;
129            next = null;
130            return entry;
131        }
132
133        public void remove() {
134            throw new UnsupportedOperationException("remove");
135        }
136
137        private class ClassEntry implements Entry {
138            private final String name;
139            private final JarEntry entry;
140
141            private ClassEntry(JarEntry entry, String name) {
142                this.name = name;
143                this.entry = entry;
144            }
145
146            public String getName() {
147                return name;
148            }
149
150            public InputStream getBytecode() throws IOException {
151                return jar.getInputStream(entry);
152            }
153        }
154    }
155}
156