package bluej.pkgmgr;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.jar.Attributes;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import bluej.pkgmgr.target.ClassTarget;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.stage.FileChooser.ExtensionFilter;
import javafx.stage.Window;
import bluej.Config;
import bluej.extensions.SourceType;
import bluej.utility.Debug;
import bluej.utility.DialogManager;
import bluej.utility.FileUtility;
import bluej.utility.Utility;
import threadchecker.OnThread;
import threadchecker.Tag;
| Component to manage storing projects to jar file format.
|
| @author Michael Kolling
|
final class ExportManager
{
private static final String specifyJar = Config.getString("pkgmgr.export.specifyJar");
private static final String sourceSuffix = "." + SourceType.Java.toString().toLowerCase();
private static final String contextSuffix = ".ctxt";
private static final String packageFilePrefix = "bluej.pk";
private static final String packageFileSuffix = ".bluej";
private static final String packageFileBackup = "bluej.pkh";
private final PkgMgrFrame frame;
@OnThread(Tag.FXPlatform)
private ExportDialog dialog;
public ExportManager(PkgMgrFrame frame)
{
this.frame = frame;
}
| Envoke the "create jar" user function. This starts by displaying the
|* export dialog, then it reads the options and performs the export to jar.
*/
public void export()
{
Project proj = frame.getProject();
|
|ExportDialog.ProjectInfo projectInfo = new ExportDialog.ProjectInfo(proj);
|
|boolean hasStride = proj.getPackageNames().stream().map(proj::getPackage)
|
|.flatMap(p -> p.getClassTargets().stream())
|
|.anyMatch(ct -> ct.getSourceType() == SourceType.Stride);
|
|Window parent = frame.getFXWindow();
|
|if (dialog == null)
|
|dialog = new ExportDialog(parent, projectInfo);
|
|else{ dialog.updateDialog(projectInfo);
|
|}
|
|Optional<ExportDialog.ExportInfo> result = dialog.showAndWait();
|
|if (!result.isPresent())
|
|return;
|
|ExportDialog.ExportInfo info = result.get();
|
|if (!info.mainClassName.equals(""))
{
for (Package p : proj.getProjectPackages())
{
for (ClassTarget c : p.getClassTargets())
{
if (!c.isCompiled())
|
|{
|
|DialogManager.showErrorFX(parent,"jar-executable-uncompiled-project");
return;
}
}
}
}
File fileName = FileUtility.getSaveFileFX(parent, specifyJar, Arrays.asList(new ExtensionFilter("JAR file", "*.jar")), false);
if (fileName == null)
return;
String sourceDir = proj.getProjectDir().getPath();
createJar(proj, fileName.getAbsolutePath().toString(), sourceDir, info.mainClassName, info.selectedFiles,
|
|info.includeSource, info.includePkgFiles, hasStride);
|
|}
|
|/**
| Export this project to a jar file.
|
@OnThread(Tag.FXPlatform)
private void createJar(Project proj, String fileName, String sourceDir, String mainClass,
List<File> userLibs, boolean includeSource, boolean includePkgFiles, boolean includeStrideLang)
{
if (!fileName.endsWith(".jar"))
fileName = fileName + ".jar";
File jarFile = new File(fileName);
OutputStream oStream = null;
JarOutputStream jStream = null;
try {
Manifest manifest = new Manifest();
Attributes attr = manifest.getMainAttributes();
attr.put(Attributes.Name.MANIFEST_VERSION, "1.0");
attr.put(Attributes.Name.MAIN_CLASS, mainClass);
oStream = new FileOutputStream(jarFile);
jStream = new JarOutputStream(oStream, manifest);
JarOutput jarOutput = new JarOutput(jStream);
writeDirToJar(new File(sourceDir), "", jarOutput, includeSource,
includePkgFiles,
jarFile.getCanonicalFile());
if (includeStrideLang)
{
includeJarContent(new File(Config.getBlueJLibDir(), "lang-stride.jar"), jarOutput);
}
for (URL url : proj.getPlusLibsContent())
{
try
{
includeJarContent(new File(new URI(url.toString())), jarOutput);
}
catch (URISyntaxException urie)
{
}
}
for (File f : userLibs)
includeJarContent(f, jarOutput);
frame.setStatus(Config.getString("pkgmgr.exported.jar"));
}
catch(IOException exc) {
DialogManager.showErrorFX(frame.getFXWindow(), "error-writing-jar");
Debug.reportError("problem writing jar file: " + exc);
} finally {
try {
if (jStream != null)
jStream.close();
} catch (IOException e) {
}
}
}
@OnThread(Tag.Any)
private void includeJarContent(File srcJarFile, JarOutput jarOutput) throws IOException
{
try (ZipFile jar = new ZipFile(srcJarFile)) {
for (ZipEntry entry : Utility.iterableStream(jar.stream())) {
jarOutput.writeJarEntry(jar.getInputStream(entry), entry.getName());
}
}
}
| Write the contents of a directory to a jar stream. Recursively called
| for subdirectories.
| outputFile should be the canonical file representation of the Jar file
| we are creating (to prevent including itself in the Jar file)
|
@OnThread(Tag.Any)
private void writeDirToJar(File sourceDir, String pathPrefix,
JarOutput jarOutput, boolean includeSource, boolean includePkg, File outputFile)
throws IOException
{
File[] dir = sourceDir.listFiles();
for (int i = 0; i < dir.length; i++) {
if (dir[i].isDirectory()) {
if (!skipDir(dir[i], includePkg) ) {
writeDirToJar(dir[i], pathPrefix + dir[i].getName() + "/",
jarOutput, includeSource, includePkg, outputFile);
}
}
else {
if (!skipFile(dir[i].getName(), !includeSource, !includePkg) &&
!outputFile.equals(dir[i].getCanonicalFile())) {
jarOutput.writeJarEntry(dir[i], pathPrefix + dir[i].getName());
}
}
}
}
| Copy all files specified in the given list to the new jar directory.
|
@OnThread(Tag.Any)
private void copyLibsToJar(List<File> userLibs, File destDir)
throws IOException
{
for (Iterator<File> it = userLibs.iterator(); it.hasNext(); ) {
File lib = it.next();
FileUtility.copyFile(lib, new File(destDir, lib.getName()));
}
}
| array of directory names not to be included in jar file
|
@OnThread(Tag.Any)
private static final String[] skipDirs = { "CVS", ".svn", ".git"
};
| Test whether a given directory should be skipped (not included) in
| export.
|
@OnThread(Tag.Any)
private boolean skipDir(File dir, boolean includePkg)
{
if (dir.getName().equals(Project.projectLibDirName))
return ! includePkg;
for (int i = 0; i < skipDirs.length; i++) {
if (dir.getName().equals(skipDirs[i]))
return true;
}
return false;
}
| Checks whether a file should be skipped during a copy operation.
| BlueJ specific files (bluej.pkg and *.ctxt) and - optionally - Java
| source files are skipped.
|
@OnThread(Tag.Any)
private boolean skipFile(String fileName, boolean skipSource, boolean skipPkg)
{
if (fileName.equals(packageFileBackup))
return true;
if (fileName.endsWith(sourceSuffix) || fileName.endsWith(sourceSuffix + "~"))
return skipSource;
if (fileName.startsWith(packageFilePrefix) || fileName.endsWith(packageFileSuffix) ||
fileName.endsWith(contextSuffix))
return skipPkg;
return false;
}
| A class for writing files to a JAR which does not allow duplicate files.
| In the case of duplicates, the file inserted first is kept, with the later
| duplicate(s) discarded.
|
@OnThread(Tag.Any)
private static class JarOutput
{
private final JarOutputStream jStream;
private final HashSet<String> existingNames = new HashSet<>();
public JarOutput(JarOutputStream jStream)
{
this.jStream = jStream;
existingNames.add("META-INF/MANIFEST.MF");
}
| Write a jar file entry to the jar output stream.
| Note: entryName should always be a path with / seperators
| (NOT the platform dependant File.seperator)
|
@OnThread(Tag.Any)
public void writeJarEntry(File srcFile, String entryName)
throws IOException
{
writeJarEntry(new FileInputStream(srcFile), entryName);
}
@OnThread(Tag.Any)
public void writeJarEntry(InputStream src, String entryName)
throws IOException
{
try
{
if (!existingNames.contains(entryName))
{
existingNames.add(entryName);
jStream.putNextEntry(new ZipEntry(entryName));
FileUtility.copyStream(src, jStream);
}
} catch (ZipException exc)
{
Debug.message("warning: " + exc);
} finally
{
if (src != null)
src.close();
}
}
}
}
. - ExportManager
. ExportManager
. createJar
. includeJarContent
. writeDirToJar
. copyLibsToJar
. skipDir
. skipFile
top,
use,
map,
class JarOutput
. JarOutput
. writeJarEntry
. writeJarEntry
315 neLoCode
+ 40 LoComm