/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.lang.gosuc;
import gw.config.CommonServices;
import gw.config.IGlobalLoaderProvider;
import gw.config.IMemoryMonitor;
import gw.fs.IDirectory;
import gw.lang.GosuShop;
import gw.lang.init.GosuInitialization;
import gw.lang.parser.IGosuParser;
import gw.lang.reflect.IType;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.java.JavaTypes;
import gw.lang.reflect.module.Dependency;
import gw.lang.reflect.module.IExecutionEnvironment;
import gw.lang.reflect.module.IJreModule;
import gw.lang.reflect.module.IModule;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*/
public class Gosuc implements IGosuc {
private GosucProject _project;
private IModule _globalModule;
private List<GosucModule> _allGosucModules;
public Gosuc( String projectFile, ICustomParser custParser ) throws FileNotFoundException {
File file = new File( projectFile );
if( !file.isFile() ) {
System.err.println( "The project file does not exist: " + file );
}
FileInputStream is = new FileInputStream( file );
_project = GosucProjectParser.parse( new BufferedInputStream( is ), custParser );
_allGosucModules = _project.getModules();
}
public Gosuc( ICustomParser custParser, String projectFileContent ) throws FileNotFoundException {
_project = GosucProjectParser.parse( projectFileContent, custParser );
_allGosucModules = _project.getModules();
}
public void initializeGosu() {
CommonServices.getKernel().redefineService_Privileged( IGlobalLoaderProvider.class,
new GosucGlobalLoaderProvider( _project.getGlobalLoaders() ) );
IMemoryMonitor memoryMonitor = _project.getMemoryMonitor();
if (memoryMonitor != null) {
CommonServices.getKernel().redefineService_Privileged(IMemoryMonitor.class, memoryMonitor);
}
IExecutionEnvironment execEnv = TypeSystem.getExecutionEnvironment( _project );
List<IModule> modules = defineModules( _project );
modules.add( _globalModule );
GosuInitialization.instance( execEnv ).initializeMultipleModules( modules );
updateAllModuleClasspaths( _project );
IModule module = execEnv.getModule( IExecutionEnvironment.GLOBAL_MODULE_NAME );
TypeSystem.pushModule( module );
try {
Object o1 = IGosuParser.NaN;
Object o2 = JavaTypes.DOUBLE();
}
finally {
TypeSystem.popModule( module );
}
_project.startDependencies();
}
private List<IModule> defineModules( GosucProject project ) {
IExecutionEnvironment execEnv = TypeSystem.getExecutionEnvironment( project );
execEnv.createJreModule( );
_globalModule = GosuShop.createGlobalModule(execEnv);
_globalModule.configurePaths(Collections.<IDirectory>emptyList(), Collections.<IDirectory>emptyList());
_globalModule.addDependency( new Dependency( execEnv.getJreModule(), true ) );
List<IDirectory> allSourcePaths = new ArrayList<IDirectory>();
List<IDirectory> allRoots = new ArrayList<IDirectory>();
Map<String, IModule> modules = new HashMap<String, IModule>();
List<IModule> allModules = new ArrayList<IModule>();
for( GosucModule gosucModule : _allGosucModules ) {
IModule module = defineModule( project, gosucModule );
if( module != null ) {
allSourcePaths.addAll( module.getSourcePath() );
allRoots.addAll( module.getRoots() );
modules.put( gosucModule.getName(), module );
allModules.add( module );
}
}
for( GosucModule gosucModule : _allGosucModules ) {
IModule module = modules.get( gosucModule.getName() );
for( GosucDependency dep : gosucModule.getDependencies() ) {
IModule moduleDep = modules.get( dep.getModuleName() );
if( moduleDep != null ) {
module.addDependency( new Dependency( moduleDep, isExported( gosucModule, dep.getModuleName() ) ) );
}
}
}
addImplicitJreModuleDependency( project, allModules );
allSourcePaths.addAll( execEnv.getJreModule().getSourcePath() );
List<IModule> rootModules = findRootModules(allModules);
for (IModule rootModule : rootModules) {
_globalModule.addDependency(new Dependency(rootModule, true));
}
_globalModule.setSourcePath( allSourcePaths );
_globalModule.setRoots(allRoots);
return allModules;
}
public List<IModule> findRootModules(List<IModule> modules) {
List<IModule> moduleRoots = new ArrayList<IModule>(modules);
for (IModule module : modules) {
for (Dependency d : module.getDependencies()) {
moduleRoots.remove(d.getModule());
}
}
return moduleRoots;
}
public IModule defineModule( GosucProject project, GosucModule gosucModule ) {
IModule gosuModule = GosuShop.createModule( TypeSystem.getExecutionEnvironment( project ),
gosucModule.getName() );
List<IDirectory> sourceFolders = getSourceFolders( gosucModule );
gosuModule.configurePaths(getClassPaths(gosucModule), sourceFolders);
IDirectory sourceRoot = computeCommonRoot( sourceFolders );
if( sourceRoot != null ) {
gosuModule.setRoots(Collections.<IDirectory>singletonList(sourceRoot));
}
gosuModule.setNativeModule( gosucModule );
return gosuModule;
}
private IDirectory computeCommonRoot( List<IDirectory> sourceFolders ) {
if( sourceFolders.isEmpty() ) {
return null;
}
else if( sourceFolders.size() == 1 ) {
return sourceFolders.get( 0 ).getParent();
}
else {
String[] paths = new String[sourceFolders.size()];
int minLength = Integer.MAX_VALUE;
for( int i = 0; i < paths.length; i++ ) {
paths[i] = sourceFolders.get( i ).getPath().getFileSystemPathString();
if( paths[i].length() < minLength ) {
minLength = paths[i].length();
}
}
int charIndex;
outer:
for( charIndex = 0; charIndex < minLength; charIndex++ ) {
char c0 = paths[0].charAt( charIndex );
for( int i = 1; i < paths.length; i++ ) {
if( paths[i].charAt( charIndex ) != c0 ) {
break outer;
}
}
}
String dirName = paths[0].substring( 0, charIndex );
File dir = new File( dirName );
if( !dir.exists() ) {
return null;
}
else {
return CommonServices.getFileSystem().getIDirectory( dir );
}
}
}
private List<IDirectory> getSourceFolders( GosucModule gosucModule ) {
List<IDirectory> sourceFolders = new ArrayList<IDirectory>();
for( String path : gosucModule.getAllSourceRoots() ) {
sourceFolders.add( GosucUtil.getDirectoryForPath( path ) );
}
return sourceFolders;
}
public boolean isExported( GosucModule gosucModule, String childModuleName ) {
for( GosucDependency dep : gosucModule.getDependencies() ) {
if( dep.getModuleName().equals( childModuleName ) ) {
return dep.isExported();
}
if( isExported( findGosucModule( dep.getModuleName() ), childModuleName ) ) {
return true;
}
}
return false;
}
private GosucModule findGosucModule( String moduleName ) {
for( GosucModule mod : _allGosucModules ) {
if( mod.getName().equals( moduleName ) ) {
return mod;
}
}
return null;
}
private void addImplicitJreModuleDependency( GosucProject project, List<IModule> modules ) {
IJreModule jreModule = (IJreModule)TypeSystem.getExecutionEnvironment( project ).getJreModule();
updateJreModuleWithProjectSdk( project, jreModule );
for( IModule module : modules ) {
module.addDependency( new Dependency( jreModule, true ) );
}
modules.add( jreModule );
}
public static void updateJreModuleWithProjectSdk( GosucProject project, IJreModule jreModule ) {
GosucSdk projectSdk = project.getSdk();
List<String> classFiles = projectSdk.getPaths();
List<IDirectory> dirs = new ArrayList<IDirectory>();
for (String path : classFiles) {
dirs.add(GosucUtil.getDirectoryForPath(path));
}
jreModule.configurePaths(dirs, Collections.<IDirectory>emptyList());
jreModule.setNativeSDK( projectSdk );
}
void updateAllModuleClasspaths( GosucProject project ) {
final List<? extends IModule> modules = TypeSystem.getExecutionEnvironment( project ).getModules();
List<GosucModule> gosucModules = new ArrayList<GosucModule>();
for( IModule module : modules ) {
GosucModule gosucModule = (GosucModule)module.getNativeModule();
if( gosucModule != null ) {
gosucModules.add( gosucModule );
}
}
// FIXME-isd: why do we need to update classpaths again?
Map<String, List<IDirectory>> classpathMap = createClassPathMap( gosucModules.toArray( new GosucModule[gosucModules.size()] ) );
for( IModule module : modules ) {
if( module.getNativeModule() != null ) {
module.configurePaths(classpathMap.get(module.getName()), module.getSourcePath());
}
}
}
private Map<String, List<IDirectory>> createClassPathMap( GosucModule[] allGosucModules ) {
Map<String, List<IDirectory>> classpathMap = new HashMap<String, List<IDirectory>>();
for( GosucModule module : allGosucModules ) {
String name = module.getName();
List<IDirectory> classPaths = getClassPaths( module );
classpathMap.put( name, classPaths );
}
// simplify classpaths
for( GosucModule module : allGosucModules ) {
List<IDirectory> referencedTotalClasspath = getReferencedTotalClasspath( module, classpathMap );
if( referencedTotalClasspath != null ) {
List<IDirectory> claspath = classpathMap.get( module.getName() );
for( Iterator<IDirectory> i = claspath.iterator(); i.hasNext(); ) {
IDirectory dir = i.next();
if( referencedTotalClasspath.contains( dir ) ) {
i.remove();
}
}
}
}
return classpathMap;
}
private List<IDirectory> getReferencedTotalClasspath( GosucModule gosucModule, Map<String, List<IDirectory>> classpathMap ) {
List<IDirectory> totalClasspath = new ArrayList<IDirectory>();
List<GosucModule> referencedModules = getAllRequiredModules( gosucModule );
for( GosucModule m : referencedModules ) {
totalClasspath.addAll( classpathMap.get( m.getName() ) );
}
return totalClasspath;
}
public List<GosucModule> getAllRequiredModules( GosucModule gosucModule ) {
Set<GosucModule> visitedProjects = new HashSet<GosucModule>();
List<GosucModule> modules = new ArrayList<GosucModule>();
getAllRequiredProjects( gosucModule, modules, visitedProjects );
return modules;
}
private void getAllRequiredProjects( GosucModule gosucModule, List<GosucModule> gosucModuleList, Set<GosucModule> visitedModules ) {
visitedModules.add( gosucModule );
for( GosucDependency dep : gosucModule.getDependencies() ) {
GosucModule depMod = findGosucModule( dep.getModuleName() );
if( !visitedModules.contains( depMod ) ) {
gosucModuleList.add( depMod );
getAllRequiredProjects( depMod, gosucModuleList, visitedModules );
}
}
}
private static List<IDirectory> getClassPaths(GosucModule gosucModule) {
List<IDirectory> paths = new ArrayList<IDirectory>();
for( String path : gosucModule.getClasspath() ) {
paths.add( GosucUtil.getDirectoryForPath( path ) );
}
return paths;
}
public List<IType> compile( String moduleName, List<String> types ) {
IModule module = moduleName == null
? TypeSystem.getGlobalModule()
: TypeSystem.getExecutionEnvironment().getModule( moduleName );
return compile( module, types );
}
public List<IType> compile( IModule module, List<String> types ) {
TypeSystem.pushModule( module );
try {
return new GosucCompiler().compile( _project, types );
}
finally {
TypeSystem.popModule( module );
}
}
// You can use this for testing by:
// - From an IJ project use the 'Write Gosuc Project' command to write out the project file
// - Then run this from IJ using 'classpath of module' setting to match the proper module for pl/pc/cc etc. (this is so the global loaders will be in the classpath)
public static void main( String[] args ) throws FileNotFoundException {
String error = GosucArg.parseArgs( args );
if( error != null ) {
System.err.println( error );
return;
}
String strFile = GosucArg.PROJECT.getValue();
Gosuc gosuc = new Gosuc( strFile, maybeGetCustomParser() );
gosuc.initializeGosu();
gosuc.compile( (String)null, Collections.singletonList( "-all" ) );
}
private static ICustomParser maybeGetCustomParser() {
String cls = GosucArg.PARSER.getValue();
if( cls != null ) {
try {
Class.forName( cls ).newInstance();
}
catch( Exception e ) {
throw new RuntimeException( e );
}
}
return null;
}
}