2016-06-23 03:49:47 +00:00
/ *
* Minecraft Forge
* Copyright ( c ) 2016 .
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2 . 1
* of the License .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
* /
2014-09-23 05:01:24 +00:00
package net.minecraftforge.fml.common ;
2013-10-04 21:20:05 +00:00
2013-10-31 02:20:28 +00:00
import java.io.File ;
import java.util.List ;
import java.util.Map ;
import java.util.Set ;
2014-09-23 05:01:24 +00:00
import net.minecraftforge.fml.common.asm.transformers.ModAPITransformer ;
import net.minecraftforge.fml.common.discovery.ASMDataTable ;
import net.minecraftforge.fml.common.discovery.ModCandidate ;
import net.minecraftforge.fml.common.discovery.ModDiscoverer ;
import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData ;
import net.minecraftforge.fml.common.functions.ModIdFunction ;
import net.minecraftforge.fml.common.versioning.ArtifactVersion ;
import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion ;
import net.minecraftforge.fml.common.versioning.VersionParser ;
2013-10-31 02:20:28 +00:00
import com.google.common.collect.ImmutableList ;
import com.google.common.collect.Lists ;
import com.google.common.collect.Maps ;
import com.google.common.collect.Sets ;
2013-10-04 21:20:05 +00:00
public class ModAPIManager {
public static final ModAPIManager INSTANCE = new ModAPIManager ( ) ;
2015-11-27 03:38:21 +00:00
@SuppressWarnings ( " unused " )
2013-10-04 21:20:05 +00:00
private ModAPITransformer transformer ;
private ASMDataTable dataTable ;
2013-10-31 02:20:28 +00:00
private Map < String , APIContainer > apiContainers ;
private static class APIContainer extends DummyModContainer {
private List < ArtifactVersion > referredMods ;
private ArtifactVersion ownerMod ;
private ArtifactVersion ourVersion ;
private String providedAPI ;
private File source ;
private String version ;
private Set < String > currentReferents ;
private Set < String > packages ;
2014-06-27 02:09:50 +00:00
private boolean selfReferenced ;
2013-10-31 02:20:28 +00:00
public APIContainer ( String providedAPI , String apiVersion , File source , ArtifactVersion ownerMod )
{
this . providedAPI = providedAPI ;
this . version = apiVersion ;
this . ownerMod = ownerMod ;
this . ourVersion = new DefaultArtifactVersion ( providedAPI , apiVersion ) ;
this . referredMods = Lists . newArrayList ( ) ;
this . source = source ;
this . currentReferents = Sets . newHashSet ( ) ;
this . packages = Sets . newHashSet ( ) ;
}
@Override
public File getSource ( )
{
return source ;
}
@Override
public String getVersion ( )
{
return version ;
}
@Override
public String getName ( )
{
return " API: " + providedAPI ;
}
@Override
public String getModId ( )
{
2014-06-27 02:09:50 +00:00
return providedAPI ;
2013-10-31 02:20:28 +00:00
}
@Override
public List < ArtifactVersion > getDependants ( )
{
return referredMods ;
}
@Override
public List < ArtifactVersion > getDependencies ( )
{
2014-06-27 02:09:50 +00:00
return selfReferenced ? ImmutableList . < ArtifactVersion > of ( ) : ImmutableList . of ( ownerMod ) ;
2013-10-31 02:20:28 +00:00
}
@Override
public ArtifactVersion getProcessedVersion ( )
{
return ourVersion ;
}
2013-10-04 21:20:05 +00:00
2013-10-31 02:20:28 +00:00
public void validate ( String providedAPI , String apiOwner , String apiVersion )
{
2015-12-01 18:08:35 +00:00
if ( Loader . instance ( ) . getModClassLoader ( ) . containsSource ( this . getSource ( ) ) ) {
FMLLog . bigWarning ( " The API %s from source %s is loaded from an incompatible classloader. THIS WILL NOT WORK! " , providedAPI , this . getSource ( ) . getAbsolutePath ( ) ) ;
}
2013-10-31 02:20:28 +00:00
// TODO Compare this annotation data to the one we first found. Maybe barf if there is inconsistency?
}
@Override
public String toString ( )
{
return " APIContainer{ " + providedAPI + " : " + version + " } " ;
}
public void addAPIReference ( String embedded )
{
2013-10-31 02:46:19 +00:00
if ( currentReferents . add ( embedded ) )
{
referredMods . add ( VersionParser . parseVersionReference ( embedded ) ) ;
}
2013-10-31 02:20:28 +00:00
}
public void addOwnedPackage ( String apiPackage )
{
packages . add ( apiPackage ) ;
}
public void addAPIReferences ( List < String > candidateIds )
{
for ( String modId : candidateIds )
{
addAPIReference ( modId ) ;
}
}
2014-06-27 02:09:50 +00:00
void markSelfReferenced ( )
{
selfReferenced = true ;
}
2013-10-31 02:20:28 +00:00
}
public void registerDataTableAndParseAPI ( ASMDataTable dataTable )
2013-10-04 21:20:05 +00:00
{
this . dataTable = dataTable ;
2013-10-31 02:20:28 +00:00
2014-09-23 05:01:24 +00:00
Set < ASMData > apiList = dataTable . getAll ( " net.minecraftforge.fml.common.API " ) ;
2013-10-31 02:20:28 +00:00
apiContainers = Maps . newHashMap ( ) ;
for ( ASMData data : apiList )
{
Map < String , Object > annotationInfo = data . getAnnotationInfo ( ) ;
String apiPackage = data . getClassName ( ) . substring ( 0 , data . getClassName ( ) . indexOf ( " .package-info " ) ) ;
String providedAPI = ( String ) annotationInfo . get ( " provides " ) ;
String apiOwner = ( String ) annotationInfo . get ( " owner " ) ;
String apiVersion = ( String ) annotationInfo . get ( " apiVersion " ) ;
APIContainer container = apiContainers . get ( providedAPI ) ;
if ( container = = null )
{
container = new APIContainer ( providedAPI , apiVersion , data . getCandidate ( ) . getModContainer ( ) , VersionParser . parseVersionReference ( apiOwner ) ) ;
apiContainers . put ( providedAPI , container ) ;
}
else
{
container . validate ( providedAPI , apiOwner , apiVersion ) ;
}
container . addOwnedPackage ( apiPackage ) ;
for ( ModContainer mc : data . getCandidate ( ) . getContainedMods ( ) )
{
String embeddedIn = mc . getModId ( ) ;
2013-10-31 02:46:19 +00:00
if ( container . currentReferents . contains ( embeddedIn ) )
{
continue ;
}
2013-10-31 02:20:28 +00:00
FMLLog . fine ( " Found API %s (owned by %s providing %s) embedded in %s " , apiPackage , apiOwner , providedAPI , embeddedIn ) ;
if ( ! embeddedIn . equals ( apiOwner ) )
{
container . addAPIReference ( embeddedIn ) ;
}
}
}
for ( APIContainer container : apiContainers . values ( ) )
{
for ( String pkg : container . packages )
{
Set < ModCandidate > candidates = dataTable . getCandidatesFor ( pkg ) ;
for ( ModCandidate candidate : candidates )
{
List < String > candidateIds = Lists . transform ( candidate . getContainedMods ( ) , new ModIdFunction ( ) ) ;
if ( ! candidateIds . contains ( container . ownerMod . getLabel ( ) ) & & ! container . currentReferents . containsAll ( candidateIds ) )
{
FMLLog . info ( " Found mod(s) %s containing declared API package %s (owned by %s) without associated API reference " , candidateIds , pkg , container . ownerMod ) ;
container . addAPIReferences ( candidateIds ) ;
}
}
}
2013-10-31 13:39:34 +00:00
if ( apiContainers . containsKey ( container . ownerMod . getLabel ( ) ) )
{
ArtifactVersion owner = container . ownerMod ;
do
{
APIContainer parent = apiContainers . get ( owner . getLabel ( ) ) ;
2014-06-27 02:09:50 +00:00
if ( parent = = container )
{
FMLLog . finer ( " APIContainer %s is it's own parent. skipping " , owner ) ;
container . markSelfReferenced ( ) ;
break ;
}
2013-12-16 16:47:48 +00:00
FMLLog . finer ( " Removing upstream parent %s from %s " , parent . ownerMod . getLabel ( ) , container ) ;
2013-10-31 13:39:34 +00:00
container . currentReferents . remove ( parent . ownerMod . getLabel ( ) ) ;
container . referredMods . remove ( parent . ownerMod ) ;
owner = parent . ownerMod ;
}
while ( apiContainers . containsKey ( owner . getLabel ( ) ) ) ;
}
2013-10-31 02:20:28 +00:00
FMLLog . fine ( " Creating API container dummy for API %s: owner: %s, dependents: %s " , container . providedAPI , container . ownerMod , container . referredMods ) ;
}
2013-10-04 21:20:05 +00:00
}
2013-10-31 02:20:28 +00:00
public void manageAPI ( ModClassLoader modClassLoader , ModDiscoverer discoverer )
2013-10-04 21:20:05 +00:00
{
2013-10-31 02:20:28 +00:00
registerDataTableAndParseAPI ( discoverer . getASMTable ( ) ) ;
2013-10-04 21:20:05 +00:00
transformer = modClassLoader . addModAPITransformer ( dataTable ) ;
}
2013-10-31 02:20:28 +00:00
public void injectAPIModContainers ( List < ModContainer > mods , Map < String , ModContainer > nameLookup )
{
mods . addAll ( apiContainers . values ( ) ) ;
nameLookup . putAll ( apiContainers ) ;
}
public void cleanupAPIContainers ( List < ModContainer > mods )
{
mods . removeAll ( apiContainers . values ( ) ) ;
}
public boolean hasAPI ( String modId )
{
return apiContainers . containsKey ( modId ) ;
}
2014-06-27 02:09:50 +00:00
public Iterable < ? extends ModContainer > getAPIList ( )
{
return apiContainers . values ( ) ;
}
2013-10-04 21:20:05 +00:00
}