/*
* Copyright (c) 2010-2012 Research In Motion Limited. All rights reserved.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License, Version 1.0,
* which accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*
*/
package net.rim.ejde.internal.packaging;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.rim.ejde.internal.builders.ALXBuilder;
import net.rim.ejde.internal.builders.ResourceBuilder;
import net.rim.ejde.internal.core.ContextManager;
import net.rim.ejde.internal.core.IConstants;
import net.rim.ejde.internal.core.IRIMMarker;
import net.rim.ejde.internal.launching.DeploymentHelper;
import net.rim.ejde.internal.model.BlackBerryProject;
import net.rim.ejde.internal.model.BlackBerryProjectCoreNature;
import net.rim.ejde.internal.model.BlackBerryProperties;
import net.rim.ejde.internal.model.BlackBerryPropertiesFactory;
import net.rim.ejde.internal.model.BlackBerrySDKInstall;
import net.rim.ejde.internal.model.BlackBerryVMInstallType;
import net.rim.ejde.internal.packaging.JadFile.CodEntry;
import net.rim.ejde.internal.ui.consoles.PackagingConsole;
import net.rim.ejde.internal.util.ImportUtils;
import net.rim.ejde.internal.util.InternalPackagingUtils;
import net.rim.ejde.internal.util.Messages;
import net.rim.ejde.internal.util.PackageUtils;
import net.rim.ejde.internal.util.PackagingUtils;
import net.rim.ejde.internal.util.ProblemFactory;
import net.rim.ejde.internal.util.ProjectUtils;
import net.rim.ejde.internal.util.StatusFactory;
import net.rim.ejde.internal.util.VMUtils;
import net.rim.ejde.internal.validation.DiagnosticFactory;
import net.rim.ide.OSUtils;
import net.rim.ide.Project;
import net.rim.ide.core.Util;
import net.rim.sdk.resourceutil.ResourceParseException;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.eclipse.core.internal.resources.ResourceException;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.util.IClassFileReader;
import org.eclipse.jdt.core.util.IMethodInfo;
import org.eclipse.jdt.core.util.IModifierConstants;
import org.eclipse.jdt.internal.launching.JREContainer;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.LibraryLocation;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.MessageConsoleStream;
/**
* This class provides methods to package a BlackBerry project.
*
* Usually a project has two deployment folders:
* <p>
* /deliverables/Standard
* <p>
* and
* <p>
* /deliverables/Web
* <p>
* The project is packaged into the <code>/deliverables/Standard</code> folder and all generated rapc artifacts are copied to the
* <code>/deliverables/Web</code> folder. However, if the cod file is a zipped parent cod file, we un-zip it and copy all the
* sibling cod files in the the <code>/deliverables/Web</code> folder.
*/
public class PackagingManager {
static final Logger _log = Logger.getLogger( PackagingManager.class );
static final int MAX_COMMAND_ELEMENTS = 30;
public static final String COMPRESS_RESOURCE_OPTION = "-cr";
public static final String CONVERT_PNG_RAPC_OPTION = "-convertpng";
public static final String VERBOSE_RAPC_OPTION = "-verbose";
public static final int MIDLET_JAR = 0x0001;
public static final int EVISCERATED_JAR = 0x0002;
private Vector< String > _compileOptions;
private BlackBerryProject _bbProject;
private List< String > _rapcCommandsHead;
private List< String > _rapcCommands;
private Vector< String > _sourceRoots;
private Vector< String > _protectionOptions;
private Vector< ImportedJar > _imports;
private Vector< String > _otherFiles;
private Vector< String > _outputFolders;
private MessageConsoleStream _consoleOutputStream;
private boolean writeToFile;
private PackagingManager( BlackBerryProject bbProject ) {
_bbProject = bbProject;
_compileOptions = new Vector< String >();
_sourceRoots = new Vector< String >();
_imports = new Vector< ImportedJar >();
_otherFiles = new Vector< String >();
_protectionOptions = new Vector< String >();
_outputFolders = new Vector< String >();
_rapcCommandsHead = new ArrayList< String >();
_rapcCommands = new ArrayList< String >();
// Grab and activate a console to redirect build output to.
PackagingConsole packagingConsole = PackagingConsole.getInstance();
ConsolePlugin.getDefault().getConsoleManager().addConsoles( new IConsole[] { packagingConsole } );
_consoleOutputStream = packagingConsole.newMessageStream();
}
/**
* This is a help method to package a BlackBerry project.
*
* @param project
* A BlackBerry project
*
* @throws CoreException
*/
static public void packageProject( BlackBerryProject project ) throws CoreException {
PackagingManager packagingManage = new PackagingManager( project );
packagingManage.internalPackageProject();
}
/**
* This is a help method to package a BlackBerry project.
*
* @param project
* A BlackBerry project
*
* @throws CoreException
*/
static public void generateALXForProject( BlackBerryProject project ) throws CoreException {
PackagingManager packagingManage = new PackagingManager( project );
// if the deliverable folder does not exist, create it
IProject eclipseProject = project.getProject();
IPath outputFolderPath = new Path( PackagingUtils.getRelativeStandardOutputFolder( project ) );
try {
ImportUtils.createFolders( eclipseProject, outputFolderPath, IResource.DERIVED );
} catch( CoreException e ) {
throw new ResourceException( DiagnosticFactory.CREATE_FOLDER_ERR_ID, project.getMetaFileHandler()
.getProjectRelativePath(), NLS.bind( Messages.PackagingManager_PACKAGING_CANNOT_CREATE_FOLDER_MSG,
outputFolderPath ), e );
}
packagingManage.internalGenerateALX();
}
private void internalGenerateALX() throws CoreException {
generateALX();
IResource outputFolder = _bbProject.getProject().findMember(
new Path( PackagingUtils.getRelativeAlxFileOutputFolder( _bbProject ) ) );
outputFolder.refreshLocal( IResource.DEPTH_ONE, new NullProgressMonitor() );
}
private void internalPackageProject() throws CoreException {
final BlackBerrySDKInstall bbVM = PackagingUtils.getBBSDKInstall( _bbProject.getJavaProject() );
if( bbVM == null ) {
String msg = NLS.bind( Messages.PackagingManager_PACKAGING_NO_BB_JRE_MSG, _bbProject.getProject().getName() );
reportProblem( _bbProject.getProject(), -1, 0, 0, msg, Problem.ERROR );
_log.error( msg );
return;
}
// clean the deployment folders
try {
PackagingUtils.cleanProjectOutputFolder( _bbProject );
} catch( CoreException e ) {
_log.error( e );
}
IProject eclipseProject = _bbProject.getProject();
// if the deliverable folder does not exist, create it
IPath outputFolderPath = new Path( PackagingUtils.getRelativeStandardOutputFolder( _bbProject ) );
try {
ImportUtils.createFolders( eclipseProject, outputFolderPath, IResource.DERIVED );
} catch( CoreException e ) {
throw new ResourceException( DiagnosticFactory.CREATE_FOLDER_ERR_ID, _bbProject.getMetaFileHandler()
.getProjectRelativePath(), NLS.bind( Messages.PackagingManager_PACKAGING_CANNOT_CREATE_FOLDER_MSG,
outputFolderPath ), e );
}
// calculate rapc commands
calculateRAPCCommand();
// check if the project should be packaged
if( !shouldPackage() ) {
return;
}
// run rapc command
runRapcCommand();
// post packaging steps
postPackagingProcess( _bbProject );
}
private void postPackagingProcess( BlackBerryProject BBProject ) throws CoreException {
IPath outputFolderPath = new Path( PackagingUtils.getRelativeStandardOutputFolder( _bbProject ) );
IResource outputFolder = BBProject.getProject().findMember( outputFolderPath );
// copy the cod files of dependency projects to the deployment folders
copyDependencyDeploymentFiles();
// Refresh to show new resources
outputFolder.refreshLocal( IResource.DEPTH_INFINITE, new NullProgressMonitor() );
// Generate ALX File
if( BBProject.getProperties()._packaging.getGenerateALXFile().booleanValue() ) {
generateALX();
}
}
/**
* Copies the deployment files of the dependent projects to the corresponding deployment folders.
*
* @throws CoreException
*/
private void copyDependencyDeploymentFiles() throws CoreException {
IPath standardSrcFolderPath, standardDstFolderPath;
String outputFileName;
IPath absoluteStandardSrcFolderPath, absoluteStandardDstFolderPath;
List< CodEntry > codEntries = new ArrayList< CodEntry >();
String mainVersion = PackagingUtils.getVMOutputFolderName( _bbProject );
for( BlackBerryProject dependentProj : ProjectUtils.getAllReferencedProjects( _bbProject ) ) {
JadFile jadFile = null;
try {
jadFile = new JadFile( getJadFilePath( dependentProj ).toFile() );
jadFile.parseJadFile();
String dependencyVersion = PackagingUtils.getVMOutputFolderName( dependentProj );
if( !mainVersion.equals( dependencyVersion ) ) {
// if the BB JRE of the main project is different than the dependency projects, need to calculate the cod
// relative paths
for( CodEntry entry : jadFile.getCodeEntries() ) {
entry.setUrl( ".." + File.separator + dependencyVersion + File.separator + entry.getUrl() );
}
}
codEntries.addAll( jadFile.getCodeEntries() );
} catch( IOException e ) {
_log.error( e );
}
// get the standard deployment folder of the dependent project
standardDstFolderPath = new Path( PackagingUtils.getRelativeStandardOutputFolder( dependentProj ) );
// get the standard corresponding deployment folder of the main project
absoluteStandardDstFolderPath = _bbProject.getProject().getLocation().append( standardDstFolderPath );
// make sure the destination folder is created
try {
ImportUtils.createFolders( _bbProject.getProject(), standardDstFolderPath, IResource.DERIVED );
} catch( CoreException e ) {
throw new ResourceException( DiagnosticFactory.CREATE_FOLDER_ERR_ID, _bbProject.getMetaFileHandler()
.getProjectRelativePath(), NLS.bind( Messages.PackagingManager_PACKAGING_CANNOT_CREATE_FOLDER_MSG,
standardDstFolderPath ), e );
}
// copy deployment files in standard folder
standardSrcFolderPath = new Path( PackagingUtils.getRelativeStandardOutputFolder( dependentProj ) );
absoluteStandardSrcFolderPath = dependentProj.getProject().getLocation().append( standardSrcFolderPath );
outputFileName = dependentProj.getProperties()._packaging.getOutputFileName();
File[] outputFiles = absoluteStandardSrcFolderPath.toFile()
.listFiles( new DeploymentFileNameFilter( outputFileName ) );
_log.trace( "Dependent project " + dependentProj.getElementName() + " copy folder: " + standardSrcFolderPath
+ " --> " + _bbProject.getElementName() + " / " + standardDstFolderPath );
for( File file : outputFiles ) {
if( file.exists() ) {
DeploymentHelper.executeCopy( file, absoluteStandardDstFolderPath.append( file.getName() ).toFile() );
}
}
// refresh the standard folder
IFolder folder = _bbProject.getProject().getFolder( standardDstFolderPath );
folder.refreshLocal( IResource.DEPTH_ONE, new NullProgressMonitor() );
}
if( codEntries.size() != 0 ) {
writeCodEntry( codEntries );
}
}
private void writeCodEntry( List< CodEntry > codEntries ) {
File file = getJadFilePath( _bbProject ).toFile();
BufferedWriter writer = null;
List< String > list = new ArrayList< String >();
try {
JadFile jadFile = new JadFile( file );
jadFile.parseJadFile();
// add dependency cod entries
jadFile.addCodEntries( codEntries );
list = jadFile.getOtherProperties();
for( CodEntry entry : jadFile.getCodeEntries() ) {
list.add( entry.getCodURLPropertyLine() );
list.add( entry.getCodSizePropertyLine() );
list.add( entry.getCodCreationgTimePropertyLine() );
list.add( entry.getCodShaPropertyLine() );
}
IPath destFilePath = getJadFilePath( _bbProject );
destFilePath = destFilePath.removeLastSegments( 1 );
destFilePath = destFilePath.append( _bbProject.getProperties().getPackaging().getOutputFileName()
+ IConstants.FULL_JAD_FILE_SUFFIX + IConstants.JAD_FILE_EXTENSION_WITH_DOT );
writer = new BufferedWriter( new FileWriter( destFilePath.toFile() ) );
for( int i = 0; i < list.size(); i++ ) {
writer.write( list.get( i ) + "\r\n" );
}
} catch( Exception e ) {
_log.error( e );
} finally {
try {
if( writer != null ) {
writer.close();
}
} catch( IOException e ) {
_log.error( e );
}
}
}
public static final void copyInputStream( InputStream in, OutputStream out ) throws IOException {
byte[] buffer = new byte[ 1024 ];
int len;
while( ( len = in.read( buffer ) ) >= 0 )
out.write( buffer, 0, len );
in.close();
out.close();
}
class DeploymentFileNameFilter implements FilenameFilter {
String _outputFileName;
boolean _excludeCodFile;
public DeploymentFileNameFilter( String outputFileName ) {
_outputFileName = outputFileName;
}
public DeploymentFileNameFilter( String outputFileName, boolean excludeCod ) {
_outputFileName = outputFileName;
_excludeCodFile = excludeCod;
}
@Override
public boolean accept( File dir, String name ) {
if( name.startsWith( _outputFileName ) ) {
if( _excludeCodFile ) {
if( !name.endsWith( IConstants.COD_FILE_EXTENSION_WITH_DOT ) ) {
return true;
}
} else {
return true;
}
}
return false;
}
}
private void generateALX() throws CoreException {
BlackBerryProperties properties = _bbProject.getProperties();
String targetDir = PackagingUtils.getRelativeAlxFileOutputFolder( _bbProject );
String targetFolderLocation;
if( targetDir == null || targetDir.equals( IConstants.EMPTY_STRING ) ) {
targetFolderLocation = _bbProject.getProject().getLocation().toOSString();
} else {
targetFolderLocation = _bbProject.getProject().getFolder( new Path( targetDir ) ).getLocation().toOSString();
}
String filename = properties._packaging.getOutputFileName();
String alxName = targetFolderLocation + File.separator + filename;
try {
ALXBuilder.Alx alx = ALXBuilder.generateAlx( null, _bbProject );
ALXBuilder.write( alxName, alx );
} catch( ResourceParseException rpe ) {
throw new CoreException( new Status( IStatus.ERROR, ContextManager.PLUGIN_ID, rpe.getMessage() ) );
} catch( FileNotFoundException fnfe ) {
throw new CoreException( new Status( IStatus.ERROR, ContextManager.PLUGIN_ID, fnfe.getMessage() ) );
}
// refresh the alx file
IFile alxFile = _bbProject.getProject().getFolder( new Path( targetDir ) )
.getFile( filename + IConstants.ALX_FILE_EXTENSION_WITH_DOT );
alxFile.refreshLocal( IResource.DEPTH_INFINITE, new NullProgressMonitor() );
}
private void runRapcCommand() throws CoreException {
try {
File workDir = _bbProject.getProject().getLocation().toFile();
if( writeToFile ) {
File outputFile = null;
String outputFileName = _bbProject.getProject().getName() + ".files";
outputFile = new File( workDir, outputFileName );
_rapcCommandsHead.add( "@" + outputFileName );
flushToFile( outputFile );
} else {
_rapcCommandsHead.addAll( _rapcCommands );
}
String command = getStringCommand( _rapcCommandsHead );
_log.trace( "Execute rapc command: " + command + "; Working Directory: " + workDir.getPath() );
ProcessBuilder rapcBuilder = new ProcessBuilder( _rapcCommandsHead );
String javaHome = System.getenv( "JAVA_HOME" );
if( javaHome != null ) {
Map< String, String > env = rapcBuilder.environment();
String pathName = "Path";
for( String s : env.keySet() ) {
if( s.equalsIgnoreCase( "Path" ) )
pathName = s;
}
String path = env.get( pathName );
path = path == null ? javaHome : ( path + File.pathSeparator + javaHome );
path = path + File.pathSeparator + javaHome + File.separator + "bin";
env.put( pathName, path );
_log.trace( "PATH=" + path );
}
rapcBuilder.directory( workDir );
rapcBuilder.redirectErrorStream( true );
long startTime = System.currentTimeMillis();
_consoleOutputStream.println( NLS.bind( Messages.PackagingManager_PACKAGING_PROJECT_MSG, _bbProject.getProject()
.getName() ) );
_consoleOutputStream.println( command );
Process process = rapcBuilder.start();
InputStream inStream = process.getInputStream();
InputStreamHandler inputHandler = new InputStreamHandler( _bbProject.getProject(), _consoleOutputStream, inStream );
inputHandler.start();
int result = process.waitFor();
inputHandler.join();
float spendTime = ( (float) ( System.currentTimeMillis() - startTime ) ) / 1000;
if( result == 0 ) {
_consoleOutputStream.println( NLS.bind( Messages.PackagingManager_PACKAGING_SUCCEED_MSG, new String[] {
_bbProject.getProject().getName(), String.valueOf( spendTime ) } ) );
} else {
_consoleOutputStream.println( NLS.bind( Messages.PackagingManager_PACKAGING_FAILED_MSG, new String[] {
_bbProject.getProject().getName(), String.valueOf( spendTime ) } ) );
}
} catch( IOException e ) {
throw new CoreException( StatusFactory.createErrorStatus( e.getMessage() ) );
} catch( InterruptedException e ) {
throw new CoreException( StatusFactory.createErrorStatus( e.getMessage() ) );
}
}
private void flushToFile( File file ) throws IOException {
FileOutputStream fout = null;
PrintStream indirect = null;
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
indirect = new PrintStream( bout, false, "UTF-8" );
for( int i = 0; i < _rapcCommands.size(); i++ ) {
indirect.println( _rapcCommands.get( i ) );
}
indirect.close();
byte[] newBytes = bout.toByteArray();
// either old file doesn't exist or it isn't the same
// write out the new data
fout = new FileOutputStream( file );
fout.write( newBytes );
fout.close();
} finally {
if( indirect != null ) {
indirect.close();
}
if( fout != null ) {
fout.close();
}
}
}
private String getStringCommand( List< String > commands ) {
StringBuffer command = new StringBuffer();
for( int i = 0; i < commands.size(); i++ ) {
if( i != 0 ) {
command.append( IConstants.ONE_BLANK_STRING );
}
System.out.println( commands.get( i ) );
command.append( commands.get( i ) );
}
return command.toString();
}
private void calculateRAPCCommand() throws CoreException {
// calculate rapc commands
SourcerootVisitor visitor = new SourcerootVisitor();
_bbProject.getProject().accept( visitor );
// get customized jad and rapc files
String custjad = PackagingUtils.getCustomJadFile( _bbProject);
if(custjad!=null) {
_otherFiles.add( custjad );
}
String custrapc = PackagingUtils.getCustomRapcFile( _bbProject);
if(custrapc!=null) {
_otherFiles.add( custrapc );
}
// get imported jars (project level and workspace level)
_imports = getCompileImports( _bbProject );
// check if there is any exported midlet jar
// TODO: Try invoking rapc.jar in Windows instead of rapc.exe
if( OSUtils.isWindows() ) {
// add path of rapc.exe
_rapcCommandsHead.add( getRAPCPath() );
} else {
_rapcCommandsHead.add( "java" );
_rapcCommandsHead.add( "-jar" );
// add path of rapc.jar
_rapcCommandsHead.add( getRAPCPath() );
}
// get compile options
_compileOptions = getCompileOptions();
// add compile options
if( _compileOptions.size() > 0 ) {
_rapcCommandsHead.addAll( _compileOptions );
}
// get source roots
_sourceRoots = visitor.getSourceRoots( _bbProject.getProject() );
// add source roots
// TODO: Added for preverifier and do more investigation and
// see whether we can eliminate this option or not.
if( !OSUtils.isWindows() ) {
String binDir = getRAPCPath().replace( "rapc.jar", "" );
String exepath = "-exepath=" + binDir;
_rapcCommandsHead.add( exepath );
}
StringBuffer rapcComandBuffer = new StringBuffer();
if( _sourceRoots.size() > 0 ) {
rapcComandBuffer.append( "-sourceroot=" );
rapcComandBuffer.append( composeString( _sourceRoots, File.pathSeparator ) );
_rapcCommandsHead.add( rapcComandBuffer.toString() );
}
// get protection options
_protectionOptions = getProtectionOptions( false );
// add imports
rapcComandBuffer = new StringBuffer();
writeToFile = ( _imports.size() + _protectionOptions.size() ) > MAX_COMMAND_ELEMENTS;
if( _imports.size() > 0 ) {
if( writeToFile ) {
for( int i = 0; i < _imports.size(); i++ ) {
_rapcCommands.add( "-import=" + _imports.get( i ).toString() );
}
} else {
rapcComandBuffer.append( "-import=" );
rapcComandBuffer.append( composeImportsString( _imports, File.pathSeparator, false ) );
_rapcCommands.add( rapcComandBuffer.toString() );
}
}
// add protection options
if( _protectionOptions.size() > 0 ) {
_rapcCommands.addAll( _protectionOptions );
}
// add exported jar files
for( int i = 0; i < _imports.size(); i++ ) {
if( _imports.get( i ).isExported() ) {
_rapcCommands.add( _imports.get( i ).getPath() );
}
}
prepareDescriptor();
// add other files
_rapcCommands.addAll( _otherFiles );
// get output folders
getOutputFolder();
// add output folders
if( _outputFolders.size() > 0 ) {
_rapcCommands.addAll( _outputFolders );
}
}
private void prepareDescriptor() throws CoreException {
boolean found = false;
IPath outputFilePath = PackagingUtils.getAbsoluteStandardOutputFilePath( _bbProject ).removeLastSegments( 1 );
int i;
for( i = 0; i < _otherFiles.size(); i++ ) {
String sl = _otherFiles.get( i ).toLowerCase();
if( sl.endsWith( IConstants.RAPC_FILE_EXTENSION_WITH_DOT ) || sl.endsWith( IConstants.JAD_FILE_EXTENSION_WITH_DOT ) ) {
File f = new File( _otherFiles.get( i ) );
String distFileName = _bbProject.getProperties().getPackaging().getOutputFileName();
if( sl.endsWith( IConstants.RAPC_FILE_EXTENSION_WITH_DOT ) ) {
distFileName += IConstants.RAPC_FILE_EXTENSION_WITH_DOT;
found = true;
} else {
distFileName += IConstants.JAD_FILE_EXTENSION_WITH_DOT;
}
File f2 = outputFilePath.append( distFileName ).toFile();
DeploymentHelper.executeCopy( f, f2 );
_otherFiles.set( i, f2.getAbsolutePath() );
}
}
if( !found ) {
// generate rapc file
RAPCFile rapcFile = new RAPCFile( _bbProject );
rapcFile.loadContent();
rapcFile.flushToFile();
_otherFiles.addElement( RAPCFile.getRelativeRapcFilePath( _bbProject ).toOSString() );
}
}
private IPath getJadFilePath( BlackBerryProject bbProject ) {
IPath outputFilePath = PackagingUtils.getAbsoluteStandardOutputFilePath( bbProject ).removeLastSegments( 1 );
String jadFileName = bbProject.getProperties().getPackaging().getOutputFileName();
jadFileName += IConstants.JAD_FILE_EXTENSION_WITH_DOT;
return outputFilePath.append( jadFileName );
}
/**
* Checks if the project should be packaged.
*
* @return
*/
private boolean shouldPackage() {
int MidletJarNumber = 0;
for( ImportedJar jar : _imports ) {
if( jar.isExported() ) {
if( jar.isMidletJar() ) {
MidletJarNumber++;
if( MidletJarNumber != 1 ) {
reportProblem( _bbProject.getProject(), 0, 0, 0, Messages.PackagingManager_MIDLET_JAR_ERROR_MSG1,
Problem.ERROR );
return false;
}
Vector< String > jadFiles = getJadFiles( _otherFiles );
if( jadFiles.size() == 0 ) {
reportProblem( _bbProject.getProject(), 0, 0, 0, Messages.PackagingManager_MIDLET_JAR_ERROR_MSG2,
Problem.WARNING );
} else if( jadFiles.size() > 1 ) {
reportProblem( _bbProject.getProject(), 0, 0, 0, Messages.PackagingManager_MIDLET_JAR_ERROR_MSG3,
Problem.ERROR );
return false;
}
} else if( ( jar.getType() & EVISCERATED_JAR ) > 0 ) {
reportProblem( _bbProject.getProject(), 0, 0, 0,
NLS.bind( Messages.PackagingManager_MIDLET_JAR_ERROR_MSG4, jar._path ), Problem.ERROR );
return false;
}
}
}
return true;
}
private Vector< String > getJadFiles( Vector< String > files ) {
Vector< String > jadFiles = new Vector< String >();
for( String file : files ) {
if( file.endsWith( IConstants.JAD_FILE_EXTENSION_WITH_DOT ) ) {
jadFiles.add( file );
}
}
return jadFiles;
}
private void getOutputFolder() {
Set< IPath > outputFolderSet = ImportUtils.getOutputPathSet( _bbProject );
IPath javaOutRelPath;
for( IPath path : outputFolderSet ) {
// get rid of the first project segment
javaOutRelPath = path.removeFirstSegments( 1 ).makeRelative();
IFolder folder = _bbProject.getProject().getFolder( javaOutRelPath );
if( folder == null || !folder.exists() ) {
continue;
}
if( folder.isLinked() ) {
_outputFolders.add( folder.getLocation().toOSString() );
} else {
_outputFolders.add( _bbProject.getProject().getLocation().append( javaOutRelPath ).toOSString() );
}
}
}
private String composeImportsString( Vector< ImportedJar > vector, String separator, boolean exported ) {
StringBuffer buffer = new StringBuffer();
if( vector == null ) {
return buffer.toString();
}
boolean first = true;
for( int i = 0; i < vector.size(); i++ ) {
if( vector.get( i ).isExported() != exported ) {
continue;
}
if( first ) {
buffer.append( vector.get( i ).toString() );
first = false;
} else {
buffer.append( separator );
buffer.append( vector.get( i ).toString() );
}
}
return buffer.toString();
}
private String composeString( Vector< String > vector, String separator ) {
StringBuffer buffer = new StringBuffer();
if( vector == null ) {
return buffer.toString();
}
for( int i = 0; i < vector.size(); i++ ) {
if( i == 0 ) {
buffer.append( vector.get( i ).toString() );
} else {
buffer.append( separator );
buffer.append( vector.get( i ).toString() );
}
}
return buffer.toString();
}
static private boolean existingJar( Vector< ImportedJar > vec, ImportedJar jar ) {
if( jar == null ) {
return false;
}
for( int i = 0; i < vec.size(); i++ ) {
if( vec.get( i ).getPath().equalsIgnoreCase( jar.getPath() ) ) {
return true;
}
}
return false;
}
/**
* Gets paths of all imported jars (project level and workspace level) and jars of dependency projects.
*
* @return
* @throws CoreException
*/
static public Vector< ImportedJar > getCompileImports( IJavaProject jProject ) throws CoreException {
Vector< ImportedJar > vector = new Vector< ImportedJar >();
IClasspathEntry[] entries = jProject.getRawClasspath();
if( entries != null && entries.length > 0 ) {
getCompileImportsRecusively( entries, jProject, vector, true );
}
return vector;
}
static private void getCompileImportsRecusively( IClasspathEntry[] entries, IJavaProject jProject,
Vector< ImportedJar > imports, boolean isMainProject ) throws CoreException {
if( imports == null ) {
imports = new Vector< ImportedJar >();
}
// Workspace imports; if there aren't any specified, default to
// using the runtime libraries.
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
// String jarPathString;
try {
BlackBerryProperties properties = null;
boolean needAddBBJar = false;
IPath jarPath = null;
ImportedJar importedJar = null;
for( IClasspathEntry entry : entries ) {
switch( entry.getEntryKind() ) {
case IClasspathEntry.CPE_CONTAINER: {
// libraries
IClasspathContainer container = JavaCore.getClasspathContainer( entry.getPath(),
jProject.getJavaProject() );
if( container == null ) {
continue;
}
IVMInstall containerVM;
if( !( container instanceof JREContainer ) ) {
// We need to verify the type of the container because the path of Maven container only has one
// segment and JavaRuntime.getVMInstall(IPath) return the default VM install if the entry path has one
// segment.
containerVM = null;
} else {
containerVM = JavaRuntime.getVMInstall( entry.getPath() );
}
try {
if( containerVM != null ) {
if( containerVM.getVMInstallType().getId().equals( BlackBerryVMInstallType.VM_ID ) ) {
if( isMainProject ) {
// Add jars to a list
IClasspathEntry[] classpathEntries = container.getClasspathEntries();
if( classpathEntries != null && classpathEntries.length > 0 ) {
getCompileImportsRecusively( classpathEntries, jProject, imports, false );
}
}
} else {
if( !jProject.getProject().hasNature( BlackBerryProjectCoreNature.NATURE_ID ) ) {
needAddBBJar = true;
continue;
}
}
} else {
// Add jars to a list
IClasspathEntry[] classpathEntries = container.getClasspathEntries();
if( classpathEntries != null && classpathEntries.length > 0 ) {
getCompileImportsRecusively( classpathEntries, jProject, imports, false );
}
}
} catch( CoreException e ) {
_log.error( e.getMessage() );
continue;
}
break;
}
case IClasspathEntry.CPE_LIBRARY: {
// imported jars
jarPath = PackageUtils.getAbsoluteEntryPath( entry );
// the jar path can be null if the jar file does not exist
if( jarPath == null ) {
throw new CoreException( StatusFactory.createErrorStatus( NLS.bind(
Messages.PackagingManager_Entry_Not_Found_MSG, entry.getPath() ) ) );
}
if( jarPath.lastSegment().equals( IConstants.RIM_API_JAR ) && needAddBBJar ) {
needAddBBJar = false;
}
importedJar = null;
if( PackagingUtils.getPackagExportedJar() ) {
if( entry.isExported() ) {
if( isMainProject ) {
// if the exported jar is not in the main project but a dependent project, the classes it
// contains are packaged into the dependent project jar. We don't add it to classpath.
importedJar = new ImportedJar( jarPath.toOSString(), true, getJarFileType( jarPath.toFile() ) );
}
} else {
importedJar = new ImportedJar( jarPath.toOSString(), false, getJarFileType( jarPath.toFile() ) );
}
} else {
importedJar = new ImportedJar( jarPath.toOSString(), false, getJarFileType( jarPath.toFile() ) );
}
if( importedJar != null && !existingJar( imports, importedJar ) ) {
imports.add( importedJar );
}
break;
}
case IClasspathEntry.CPE_PROJECT: {
// dependency projects
IProject project = workspaceRoot.getProject( entry.getPath().toString() );
IJavaProject javaProject = JavaCore.create( project );
try {
if( project.hasNature( BlackBerryProjectCoreNature.NATURE_ID ) ) {
properties = ContextManager.PLUGIN.getBBProperties( javaProject.getProject().getName(), false );
if( properties == null ) {
_log.error( "BlackBerry properties is null" );
break;
}
} else {
properties = BlackBerryPropertiesFactory.createBlackBerryProperties( javaProject );
}
} catch( CoreException e ) {
_log.error( e.getMessage() );
continue;
}
if( PackagingManager.getProjectTypeID( properties._application.getType() ) == Project.LIBRARY ) {
IPath absoluteJarPath = PackagingUtils.getAbsoluteStandardOutputFilePath( new BlackBerryProject(
javaProject, properties ) );
File jarFile = new File( absoluteJarPath.toOSString() + IConstants.DOT_MARK
+ IConstants.JAR_EXTENSION );
importedJar = new ImportedJar( jarFile.getAbsolutePath(), false, getJarFileType( jarFile ) );
if( !existingJar( imports, importedJar ) ) {
imports.add( importedJar );
}
IClasspathEntry[] subEntries = javaProject.getRawClasspath();
if( subEntries != null && subEntries.length > 0 ) {
getCompileImportsRecusively( subEntries, javaProject, imports, false );
}
}
break;
}
case IClasspathEntry.CPE_VARIABLE: {
// variables
String e = entry.getPath().toString();
int index = e.indexOf( '/' );
if( index == -1 ) {
index = e.indexOf( '\\' );
}
String variable = e;
IPath cpvar = JavaCore.getClasspathVariable( variable );
if( cpvar == null ) {
String msg = NLS.bind( Messages.PackagingManager_Variable_Not_Defined_MSG, variable );
throw new CoreException( StatusFactory.createErrorStatus( msg ) );
}
if( cpvar.lastSegment().equals( IConstants.RIM_API_JAR ) && needAddBBJar ) {
needAddBBJar = false;
}
// TODO RAPC does not support a class folder. We may support it later on
if( cpvar.lastSegment().endsWith( "." + IConstants.JAR_EXTENSION ) ) {
importedJar = new ImportedJar( cpvar.toOSString(), false, getJarFileType( cpvar.toFile() ) );
if( !existingJar( imports, importedJar ) ) {
imports.add( importedJar );
}
}
break;
}
}
}
if( needAddBBJar && isMainProject ) {
// insert the default BB jre lib if needed
IVMInstall bbVM = VMUtils.getDefaultBBVM();
if( bbVM != null ) {
LibraryLocation[] libLocations = bbVM.getLibraryLocations();
if( libLocations != null ) {
for( LibraryLocation location : libLocations ) {
importedJar = new ImportedJar( location.getSystemLibraryPath().toOSString(), false,
getJarFileType( location.getSystemLibraryPath().toFile() ) );
if( !existingJar( imports, importedJar ) ) {
imports.add( importedJar );
}
}
}
}
}
} catch( JavaModelException e ) {
_log.error( e.getMessage() );
}
}
public static String quoteFile( String fileName ) {
return IConstants.DOUBLE_QUOTE + fileName + IConstants.DOUBLE_QUOTE;
}
public static int getProjectTypeID( String type ) {
if( type.trim().equalsIgnoreCase( BlackBerryProject.CLDC_APPLICATION ) ) {
return Project.CLDC_APPLICATION;
}
if( type.trim().equalsIgnoreCase( BlackBerryProject.MIDLET ) ) {
return Project.MIDLET;
}
if( type.trim().equalsIgnoreCase( BlackBerryProject.LIBRARY ) ) {
return Project.LIBRARY;
}
return -1;
}
private String getRAPCPath() {
String rapcPath = IConstants.EMPTY_STRING;
try {
IVMInstall vm = null;
if( _bbProject.getProject().hasNature( BlackBerryProjectCoreNature.NATURE_ID ) ) {
vm = JavaRuntime.getVMInstall( _bbProject );
} else {
// for java proejct, we use the default BB jre
vm = VMUtils.getDefaultBBVM();
}
if( vm != null ) {
File vmLocation = vm.getInstallLocation();
IPath vmPath = new Path( vmLocation.getPath() );
vmPath = vmPath.append( "bin" );
if( OSUtils.isWindows() ) {
vmPath = vmPath.append( "rapc.exe" );
} else {
// Make sure preverify is in executable state
File f = null;
if( ( f = new File( vmPath + File.separator + IConstants.PREVERIFY_FILE_NAME ) ).exists() ) {
if( !f.canExecute() ) {
f.setExecutable( true );
}
}
// invoke rapc.jar instead of rapc.exe
vmPath = vmPath.append( "rapc.jar" );
}
rapcPath = vmPath.toOSString();
} else {
throw ProblemFactory.create_VM_MISSING_exception( _bbProject.getElementName() );
}
} catch( CoreException e ) {
_log.error( "getRapcPath: " + e.getMessage() );
}
return rapcPath;
}
private Vector< String > getCompileOptions() {
Vector< String > options = new Vector< String >();
BlackBerryProperties properties = _bbProject.getProperties();
// TODO java home
// get options from the compile section
if( properties._compile.getCompressResources().booleanValue() ) {
options.add( COMPRESS_RESOURCE_OPTION );
}
if( properties._compile.getConvertImages().booleanValue() ) {
options.add( CONVERT_PNG_RAPC_OPTION );
}
if( !properties._compile.getCreateWarningForNoExportedRoutine().booleanValue()
|| !properties._application.getType().equals( BlackBerryProject.CLDC_APPLICATION ) ) {
options.add( Project.NO_MAIN_RAPC_OPTION );
}
if( !properties._compile.getOutputCompilerMessages().booleanValue() ) {
options.add( Project.QUIET_RAPC_OPTION );
}
if( !StringUtils.isBlank( properties._compile.getAliasList() )
&& properties._application.getType().equals( BlackBerryProject.LIBRARY ) ) {
options.add( Project.ALIAS_RAPC_OPTION + properties._compile.getAliasList() );
}
// add other options
InternalPackagingUtils.addOtherOptions( options, properties );
// add codename
int projectType = getProjectTypeID( properties._application.getType() );
String outputFileName = PackagingUtils.getRelativeStandardOutputFilePath( _bbProject ).toOSString();
if( projectType == Project.LIBRARY ) {
options.addElement( "library=" + outputFileName );
} else {
options.addElement( "codename=" + outputFileName );
}
if( projectType == Project.MIDLET ) {
options.addElement( "-midlet" );
}
return options;
}
/**
* Checks if a jar file is a MidletJar created by rapc.
*
* @param f
* @return
*/
static public int getJarFileType( File f ) {
int type = 0x0;
if( !f.exists() ) {
return type;
}
java.util.jar.JarFile jar = null;
try {
jar = new java.util.jar.JarFile( f, false );
java.util.jar.Manifest manifest = jar.getManifest();
if( manifest != null ) {
java.util.jar.Attributes attributes = manifest.getMainAttributes();
String profile = attributes.getValue( "MicroEdition-Profile" );
if( profile != null ) {
if( "MIDP-1.0".equals( profile ) || "MIDP-2.0".equals( profile ) ) {
type = type | MIDLET_JAR;
}
}
}
Enumeration< JarEntry > entries = jar.entries();
JarEntry entry;
String entryName;
InputStream is = null;
IClassFileReader classFileReader = null;
// check the attribute of the class files in the jar file
for( ; entries.hasMoreElements(); ) {
entry = entries.nextElement();
entryName = entry.getName();
if( entryName.endsWith( IConstants.CLASS_FILE_EXTENSION_WITH_DOT ) ) {
is = jar.getInputStream( entry );
classFileReader = ToolFactory.createDefaultClassFileReader( is, IClassFileReader.ALL );
if( isEvisceratedClass( classFileReader ) ) {
type = type | EVISCERATED_JAR;
break;
}
}
}
} catch( IOException e ) {
_log.error( e.getMessage() );
} finally {
try {
if( jar != null ) {
jar.close();
}
} catch( IOException e ) {
_log.error( e.getMessage() );
}
}
return type;
}
/**
* Verify if the given <code>classFileReader</code> has code attributes.
*
* @param classFileReader
* @return
*/
static private boolean isEvisceratedClass( IClassFileReader classFileReader ) {
// ignore interface classes
if( !classFileReader.isClass() ) {
return false;
}
IMethodInfo[] methodInfos = classFileReader.getMethodInfos();
if( methodInfos == null ) {
return false;
}
for( int i = 0; i < methodInfos.length; i++ ) {
// Ignore <init>, <clinit> and abstract methods
if( "<init>".equalsIgnoreCase( String.valueOf( methodInfos[ i ].getName() ) ) || methodInfos[ i ].isClinit()
|| ( methodInfos[ i ].getAccessFlags() & IModifierConstants.ACC_ABSTRACT ) != 0 ) {
continue;
}
if( methodInfos[ i ].getCodeAttribute() == null ) {
return true;
}
}
return false;
}
public static void reportProblem( IResource resource, int line, int start, int end, String msg, int level ) {
reportProblem( resource, IRIMMarker.PACKAGING_PROBLEM, line, start, end, msg, level );
}
public static void reportProblem( IResource resource, String type, int line, int start, int end, String msg, int level ) {
try {
IMarker m = resource.createMarker( type );
m.setAttribute( IMarker.LINE_NUMBER, line );
m.setAttribute( IMarker.MESSAGE, msg );
m.setAttribute( IMarker.CHAR_START, start );
m.setAttribute( IMarker.CHAR_END, end );
switch( level ) {
case Problem.ERROR:
m.setAttribute( IMarker.PRIORITY, IMarker.PRIORITY_HIGH );
m.setAttribute( IMarker.SEVERITY, IMarker.SEVERITY_ERROR );
break;
case Problem.WARNING:
m.setAttribute( IMarker.PRIORITY, IMarker.PRIORITY_NORMAL );
m.setAttribute( IMarker.SEVERITY, IMarker.SEVERITY_WARNING );
break;
case Problem.INFO:
default:
m.setAttribute( IMarker.PRIORITY, IMarker.PRIORITY_LOW );
m.setAttribute( IMarker.SEVERITY, IMarker.SEVERITY_INFO );
break;
}
} catch( CoreException e ) {
_log.error( e.getMessage(), e );
}
}
private Vector< String > getProtectionOptions( boolean forMakefile ) {
Vector< String > v = new Vector< String >();
Hashtable< String, String > classProtection, packageProtection;
classProtection = _bbProject.getProperties()._hiddenProperties.getClassProtection();
packageProtection = _bbProject.getProperties()._hiddenProperties.getPackageProtection();
Object keys[] = packageProtection.keySet().toArray();
for( int i = 0; i < keys.length; ++i ) {
v.addElement( "package:" + Util.doubleDollar( keys[ i ].toString() ) + "="
+ stripPath( packageProtection.get( keys[ i ] ) ) );
}
keys = classProtection.keySet().toArray();
for( int i = 0; i < keys.length; ++i ) {
if( forMakefile ) {
v.addElement( "class:" + Util.doubleDollar( keys[ i ].toString() ) + "="
+ stripPath( classProtection.get( keys[ i ] ) ) );
} else {
// When we are not creating a makefile don't add double $$ to rapc cmd line
v.addElement( "class:" + keys[ i ].toString() + "=" + stripPath( classProtection.get( keys[ i ] ) ) );
}
}
return v;
}
private String stripPath( Object f ) {
return new File( f.toString() ).getName();
}
private class SourcerootVisitor implements IResourceVisitor {
Set< String > _resourceRootSet;
public SourcerootVisitor() {
_resourceRootSet = new HashSet< String >();
}
public boolean visit( IResource resource ) throws CoreException {
if( !( resource instanceof IFile ) ) {
return shouldBeSourceRoot( resource );
}
String extension = resource.getFileExtension();
if( extension != null ) {
if( extension.equalsIgnoreCase( IConstants.JAVA_EXTENSION ) ) {
String sourceRoot = calculateSourceRoot( resource.getLocation().toFile() );
if( sourceRoot != null ) {
_resourceRootSet.add( sourceRoot );
}
}
}
return false;
}
private String calculateSourceRoot( File javaFile ) {
String packagename = PackageUtils.getJavaFilePackageID( javaFile );
packagename = packagename.replace( IConstants.DOT_CHAR, IConstants.BACK_SLASH_CHAR );
IPath absolutePath = new Path( javaFile.getPath() );
IPath packageAndFileNamPath = null;
if( packagename.trim().equals( IConstants.EMPTY_STRING ) ) {
packageAndFileNamPath = new Path( javaFile.getName() );
} else {
packageAndFileNamPath = new Path( packagename );
packageAndFileNamPath = packageAndFileNamPath.append( new Path( javaFile.getName() ) );
}
int differentSegNumber = absolutePath.segmentCount() - packageAndFileNamPath.segmentCount();
if( differentSegNumber >= 0 ) {
IPath actualPackageAndFileNamPath = absolutePath.removeFirstSegments( differentSegNumber );
if( actualPackageAndFileNamPath.matchingFirstSegments( packageAndFileNamPath ) == packageAndFileNamPath
.segmentCount() ) {
return absolutePath.removeLastSegments( packageAndFileNamPath.segmentCount() ).toOSString();
}
}
_log.debug( "Java file [" + javaFile.getPath() + "] is not in the right package." ); //$NON-NLS-1$ //$NON-NLS-2$
return null;
}
public Vector< String > getSourceRoots( IProject proj ) {
Vector< String > roots = new Vector< String >();
roots.addAll( _resourceRootSet );
try {
if( proj.hasNature( JavaCore.NATURE_ID ) ) {
IJavaProject jproj = JavaCore.create( proj );
IClasspathEntry[] clpentrs = jproj.getResolvedClasspath( true );
IWorkspaceRoot workspaceRoot = proj.getWorkspace().getRoot();
for( IClasspathEntry clpentry : clpentrs ) {
if( ( clpentry.getEntryKind() == IClasspathEntry.CPE_SOURCE )
&& !ImportUtils.getImportPref( ResourceBuilder.LOCALE_INTERFACES_FOLDER_NAME ).equals(
clpentry.getPath().lastSegment() ) ) {
// Try to resolve the source container
IResource resource = workspaceRoot.findMember( clpentry.getPath() );
boolean found = false;
if( resource instanceof IContainer ) {
for( String resRoot : _resourceRootSet ) {
if( resource.getLocation().equals( new Path( resRoot ) ) ) {
found = true;
break;
}
}
if( !found ) {
roots.add( resource.getLocation().toOSString() );
}
}
}
}
}
} catch( CoreException e ) {
_log.error( e );
}
return roots;
}
private boolean shouldBeSourceRoot( IResource resource ) {
if( resource instanceof IProject ) {
return true;
}
if( PackageUtils.isUnderSrcFolder( resource ) ) {
return true;
}
return false;
}
}
/**
* This class is a handler for executing reading an InputStream of a process in a StringBuffer
*
*
*/
static public class InputStreamHandler extends Thread {
private InputStream _stream;
private MessageConsoleStream _consoleStream;
private IProject _project;
/**
* Regular expression to match an error line pattern. These lines are usually in the form file:line:msg
*/
private static final Pattern javaErrorStartPattern = Pattern.compile( ".*\\.java:\\d+:\\s\\S.*" );
private static final Pattern rapcErrorStartPattern = Pattern.compile( "[Ee]rror.*" );
private static final Pattern rapcErrorStartPattern2 = Pattern.compile( ".*[Ee]rror!.*" );
/**
* Regular expression to match a symbol error. This typically looks like symbol : <missing symbol>
*/
private static final Pattern errorSymbolPattern = Pattern.compile( "^symbol\\s*:\\s\\S.*" );
private static final Pattern errorLocationPattern = Pattern.compile( "^location\\s*:\\s\\S.*$" );
private static final Pattern errorSourcePattern = Pattern.compile( "^\\s+\\S.*$" );
private static final Pattern errorSourceLocationPattern = Pattern.compile( "^\\s*\\^\\s*$" );
/**
* Regular expression to match a warning line pattern. These lines are usually in the form file:line: Warning!: msg
*/
private static final Pattern javaWarningStartPattern = Pattern.compile( ".*\\.java:\\d+:\\s[Ww]arning.*" );
private static final Pattern errorLineNumberPattern = Pattern.compile( ":\\d+:" );
private static final Pattern rapcWarningStartPattern = Pattern.compile( "[Ww]arning.*" );
private static final Pattern rapcWarningStartPattern2 = Pattern.compile( ".*[Ww]arning!.*" );
/**
* @param captureBuffer
* @param stream
*/
public InputStreamHandler( IProject project, MessageConsoleStream consoleOutputStream, InputStream inputStream ) {
_stream = inputStream;
_consoleStream = consoleOutputStream;
_project = project;
}
/*
* (non-Javadoc)
*
* @see java.lang.Thread#run()
*/
@Override
public void run() {
try {
/*
* List of queued problems to submit. This is a stack, because it is useful to get the problem just added to add
* additional info to it.
*/
Stack< Problem > problems = new Stack< Problem >();
InputStreamReader isr = new InputStreamReader( _stream );
BufferedReader br = new BufferedReader( isr );
while( true ) {
String line = br.readLine();
if( line == null ) {
break;
}
try {
parse( line, problems );
} catch( RuntimeException e ) {
_log.error( e.getMessage(), e );
}
_consoleStream.println( line );
}
_stream.close();
reportProblems( problems );
} catch( IOException ioe ) {
_log.error( ioe );
}
}
private void reportProblems( Stack< Problem > problems ) {
for( Problem problem : problems ) {
reportProblem( problem );
}
problems.clear();
}
private void reportProblem( Problem problem ) {
_log.trace( "reporting problem " + problem.file + " at " + problem.line + " about " + problem.msg );
IResource resource = null;
if( problem.file != null ) {
IPath problemFileLocation = new Path( problem.file );
resource = ProjectUtils.getResource( _project, problemFileLocation.toFile() );
} else {
resource = _project;
}
if( resource != null ) {
PackagingManager.reportProblem( resource, problem.line, problem.start, problem.end, problem.msg, problem.level );
}
}
/**
* Takes a string and delegates it based on the pattern of it.
*
* @param s
*/
protected void parse( String s, Stack< Problem > problems ) {
if( javaWarningStartPattern.matcher( s ).matches() ) {
parseJavaWarningStart( s, problems );
} else if( rapcWarningStartPattern.matcher( s ).matches() || rapcWarningStartPattern2.matcher( s ).matches() ) {
parseRAPCWarningStart( s, problems );
} else if( javaErrorStartPattern.matcher( s ).matches() ) {
parseJavaErrorString( s, problems );
} else if( rapcErrorStartPattern.matcher( s ).matches() || rapcErrorStartPattern2.matcher( s ).matches() ) {
parseRAPCErrorStart( s, problems );
}
}
/**
* Parses an error generated by RAPC.
*/
private void parseRAPCErrorStart( String s, Stack< Problem > problems ) {
// For example:
// Error!: Error: java compiler failed:
// C:\Java\jdk1.6.0_01\bin\javac.exe -source 1.3
// -target 1.1 -g -O -d C:\DOCUME~1\zqiu\LOCALS~1\Temp\r ...
Matcher m = rapcErrorStartPattern.matcher( s );
if( !m.find() ) {
m = rapcErrorStartPattern2.matcher( s );
if( !m.find() ) {
_log.error( "Failed to parse RAPC error message: " + s );
return;
}
}
Problem problem = new Problem();
problem.msg = s;
problem.level = Problem.ERROR;
problems.add( problem );
}
/**
* Parses an error start. An error start has the file name, line number, and message. Subsequent lines will hold more
* information about the specific error.
*/
private void parseRAPCWarningStart( String s, Stack< Problem > problems ) {
// For example:
// Warning!: Reference to class: net.rim.device.api.io.File requires
// signing
// with key: RIM Runtime API
Matcher m = rapcWarningStartPattern.matcher( s );
if( !m.find() ) {
m = rapcWarningStartPattern2.matcher( s );
if( !m.find() ) {
_log.error( "Failed to parse RAPC warning message: " + s );
return;
}
}
Problem problem = new Problem();
problem.msg = s;
problem.level = Problem.WARNING;
problems.add( problem );
}
/**
* Parses an error start. An error start has the file name, line number, and message. Subsequent lines will hold more
* information about the specific error.
*/
private void parseJavaWarningStart( String s, Stack< Problem > problems ) {
// For example:
// C:\samples\com\rim\samples\device\tictactoe\TTTService.java:10:
// package net.rim.blackberry.api.blackberrymessenger does not exist
int firstColon, secondColon;
Matcher m = errorLineNumberPattern.matcher( s );
if( m.find() ) {
firstColon = m.start();
secondColon = m.end() - 1;
} else {
_log.warn( "Faling back to old way of doing things..." );
secondColon = s.lastIndexOf( ':' );
firstColon = s.lastIndexOf( ':', secondColon - 1 );
}
Problem problem = new Problem();
problem.file = s.substring( 0, firstColon ).trim();
problem.line = Integer.parseInt( s.substring( firstColon + 1, secondColon ) );
problem.msg = s.substring( secondColon + 1 ).trim();
problem.level = Problem.WARNING;
problems.add( problem );
}
/* Keep track of state for parse() automaton */
private int parseState;
/**
* Takes a string and delegates it based on the pattern of it.
*
* @param s
*/
private void parseJavaErrorString( String s, Stack< Problem > problems ) {
_log.trace( "Parsing " + s );
if( javaErrorStartPattern.matcher( s ).matches() ) {
// Some warnings appear on stderr for some reason...
// For example:
// C:\samples\com\rim\samples\device\syncdemo\SyncDemo.java:288:
// warning: [deprecation] getScreenWidth() in
// net.rim.device.api.ui.Graphics has been deprecated
if( javaWarningStartPattern.matcher( s ).matches() ) {
parseJavaWarningStart( s, problems );
} else {
parseErrorStart( s, problems );
}
parseState = 0;
} else if( ( parseState == 0 ) && errorSymbolPattern.matcher( s ).matches() ) {
parseErrorSymbol( s, problems );
parseState = 1;
} else if( ( parseState == 1 ) && errorLocationPattern.matcher( s ).matches() ) {
// Optional "location:*" line. Only occurs for stuff in inner
// classes or methods.
} else if( ( parseState == 1 ) && errorSourcePattern.matcher( s ).matches() ) {
parseErrorSource( s, problems );
parseState = 2;
} else if( ( parseState == 2 ) && errorSourceLocationPattern.matcher( s ).matches() ) {
parseErrorLocation( s, problems );
parseState = 3;
}
}
/**
* Parses an error start. An error start has the file name, line number, and message. Subsequent lines will hold more
* information about the specific error.
*/
private void parseErrorStart( String s, Stack< Problem > problems ) {
// For example:
// C:\samples\com\rim\samples\device\tictactoe\TTTService.java:10:
// package net.rim.blackberry.api.blackberrymessenger does not exist
int firstColon, secondColon;
Matcher m = errorLineNumberPattern.matcher( s );
if( m.find() ) {
firstColon = m.start();
secondColon = m.end() - 1;
} else {
_log.warn( "Faling back to old way of doing things..." );
secondColon = s.lastIndexOf( ':' );
firstColon = s.lastIndexOf( ':', secondColon - 1 );
}
Problem problem = new Problem();
problem.file = s.substring( 0, firstColon ).trim();
problem.line = Integer.parseInt( s.substring( firstColon + 1, secondColon ) );
problem.msg = s.substring( secondColon + 1 ).trim();
problem.level = Problem.ERROR;
problems.add( problem );
}
/**
* This parses the line associated with a "cannot find symbol" error. Following such an error, rapc will output the undef
* symbol.
*
* @param s
*/
private void parseErrorSymbol( String s, Stack< Problem > problems ) {
// For example:
// C:\samples\com\rim\samples\device\tictactoe\TTTRequestListener.java
// :28: cannot find symbol
// symbol : class Session
try {
int colon = s.indexOf( ":" );
Problem problem = problems.peek();
if( ( problem.msg != null ) && problem.msg.equals( "cannot find symbol" ) ) {
problem.msg += " " + s.substring( colon ).trim();
}
} catch( EmptyStackException e ) {
_log.error( e.getMessage(), e );
}
}
private void parseErrorLocation( String s, Stack< Problem > problems ) {
try {
Problem problem = problems.peek();
if( problem != null ) {
problem.start = s.indexOf( '^' );
String source = problem.source;
int sourceLength = source.length();
for( int i = problem.start; i < sourceLength; i++ ) {
char c = source.charAt( i );
// All valid characters that can go into java
// identifiers.
if( ( ( c > 96 ) && ( c < 123 ) ) || ( ( c > 64 ) && ( c < 91 ) ) || ( ( c > 47 ) && ( c < 58 ) )
|| ( c == 36 ) || ( c == 95 ) ) {
// Do Nothing
} else {
problem.end = i + 1;
break;
}
}
}
} catch( EmptyStackException e ) {
_log.error( e.getMessage(), e );
}
}
private void parseErrorSource( String s, Stack< Problem > problems ) {
try {
Problem problem = problems.peek();
if( problem != null ) {
problem.source = s;
}
} catch( EmptyStackException e ) {
_log.error( e.getMessage(), e );
}
}
}
/**
* Problem declaration
*/
static public class Problem {
public static final int ERROR = 1;
public static final int WARNING = 2;
public static final int INFO = 4;
String file;
int line;
String msg;
int level;
String source;
int start = -1;
int end = -1;
@Override
public String toString() {
return "Problem[file=" + file + ";line=" + line + ";msg=" + msg + ";level=" + level + ";]";
}
}
/**
* ImportedJar class is used to differentiate if a jar is exported or not.
*
*
*/
public static class ImportedJar {
String _path;
boolean _exported;
int _type;
public ImportedJar( String path, boolean exported, int jarType ) {
_path = path;
_exported = exported;
_type = jarType;
}
public boolean isExported() {
return _exported;
}
public void setExported( boolean exported ) {
_exported = exported;
}
public String getPath() {
return _path;
}
public void setPath( String path ) {
_path = path;
}
public int getType() {
return _type;
}
public void setType( int type ) {
_type = type;
}
public String toString() {
return _path;
}
public boolean isMidletJar() {
return ( _type & MIDLET_JAR ) > 0;
}
}
}