001/*
002 * Forge Mod Loader
003 * Copyright (c) 2012-2013 cpw.
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser Public License v2.1
006 * which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
008 * 
009 * Contributors:
010 *     cpw - implementation
011 */
012
013package cpw.mods.fml.common.discovery;
014
015import java.io.File;
016import java.util.Arrays;
017import java.util.List;
018import java.util.logging.Level;
019import java.util.regex.Matcher;
020import java.util.regex.Pattern;
021
022import com.google.common.base.Throwables;
023import com.google.common.collect.ImmutableList;
024import com.google.common.collect.Lists;
025
026import cpw.mods.fml.common.FMLLog;
027import cpw.mods.fml.common.LoaderException;
028import cpw.mods.fml.common.ModClassLoader;
029import cpw.mods.fml.common.ModContainer;
030import cpw.mods.fml.relauncher.RelaunchLibraryManager;
031
032public class ModDiscoverer
033{
034    private static Pattern zipJar = Pattern.compile("(.+).(zip|jar)$");
035
036    private List<ModCandidate> candidates = Lists.newArrayList();
037
038    private ASMDataTable dataTable = new ASMDataTable();
039
040    private List<File> nonModLibs = Lists.newArrayList();
041
042    public void findClasspathMods(ModClassLoader modClassLoader)
043    {
044        List<String> knownLibraries = ImmutableList.<String>builder().addAll(modClassLoader.getDefaultLibraries()).addAll(RelaunchLibraryManager.getLibraries()).build();
045        File[] minecraftSources = modClassLoader.getParentSources();
046        if (minecraftSources.length == 1 && minecraftSources[0].isFile())
047        {
048            FMLLog.fine("Minecraft is a file at %s, loading", minecraftSources[0].getAbsolutePath());
049            candidates.add(new ModCandidate(minecraftSources[0], minecraftSources[0], ContainerType.JAR, true, true));
050        }
051        else
052        {
053            for (int i = 0; i < minecraftSources.length; i++)
054            {
055                if (minecraftSources[i].isFile())
056                {
057                    if (knownLibraries.contains(minecraftSources[i].getName()))
058                    {
059                        FMLLog.finer("Skipping known library file %s", minecraftSources[i].getAbsolutePath());
060                    }
061                    else
062                    {
063                        FMLLog.fine("Found a minecraft related file at %s, examining for mod candidates", minecraftSources[i].getAbsolutePath());
064                        candidates.add(new ModCandidate(minecraftSources[i], minecraftSources[i], ContainerType.JAR, i==0, true));
065                    }
066                }
067                else if (minecraftSources[i].isDirectory())
068                {
069                    FMLLog.fine("Found a minecraft related directory at %s, examining for mod candidates", minecraftSources[i].getAbsolutePath());
070                    candidates.add(new ModCandidate(minecraftSources[i], minecraftSources[i], ContainerType.DIR, i==0, true));
071                }
072            }
073        }
074
075    }
076
077    public void findModDirMods(File modsDir)
078    {
079        File[] modList = modsDir.listFiles();
080        // Sort the files into alphabetical order first
081        Arrays.sort(modList);
082
083        for (File modFile : modList)
084        {
085            if (modFile.isDirectory())
086            {
087                FMLLog.fine("Found a candidate mod directory %s", modFile.getName());
088                candidates.add(new ModCandidate(modFile, modFile, ContainerType.DIR));
089            }
090            else
091            {
092                Matcher matcher = zipJar.matcher(modFile.getName());
093
094                if (matcher.matches())
095                {
096                    FMLLog.fine("Found a candidate zip or jar file %s", matcher.group(0));
097                    candidates.add(new ModCandidate(modFile, modFile, ContainerType.JAR));
098                }
099                else
100                {
101                    FMLLog.fine("Ignoring unknown file %s in mods directory", modFile.getName());
102                }
103            }
104        }
105    }
106
107    public List<ModContainer> identifyMods()
108    {
109        List<ModContainer> modList = Lists.newArrayList();
110
111        for (ModCandidate candidate : candidates)
112        {
113            try
114            {
115                List<ModContainer> mods = candidate.explore(dataTable);
116                if (mods.isEmpty() && !candidate.isClasspath())
117                {
118                    nonModLibs.add(candidate.getModContainer());
119                }
120                else
121                {
122                    modList.addAll(mods);
123                }
124            }
125            catch (LoaderException le)
126            {
127                FMLLog.log(Level.WARNING, le, "Identified a problem with the mod candidate %s, ignoring this source", candidate.getModContainer());
128            }
129            catch (Throwable t)
130            {
131                Throwables.propagate(t);
132            }
133        }
134
135        return modList;
136    }
137
138    public ASMDataTable getASMTable()
139    {
140        return dataTable;
141    }
142
143    public List<File> getNonModLibs()
144    {
145        return nonModLibs;
146    }
147
148}