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.util.ArrayList;
022import java.util.Arrays;
023import java.util.Iterator;
024import java.util.List;
025import java.util.NoSuchElementException;
026
027/**
028 * @version $Rev$ $Date$
029 */
030public class CompositeArchive implements Archive, AutoCloseable {
031
032    private final List<Archive> archives = new ArrayList<Archive>();
033
034    public CompositeArchive(Archive... archives) {
035        this(Arrays.asList(archives));
036    }
037
038    public CompositeArchive(Iterable<Archive> archives) {
039        for (Archive archive : archives) {
040            this.archives.add(archive);
041        }
042    }
043
044    public InputStream getBytecode(String className) throws IOException, ClassNotFoundException {
045        for (Archive archive : archives) {
046            try {
047                return archive.getBytecode(className);
048            } catch (ClassNotFoundException e) {
049            }
050        }
051
052        throw new ClassNotFoundException(className);
053    }
054
055    public Class<?> loadClass(String className) throws ClassNotFoundException {
056        for (Archive archive : archives) {
057            try {
058                return archive.loadClass(className);
059            } catch (ClassNotFoundException e) {
060            }
061        }
062
063        throw new ClassNotFoundException(className);
064    }
065
066    public Iterator<Entry> iterator() {
067        if (archives.size() == 1) return archives.get(0).iterator();
068        return new CompositeIterator(archives);
069    }
070
071    @Override
072    public void close() throws Exception {
073        archives.stream()
074                .filter(AutoCloseable.class::isInstance)
075                .map(AutoCloseable.class::cast)
076                .forEach(it -> {
077                    try {
078                        it.close();
079                    } catch (final Exception e) {
080                        // no-op
081                    }
082                });
083    }
084
085    private static class CompositeIterator implements Iterator<Entry> {
086
087        private Iterator<Archive> archives;
088        private Iterator<Entry> current;
089
090        private CompositeIterator(Iterable<Archive> archives) {
091            this.archives = archives.iterator();
092            if (this.archives.hasNext()) {
093                current = this.archives.next().iterator();
094            }
095        }
096
097        public boolean hasNext() {
098            if (current == null) return false;
099            if (current.hasNext()) return true;
100            
101            if (archives.hasNext()) {
102                current = archives.next().iterator();
103                return hasNext();
104            }
105            return false;
106        }
107
108        public Entry next() {
109            if (!hasNext()) throw new NoSuchElementException();
110
111            return current.next();
112        }
113
114        public void remove() {
115            throw new UnsupportedOperationException();
116        }
117    }
118}