package greenfoot.core;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import bluej.Boot;
import bluej.Config;
import bluej.compiler.CompileReason;
import bluej.compiler.CompileType;
import bluej.debugger.Debugger;
import bluej.debugger.DebuggerObject;
import bluej.debugger.DebuggerResult;
import bluej.editor.Editor;
import bluej.extensions.BlueJ;
import bluej.extensions.SourceType;
import bluej.pkgmgr.DocPathEntry;
import bluej.pkgmgr.Package;
import bluej.pkgmgr.Project;
import bluej.pkgmgr.target.ClassTarget;
import bluej.utility.Debug;
import bluej.utility.DialogManager;
import greenfoot.core.GreenfootMain.VersionCheckInfo;
import greenfoot.core.GreenfootMain.VersionInfo;
import greenfoot.util.Version;
import greenfoot.vmcomm.GreenfootDebugHandler;
import javafx.application.Platform;
import threadchecker.OnThread;
import threadchecker.Tag;


| The ProjectManager is on the BlueJ-VM. It monitors pacakage events from BlueJ | and launches the greenfoot project in the greenfoot-VM. | | @author Poul Henriksen <polle@mip.sdu.dk> | public class ProjectManager {
| Singleton instance | private static ProjectManager instance;
| The class that will be instantiated in the greenfoot VM to launch the project | private String launchClass = GreenfootLauncherDebugVM.class.getName(); private static final String launcherName = "greenfootLauncher"; private static volatile boolean launchFailed = false;
| Filter that matches class files | private static FilenameFilter classFilter = new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.toLowerCase().endsWith(".class"); } }; @OnThread(Tag.FXPlatform) public static class ProjectAPIVersionAccess {
| Attempts to find the version number the greenfoot API that a greenfoot | project was created with. If it can not find a version number, it will | return Version.NO_VERSION. Thread-safe. | | @return API version | public Version getAPIVersion(Project project) { String versionString = project.getUnnamedPackage().getLastSavedProperties().getProperty("version"); return new Version(versionString); }
| Sets the API version and saves this to the project file. | @param version | public void setAPIVersionAndSave(Project project, String version) { Properties props = new Properties(); props.putAll(project.getUnnamedPackage().getLastSavedProperties()); if (! version.equals(props.get("version"))) { props.put("version", version); project.getUnnamedPackage().save(props); } } } private ProjectManager() { }
| Get the singleton instance. Make sure it is initialised first. | | @see #init(BlueJ) | public static ProjectManager instance() { if (instance == null) { instance = new ProjectManager(); } return instance; }
| Open a Greenfoot project. | @OnThread(Tag.FXPlatform) public void launchProject(final Project project) { ProjectAPIVersionAccess projectAPIVersionAccess = new ProjectAPIVersionAccess(); GreenfootMain.VersionCheckInfo versionOK = checkVersion(project, projectAPIVersionAccess); if (versionOK.versionInfo != GreenfootMain.VersionInfo.VERSION_BAD) { try { if (versionOK.versionInfo == GreenfootMain.VersionInfo.VERSION_UPDATED) { project.getPackage("").reload(); if (versionOK.removeAWTImports) { for (ClassTarget ctarget : project.getPackage("").getClassTargets()) { Editor ed = ctarget.getEditor(); if (ed != null) { ed.removeImports(Arrays.asList("java.awt.Color", "java.awt.Font")); } } project.scheduleCompilation(true, CompileReason.EARLY, CompileType.INTERNAL_COMPILE, project.getPackage("")); } } GreenfootDebugHandler.addDebuggerListener(project); List<DocPathEntry> sourcePath = project.getSourcePath(); String language = Config.getPropString("bluej.language"); if (! language.equals("english")) { File langlib = new File(Config.getBlueJLibDir(), language); File apiDir = new File(new File(langlib, "greenfoot"), "api"); sourcePath.add(new DocPathEntry(apiDir, "")); } File langlib = new File(Config.getBlueJLibDir(), "english"); File apiDir = new File(new File(langlib, "greenfoot"), "api"); sourcePath.add(new DocPathEntry(apiDir, "")); } catch (IOException e) { Debug.reportError("Could not create greenfoot launcher.", e); greenfootLaunchFailed(project); } } else { Project.cleanUp(project); } }
| Check whether failure to launch has been recorded. | public static boolean checkLaunchFailed() { return launchFailed; }
| Launching Greenfoot failed. Display a dialog, and exit. | @OnThread(Tag.FXPlatform) public static void greenfootLaunchFailed(Project project) { launchFailed = true; String text = Config.getString("greenfoot.launchFailed"); DialogManager.showErrorTextFX(null, text); System.exit(1); }
| Checks whether the API version this project was created with is | compatible with the current API version. If it is not, it will attempt to | update the project to the current version of the API and present the user | with a dialog with instructions on what to do if there are changes in API | version that requires manual modifications of the API. | | @param projectDir Directory of the project. | @return one of GreenfootMain.VERSION_OK, VERSION_UPDATED or VERSION_BAD | @OnThread(Tag.FXPlatform) private GreenfootMain.VersionCheckInfo checkVersion(Project project, ProjectAPIVersionAccess projectAPIVersionAccess) { File greenfootLibDir = Config.getGreenfootLibDir(); Version projectVersion = projectAPIVersionAccess.getAPIVersion(project); Version apiVersion = GreenfootMain.getAPIVersion(); String greenfootApiVersion = Boot.GREENFOOT_API_VERSION; if (projectVersion.isBad()) { String message = projectVersion.getBadMessage(); DialogManager.showInfoTextFX(null, Config.getString("project.version.mismatch"), message, false); Debug.message("Bad version number in project: " + greenfootLibDir); prepareGreenfootProject(greenfootLibDir, project, projectAPIVersionAccess, true, greenfootApiVersion); return new VersionCheckInfo(VersionInfo.VERSION_UPDATED, false); } else if (projectVersion.isOlderAndBreaking(apiVersion)) { String message = projectVersion.getChangesMessage(apiVersion); boolean removeAWTImports; if (projectVersion.crosses300Boundary(apiVersion)) { message += "\n"; removeAWTImports = DialogManager.askQuestionFX(null, "greenfoot-importfix-question", message) == 0; } else { DialogManager.showInfoTextFX(null, Config.getString("project.version.mismatch"), message, false); removeAWTImports = false; } prepareGreenfootProject(greenfootLibDir, project, projectAPIVersionAccess, true, greenfootApiVersion); return new VersionCheckInfo(VersionInfo.VERSION_UPDATED, removeAWTImports); } else if (apiVersion.isOlderAndBreaking(projectVersion)) { String message = projectVersion.getNewerMessage(); int buttonIndex = DialogManager.showInfoTextFX(null, Config.getString("project.version.mismatch"), message, true); if (buttonIndex == 0) { return new VersionCheckInfo(VersionInfo.VERSION_BAD, false); } prepareGreenfootProject(greenfootLibDir, project, projectAPIVersionAccess, true, greenfootApiVersion); return new VersionCheckInfo(VersionInfo.VERSION_UPDATED, false); } else if (projectVersion.isNonBreaking(apiVersion)) { prepareGreenfootProject(greenfootLibDir, project, projectAPIVersionAccess, true, greenfootApiVersion); return new VersionCheckInfo(VersionInfo.VERSION_UPDATED, false); } else if (projectVersion.isInternal(apiVersion)) { prepareGreenfootProject(greenfootLibDir, project, projectAPIVersionAccess, false, greenfootApiVersion); return new VersionCheckInfo(VersionInfo.VERSION_UPDATED, false); } else { prepareGreenfootProject(greenfootLibDir, project, projectAPIVersionAccess, false, greenfootApiVersion); return new VersionCheckInfo(VersionInfo.VERSION_OK, false); } }
| Makes a project a greenfoot project. It cleans up the project directory | and makes sure everything that needs to be there is there. | | @param deleteClassFiles whether the class files in the destination should | be deleted. If true, they will be deleted and appear as | needing a recompile in the Greenfoot class browser. | @OnThread(Tag.FXPlatform) private static void prepareGreenfootProject(File greenfootLibDir, Project project, ProjectAPIVersionAccess p, boolean deleteClassFiles, String greenfootApiVersion) { File dst = project.getProjectDir(); File greenfootDir = new File(dst, "greenfoot"); deleteGreenfootDir(greenfootDir); if (deleteClassFiles) { deleteAllClassFiles(dst); } try { File pkgFile = new File(dst, "bluej.pkg"); if (pkgFile.exists()) { pkgFile.delete(); } File pkhFile = new File(dst, "bluej.pkh"); if (pkhFile.exists()) { pkhFile.delete(); } } catch (SecurityException e) { } try { File images = new File(dst, "images"); images.mkdir(); File sounds = new File(dst, "sounds"); sounds.mkdir(); } catch (SecurityException e) { Debug.reportError("SecurityException when trying to create images/sounds directories", e); } p.setAPIVersionAndSave(project, greenfootApiVersion); }
| Deletes all class files in the directory, including the greenfoot subdirectory, | only if they have a .java file related to them. | private static void deleteAllClassFiles(File dir) { String[] classFiles = dir.list(classFilter); if (classFiles == null) return; for (int i = 0; i < classFiles.length; i++) { String fileName = classFiles[i]; int index = fileName.lastIndexOf('.'); String javaFileName = fileName.substring(0, index) + "." + SourceType.Java.toString().toLowerCase(); File file = new File(dir, fileName); File javaFile = new File(dir, javaFileName); if (javaFile.exists()) { file.delete(); } } } private static void deleteGreenfootDir(File greenfootDir) { if (greenfootDir.exists()) { try { File actorJava = new File(greenfootDir, "Actor.java"); if (actorJava.exists()) { actorJava.delete(); } } catch (SecurityException e) { } try { File worldJava = new File(greenfootDir, "World.java"); if (worldJava.exists()) { worldJava.delete(); } } catch (SecurityException e) { } try { File actorJava = new File(greenfootDir, "Actor.class"); if (actorJava.exists()) { actorJava.delete(); } } catch (SecurityException e) { } try { File worldJava = new File(greenfootDir, "World.class"); if (worldJava.exists()) { worldJava.delete(); } } catch (SecurityException e) { } try { File worldJava = new File(greenfootDir, "project.greenfoot"); if (worldJava.exists()) { worldJava.delete(); } } catch (SecurityException e) { } try { greenfootDir.delete(); } catch (SecurityException e) { } } }
| Launch the Greenfoot debug VM code (and tell it where to connect to for RMI purposes). | | @param project A just-opened project | @OnThread(Tag.FXPlatform) public void openGreenfoot(final Project project, GreenfootDebugHandler greenfootDebugHandler) { File shmFile = greenfootDebugHandler.getShmFile(); Properties debugVMProps = new Properties(); debugVMProps.put("bluej.language", Config.getPropString("bluej.language", "")); debugVMProps.put("vm.language", Config.getPropString("vm.language", "")); debugVMProps.put("vm.country", Config.getPropString("vm.country", "")); File tmpPropsFile = null; try { tmpPropsFile = File.createTempFile("debugvm", "props"); tmpPropsFile.deleteOnExit(); debugVMProps.store(new FileOutputStream(tmpPropsFile), ""); } catch (IOException e) { Debug.reportError(e); } final String[] consParams = { project.getProjectDir().getPath(), Config.getBlueJLibDir().getAbsolutePath(), Config.getUserConfigDir().getAbsolutePath(), tmpPropsFile == null ? "" : tmpPropsFile.getAbsolutePath(), shmFile == null ? "" : shmFile.getAbsolutePath(), Integer.toString(greenfootDebugHandler.getShmFileSize()) }; Package pkg = project.getPackage(""); final Debugger debugger = pkg.getProject().getDebugger(); String [] argTypes = new String[consParams.length]; DebuggerObject [] argObjects = new DebuggerObject[consParams.length]; for (int i = 0; i < consParams.length; i++) { argTypes[i] = "java.lang.String"; argObjects[i] = debugger.getMirror(consParams[i]); } new Thread() { public void run() { DebuggerResult result = debugger.instantiateClass(launchClass, argTypes, argObjects); Platform.runLater(() -> { final DebuggerObject debugObject = result.getResultObject(); if (debugObject != null) { String wrappedName = greenfootDebugHandler.addObject(debugObject, debugObject.getGenType(), launcherName); pkg.getDebugger().addObject(pkg.getQualifiedName(), wrappedName, debugObject); pkg.compileOnceIdle(null, CompileReason.LOADED, CompileType.INTERNAL_COMPILE); } int status = result.getExitStatus(); if (status == Debugger.EXCEPTION) { Debug.message("Greenfoot launch failed due to exception in debug VM: " + result.getException().getText()); greenfootLaunchFailed(project); } else if (status == Debugger.TERMINATED) { Debug.message("Greenfoot launch failed due to debug VM terminating."); greenfootLaunchFailed(project); } }); } }.start(); } }
top, use, map, class ProjectManager

.   accept

top, use, map, class ProjectManager . ProjectAPIVersionAccess

.   getAPIVersion
.   setAPIVersionAndSave
.   ProjectManager
.   instance
.   launchProject
.   checkLaunchFailed
.   greenfootLaunchFailed
.   checkVersion
.   prepareGreenfootProject
.   deleteAllClassFiles
.   deleteGreenfootDir
.   openGreenfoot
.   run




612 neLoCode + 33 LoComm