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) {
102                return true;
103            }
104            while (stream.hasMoreElements()) {
105                final JarEntry entry = stream.nextElement();
106                final String entryName = entry.getName();
107                if (entry.isDirectory() || !entryName.endsWith(".class")) {
108                    continue;
109                }
110
111                String className = entryName;
112                if (entryName.endsWith(".class")) {
113                    className = className.substring(0, className.length() - 6);
114                }
115                if (className.contains(".")) {
116                    continue;
117                }
118
119                next = new ClassEntry(entry, className.replace('/', '.'));
120                return true;
121            }
122            return false;
123        }
124
125        public boolean hasNext() {
126            return advance();
127        }
128
129        public Entry next() {
130            if (!hasNext()) throw new NoSuchElementException();
131            Entry entry = next;
132            next = null;
133            return entry;
134        }
135
136        public void remove() {
137            throw new UnsupportedOperationException("remove");
138        }
139
140        private class ClassEntry implements Entry {
141            private final String name;
142            private final JarEntry entry;
143
144            private ClassEntry(JarEntry entry, String name) {
145                this.name = name;
146                this.entry = entry;
147            }
148
149            public String getName() {
150                return name;
151            }
152
153            public InputStream getBytecode() throws IOException {
154                return jar.getInputStream(entry);
155            }
156        }
157    }
158}
159