/*
 * Copyright 2004-2010 the Seasar Foundation and the Others.
 *
 * Licensed 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.seasar.mayaa.impl.builder.library.scanner;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import org.seasar.mayaa.builder.library.scanner.SourceScanner;
import org.seasar.mayaa.impl.ParameterAwareImpl;
import org.seasar.mayaa.impl.source.ClassLoaderSourceDescriptor;
import org.seasar.mayaa.impl.util.IOUtil;
import org.seasar.mayaa.impl.util.StringUtil;
import org.seasar.mayaa.impl.util.collection.NullIterator;
import org.seasar.mayaa.source.SourceDescriptor;

/**
 * @author Taro Kato (Gluegent, Inc.)
 */
public class JarSourceScanner extends ParameterAwareImpl implements
        SourceScanner {

    private static final long serialVersionUID = 6023587474701789138L;

    private String _root;
    private SourceDescriptor _descriptor;
    private Set _folderFilters = new HashSet();
    private Set _ignores = new HashSet();
    private Set _extensions = new HashSet();

    public Iterator scan() {
        if (_descriptor == null) {
            return NullIterator.getInstance();
        }
        Set aliases = new HashSet();
        scanSource(_descriptor, aliases);
        return new AliasToSourceIterator(aliases.iterator());
    }

    protected String getJarName(String systemID) {
        if (StringUtil.hasValue(systemID)) {
            int pos = systemID.lastIndexOf("/");
            if (pos != -1) {
                return systemID.substring(pos + 1);
            }
            return systemID;
        }
        return null;
    }

    protected void scanSource(SourceDescriptor source, Set aliases) {
        if (source == null || source.exists() == false) {
            throw new IllegalArgumentException();
        }
        String jarName = getJarName(source.getSystemID());
        if (StringUtil.hasValue(jarName)) {
            String jarPath = ((_root != null) ? _root : "") + source.getSystemID();
            Date timestamp = source.getTimestamp();

            InputStream stream = null;
            JarInputStream jar = null;
            try {
                stream = source.getInputStream();
                jar = new JarInputStream(stream);
                addAliases(jar, jarPath, timestamp, aliases);
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                if (jar != null) {
                    IOUtil.close(jar);
                } else {
                    IOUtil.close(stream);
                }
            }
        }
    }

    protected void addAliases(
            JarInputStream jar, String jarPath, Date timestamp, Set aliases)
            throws IOException {
        JarEntry entry;
        while ((entry = jar.getNextJarEntry()) != null) {
            String entryName = entry.getName();
            if (nameStartsWith(entryName)
                    && extensionEndsWith(entryName)) {
                aliases.add(new SourceAlias(jarPath, entryName, timestamp));
            }
        }
    }

    protected boolean nameStartsWith(String entryName) {
        if (_folderFilters.size() > 0) {
            boolean ok = false;
            for (Iterator it = _folderFilters.iterator(); it.hasNext(); ) {
                String filter = (String) it.next();
                if (entryName.startsWith(filter)) {
                    ok = true;
                }
            }
            if (!ok) {
                return false;
            }
        }
        if (_ignores.size() > 0) {
            for (Iterator it = _ignores.iterator(); it.hasNext(); ) {
                String filter = (String) it.next();
                if (entryName.startsWith(filter)) {
                    return false;
                }
            }
        }
        return true;
    }

    protected boolean extensionEndsWith(String entryName) {
        if (_extensions.size() == 0) {
            return true;        // all
        }
        for (Iterator it = _extensions.iterator(); it.hasNext(); ) {
            String filter = (String) it.next();
            if (entryName.endsWith(filter)) {
                return true;
            }
        }
        return false;
    }

    public void setDescriptor(SourceDescriptor descriptor) {
        _descriptor = descriptor;
    }

    protected String getFileName(String path) {
        int separatorIndex = path.lastIndexOf('/');
        if (separatorIndex > 0) {
            path = path.substring(separatorIndex+1);
        } else if (File.separatorChar != '/') {
            separatorIndex = path.lastIndexOf(File.separatorChar);
            if (separatorIndex > 0) {
                path = path.substring(separatorIndex+1);
            }
        }
        return path;
    }

    //  Parameterizable implements ------------------------------------

    public void setParameter(String name, String value) {
        if ("root".equals(name)) {
            _root = value;
        } else if ("folder".equals(name)) {
            _folderFilters.add(value);
        } else if ("ignore".equals(name)) {
            _ignores.add(value);
        } else if ("extension".equals(name)) {
            _extensions.add(value);
        }
        super.setParameter(name, value);
    }

    // suppport class -----------------------------------------------

    protected static class AliasToSourceIterator implements Iterator {

        private Iterator _it;

        public AliasToSourceIterator(Iterator it) {
            if (it == null) {
                throw new IllegalArgumentException();
            }
            _it = it;
        }

        public boolean hasNext() {
            return _it.hasNext();
        }

        public Object next() {
            SourceAlias alias = (SourceAlias) _it.next();
            ClassLoaderSourceDescriptor source =
                new ClassLoaderSourceDescriptor();
            source.setSystemID(alias.getSystemID());
            source.setParameter(SourceAlias.ALIAS, alias.getAlias());
            source.setTimestamp(alias.getTimestamp());
            return source;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

    }

}

