/*******************************************************************************
* Copyright (c) 2004 Ferenc Hechler - ferenc_hechler@users.sourceforge.net
*
* This file is part of the Fat Jar Eclipse Plug-In
*
* The Fat Jar Eclipse Plug-In is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option) any later version.
*
* The Fat Jar Eclipse Plug-In 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Fat Jar Eclipse Plug-In;
* if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*******************************************************************************/
package net.sf.fjep.fatjar.builder;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import net.sf.fjep.autojar.AutoJarFilter;
/**
* This class generates the "Fat Jar".
*
* @author Ferenc Hechler
*/
public class FatJarBuilder {
private Map contents;
String[] visitClasses;
boolean searchClassForName;
private ArrayList fileSystemSources = null;
private ArrayList conflictResolvers = null;
private boolean escapeUCase = false;
public boolean getEscapeUCase() { return escapeUCase; }
public void setEscapeUCase(boolean escapeUCase) { this.escapeUCase = escapeUCase; }
private boolean removeSigners = true;
public boolean getRemoveSigners() { return removeSigners; }
public void setRemoveSigners(boolean removeSigners) { this.removeSigners = removeSigners; }
/**
* Use this class to generate a Fat Jar.
* 1. add multiple IFileSystemSources addSource().
* 2. Create the Fat Jar using build().
* @param tempBuildDir - temporary directory which will be
* created to collect data and removed after Fat Jar creation
* is finished
* @param fatJarFilename - Fat Jar to create with build(),
* overwrite if it already exists
*/
public FatJarBuilder() {
contents = new HashMap();
fileSystemSources = new ArrayList();
conflictResolvers = new ArrayList();
}
/**
* add a File System Source to collection
* @param fileSource
*/
public void addSource(IFileSystemSource fileSource) {
if (!fileSystemSources.contains(fileSource))
fileSystemSources.add(fileSource);
}
/**
* add classes to visit for AutoJar
* @param visitClasses
*/
public void addVisitClasses(String[] visitClasses) {
this.visitClasses = visitClasses;
}
public void setSearchClassForName(boolean searchClassForName) {
this.searchClassForName = searchClassForName;
}
/**
* add a Conflict Resolver to collection
* @param conflictResolver
*/
public void addConflictResolver(IConflictResolver conflictResolver) {
if (!conflictResolvers.contains(conflictResolver))
conflictResolvers.add(conflictResolver);
}
/**
* collect - pack - clean
*/
public void build(String fatJarFilename) {
clean();
collect();
autoJarFilter();
removeSigners();
pack(fatJarFilename);
clean();
}
private void autoJarFilter() {
if (visitClasses != null) {
AutoJarFilter ajf = new AutoJarFilter(contents, searchClassForName);
for (int i = 0; i < visitClasses.length; i++) {
String usedClass = visitClasses[i];
ajf.addRefClasses(usedClass);
}
Set excludes = new HashSet();
for (Iterator iter = contents.keySet().iterator(); iter.hasNext();) {
String contentName = (String) iter.next();
if (contentName.endsWith(".class")) {
if (!ajf.isChecked(contentName.substring(0, contentName.length()-6))) {
excludes.add(contentName);
}
}
}
for (Iterator iter = excludes.iterator(); iter.hasNext();) {
String contentName = (String) iter.next();
contents.remove(contentName);
}
}
}
private void removeSigners() {
if (removeSigners) {
Set excludes = new HashSet();
for (Iterator iter = contents.keySet().iterator(); iter.hasNext();) {
String contentName = (String) iter.next();
if (contentName.matches("META-INF/.*[.]SF")) {
excludes.add(contentName);
}
}
for (Iterator iter = excludes.iterator(); iter.hasNext();) {
String contentName = (String) iter.next();
contents.remove(contentName);
}
}
}
/**
* collect all data added via addSource to the tempBuildDir
* in the order of adding.
* Order is only relevant for conflicts.
*/
private void collect() {
for (Iterator iterator = fileSystemSources.iterator(); iterator.hasNext();) {
IFileSystemSource fileSystemSource = (IFileSystemSource) iterator.next();
while (fileSystemSource.hasMoreElements()) {
IFileSystemElement fileSystemElement = (IFileSystemElement) fileSystemSource.nextElement();
if (escapeUCase) {
fileSystemElement = new UCaseFileSystemElement(fileSystemElement, true);
}
try {
addContent(fileSystemElement);
} catch (IOException e) {
e.printStackTrace();
System.err.println("error adding " + fileSystemElement.getFullName());
}
}
}
}
private void addContent(IFileSystemElement fileSystemElement) throws IOException {
String contentName = fileSystemElement.getFullName().replace(File.separatorChar, '/');
if (contents.containsKey(contentName)) {
resolveConflicts(fileSystemElement);
}
else {
byte[] contentBytes;
long size = fileSystemElement.getSize();
InputStream fileStream = fileSystemElement.getStream();
if (size == -1) {
size = 0;
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
while (true) {
int cnt = fileStream.read(buf);
if (cnt <= 0) {
break;
}
out.write(buf, 0, cnt);
size += cnt;
}
out.flush();
contentBytes = out.toByteArray();
out.close();
}
else {
contentBytes = new byte[(int)size];
fileStream.read(contentBytes);
}
fileStream.close();
contents.put(contentName, contentBytes);
}
}
/**
* handle conflicts (multiple sources for same target output file.
* currently this function does nothing, the first file written wins.
* TODO: handle manifest.mf when merging is active.
* @param conflictOutputFile
* @param fileSystemElement
*/
private void resolveConflicts(File conflictOutputFile, IFileSystemElement fileSystemElement) {
boolean ok = false;
for (Iterator iterator = conflictResolvers.iterator(); iterator.hasNext();) {
IConflictResolver conflictResolver = (IConflictResolver) iterator.next();
ok = conflictResolver.handleConflict(conflictOutputFile, fileSystemElement);
if (ok)
break;
}
if (!ok) {
System.out.println("not resolving conflict in file " + fileSystemElement.getFolder() + File.separator + fileSystemElement.getName());
}
}
/**
* resolve conflict with existing entry in contents
* @param fileSystemElement
*/
private void resolveConflicts(IFileSystemElement fileSystemElement) {
boolean ok = false;
if (!ok) {
System.out.println("not resolving conflict in file " + fileSystemElement.getFolder() + File.separator + fileSystemElement.getName());
}
}
/**
* erase tempBuildDir
*/
private void clean() {
contents.clear();
}
/**
* jar all files in tempBuildDir to fatJarFilename
*/
private void pack(String jarName) {
IJarBuilder newJar = new JarBuilder(jarName);
if (contents.containsKey("META-INF/MANIFEST.MF")) {
String contentName = "META-INF/MANIFEST.MF";
byte[] contentBytes = (byte[])contents.get(contentName);
newJar.add(contentBytes, contentName);
contents.remove(contentName);
}
for (Iterator iter = contents.keySet().iterator(); iter.hasNext();) {
String contentName = (String) iter.next();
byte[] contentBytes = (byte[])contents.get(contentName);
newJar.add(contentBytes, contentName);
}
newJar.close();
}
public static void main(String[] args) {
String absFilename = "U:/opt/eclipse301/runtime-workbench-workspace/AntExportTest/classes/aet/AETMain.class";
String relFilename = "aet/AETMain.class";
NativeFileSystemSource s = new NativeFileSystemSource(new File(absFilename), relFilename);
while (s.hasMoreElements()) {
IFileSystemElement e = s.nextElement();
System.out.println(e);
}
}
}