package com.mobilesorcery.sdk.builder.blackberry;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import com.mobilesorcery.sdk.builder.java.KeystoreCertificateInfo;
import com.mobilesorcery.sdk.core.CollectingLineHandler;
import com.mobilesorcery.sdk.core.CommandLineBuilder;
import com.mobilesorcery.sdk.core.CommandLineExecutor;
import com.mobilesorcery.sdk.core.MoSyncBuilder;
import com.mobilesorcery.sdk.core.MoSyncTool;
import com.mobilesorcery.sdk.core.Util;
import com.mobilesorcery.sdk.core.Version;
public class JDE {
private final static Pattern JDE_RAPC_VERSION_PATTERN = Pattern.compile(".*Build:\\s*(.*)");
private static final Version VER_430 = new Version("4.3.0");
public final static int TYPE_DEV_TOOLS = 0;
public final static int TYPE_SIMULATOR = 1;
private IPath root;
private Version version;
protected JDE(IPath root, Version version) {
this.root = root;
this.version = version;
}
public IPath getLocation() {
return root;
}
public Version getVersion() {
return version;
}
public void apply(JDE workingCopy) {
this.root = workingCopy.root;
this.version = workingCopy.version;
}
public int getType() {
return TYPE_DEV_TOOLS;
}
/**
* Uses heuristics to guess the version of a JDE at a certain location
* @return <code>null</code> if for some reason the version could not
* be guessed
*/
public static Version getVersion(int toolType, IPath root) {
JDE throwaway = create(toolType, root, null);
try {
return throwaway.guessVersion();
} catch (IOException e) {
return null;
}
}
public static JDE create(int toolType, IPath root, Version version) {
return toolType == TYPE_DEV_TOOLS ? new JDE(root, version) :
new Simulator(root, version);
}
public Version guessVersion() throws IOException {
CollectingLineHandler lines = new CollectingLineHandler();
CommandLineExecutor executor = new CommandLineExecutor(MoSyncBuilder.CONSOLE_ID);
executor.setLineHandlers(lines, lines);
runRapc(executor, new String[] { "-version" });
for (String line : lines.getLines()) {
Version version = guessVersion(line);
if (version != null) {
return version;
}
}
return null;
}
private void runRapc(CommandLineExecutor exe, String[] args) throws IOException {
String[] baseCommand = new String[] { "java", "-classpath",
getRapc().toFile().getAbsolutePath(),
"net.rim.tools.compiler.Compiler" };
ArrayList<String> fullCommand = new ArrayList<String>();
fullCommand.addAll(Arrays.asList(baseCommand));
fullCommand.addAll(Arrays.asList(args));
exe.runCommandLine(fullCommand.toArray(new String[0]));
}
private Version guessVersion(String version) {
Matcher versionMatcher = JDE_RAPC_VERSION_PATTERN.matcher(version);
if (versionMatcher.matches()) {
return new Version(versionMatcher.group(1)).truncate(Version.MICRO);
}
return null;
}
private IPath getRapc() {
return root.append("bin/rapc.jar");
}
private IPath getPreverifier() {
return root.append("bin/preverify" + MoSyncTool.getBinExtension());
}
private IPath getSignTool() {
// Win only
return root.append("bin/SignatureTool.jar");
}
/**
* Preverifies a jar and repackages it into a new jar -- will create a number of temporary files
* @param input
* @param output
* @throws IOException
*/
public void preverifyJAR(File jar, File output) throws IOException {
File tmpJar = new File(jar.getParentFile(), "tmp-jar");
Util.deleteFiles(tmpJar, null, Util.INFINITE_DEPTH, new NullProgressMonitor());
File tmpPrev = new File(jar.getParentFile(), "tmp-prev");
try {
Util.unjar(jar, tmpJar);
// Copy all non-class files!
Util.copy(new NullProgressMonitor(), tmpJar, tmpPrev, new FileFilter() {
@Override
public boolean accept(File file) {
return !"class".equalsIgnoreCase(Util.getExtension(file));
}
});
String[] preverifyCommandLine = new String[] {
getPreverifier().toFile().getAbsolutePath(),
"-classpath",
getBootClasspath().toFile().getAbsolutePath(),
"-d",
tmpPrev.getAbsolutePath(),
tmpJar.getAbsolutePath()
};
CommandLineExecutor executor = new CommandLineExecutor(MoSyncBuilder.CONSOLE_ID);
executor.runCommandLine(preverifyCommandLine);
Util.jar(tmpPrev, output);
} finally {
Util.deleteFiles(tmpJar, null, Util.INFINITE_DEPTH, new NullProgressMonitor());
Util.deleteFiles(tmpPrev, null, Util.INFINITE_DEPTH, new NullProgressMonitor());
}
}
public void convertJARToCOD(File jar, File jad, File cod) throws IOException {
String codNameParam = getVersion().isOlder(VER_430) ? "codename" : "codname";
String[] rapcCommandLine = new String[] {
"import=" + getBootClasspath().toOSString(),
codNameParam + "=" + Util.getNameWithoutExtension(cod),
"-midlet",
"-quiet",
"jad=" + jad.getAbsolutePath(),
jar.getAbsolutePath()
};
CommandLineExecutor executor = new CommandLineExecutor(MoSyncBuilder.CONSOLE_ID);
executor.setExecutionDirectory(jar.getParent());
runRapc(executor, rapcCommandLine);
}
public void sign(File finalOutput, KeystoreCertificateInfo certInfo) throws IOException {
if (VER_430.isNewer(getVersion())) {
throw new IOException("Signing is only supported for BlackBerry JDE versions above 4.3.0 (due to limitations in the JDE -- please sign manually)");
}
String password = certInfo.getKeyPassword();
if (Util.isEmpty(password)) {
throw new IOException("Missing password for signing");
}
// BLURGH!
CommandLineBuilder commandLine = new CommandLineBuilder("java", true).flag("-jar").with(getSignTool().toFile().getAbsolutePath()).
flag("-p", true).with(password).flag("-C").flag("-a").with(finalOutput);
CommandLineExecutor executor = new CommandLineExecutor(MoSyncBuilder.CONSOLE_ID);
executor.setExecutionDirectory(finalOutput.getParent());
executor.runCommandLine(commandLine.asArray(), commandLine.toHiddenString());
}
private IPath getBootClasspath() {
return root.append("lib/net_rim_api.jar");
}
@Override
public String toString() {
String versionStr = (version == null ? "?" : version
.asCanonicalString());
return getClass().getSimpleName() + " version " + versionStr;
}
}