/*
* 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.builders;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.rim.ejde.internal.core.IRIMMarker;
import net.rim.ejde.internal.packaging.PackagingJob;
import net.rim.ejde.internal.signing.BBSigningKeys;
import net.rim.ejde.internal.util.NatureUtils;
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.ResourceBuilderUtils;
import net.rim.ejde.internal.util.VMUtils;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.compiler.BuildContext;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CompilationParticipant;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTRequestor;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
/**
* The BBCompilationParticipant updates the compilation process for BB projects.
*/
public class BBCompilationParticipant extends CompilationParticipant {
/**
* The Class BBASTRequestor.
*/
public class BBASTRequestor extends ASTRequestor {
/** The _protected classes. */
private List< String > _protectedClasses;
/** The _signing keys. */
private BBSigningKeys _signingKeys;
/** The _build context map. */
private Map< ICompilationUnit, BuildContext > _buildContextMap;
/**
* Instantiates a new bBAST requestor.
*
* @param buildContextMap
* the build context map
* @param signingKeys
* the signing keys
* @param protectedClasses
* the protected classes
*/
public BBASTRequestor( Map< ICompilationUnit, BuildContext > buildContextMap, BBSigningKeys signingKeys,
List< String > protectedClasses ) {
super();
_signingKeys = signingKeys;
_protectedClasses = protectedClasses;
_buildContextMap = buildContextMap;
}
/**
* Gets the keys for all of the types that need to be resolved. If we ever need to add hidden methods we may need to add
* these to the keys here as well.
*
* @return the keys
*/
public String[] getKeys() {
String[] keys = new String[ _protectedClasses.size() ];
for( int i = 0; i < _protectedClasses.size(); i++ ) {
keys[ i ] = "L" + _protectedClasses.get( i ).replaceAll( "[.]", "/" );
}
return keys;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTRequestor#acceptAST(org.eclipse.jdt.core.ICompilationUnit,
* org.eclipse.jdt.core.dom.CompilationUnit)
*/
@Override
public void acceptAST( ICompilationUnit source, CompilationUnit ast ) {
BBASTVisitor visitor = new BBASTVisitor( source, ast, _signingKeys, _protectedClasses );
ast.accept( visitor );
_buildContextMap.get( source ).recordNewProblems( visitor.getProblems() );
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTRequestor#acceptBinding(java.lang.String, org.eclipse.jdt.core.dom.IBinding)
*/
@Override
public void acceptBinding( String bindingKey, IBinding binding ) {
// TODO Auto-generated method stub
super.acceptBinding( bindingKey, binding );
}
}
/**
* The Class BBASTVisitor.
*/
public class BBASTVisitor extends ASTVisitor {
/** The _comp unit. */
private CompilationUnit _compUnit;
private ICompilationUnit _cu;
/** The _created problems. */
private List< CategorizedProblem > _createdProblems;
/** The _protected classes. */
private List< String > _protectedClasses;
/** The _protected methods. */
// private List<String> _protectedMethods;
/** The _res. */
private IResource _res;
/** The _signing keys. */
private BBSigningKeys _signingKeys;
/**
* Instantiates a new bBAST visitor.
*
* @param res
* the res
* @param compUnit
* the compilation unit
* @param signingKeys
* the signing keys
* @param protectedClasses
* the protected classes
*/
public BBASTVisitor( ICompilationUnit cu, CompilationUnit compUnit, BBSigningKeys signingKeys,
List< String > protectedClasses ) {
super( false );
_cu = cu;
_res = _cu.getResource();
_compUnit = compUnit;
_createdProblems = new ArrayList< CategorizedProblem >();
_signingKeys = signingKeys;
_protectedClasses = protectedClasses;
// _protectedMethods = Arrays.asList( _signingKeys.getProtectedMethods());
}
/**
* Creates the problem.
*
* @param node
* the node
* @param className
* the class name
* @param msg
* the msg
*
* @return the categorized problem
*/
private CategorizedProblem createProblem( ASTNode node, String className, String msg ) {
return createProblem( node, className, msg, null, null );
}
/**
* Creates the problem.
*
* @param node
* the node
* @param className
* the class name
* @param msg
* the msg
*
* @return the categorized problem
*/
private CategorizedProblem createProblem( ASTNode node, String className, String msg, String[] extraAttributeNames,
Object[] extraAttributeValues ) {
int startPos = node.getStartPosition(), lineNumber = _compUnit.getLineNumber( startPos ), endPos = startPos
+ node.getLength() - 1;
Integer key = _signingKeys.getKey( className );
String msgPrefix = "Signing Required: " + VMUtils.convertKeyToPreferenceLabel( key, _signingKeys ) + ": ";
return new BBCategorizedProblem( msgPrefix + msg, _res.getName().toCharArray(), startPos, endPos, lineNumber, key,
extraAttributeNames, extraAttributeValues );
}
/**
* Gets the problems.
*
* @return the problems
*/
public CategorizedProblem[] getProblems() {
return _createdProblems.toArray( new CategorizedProblem[ _createdProblems.size() ] );
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ImportDeclaration)
*/
@Override
public boolean visit( ImportDeclaration node ) {
// do nothing for the import part
return false;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.QualifiedName)
*/
@Override
public boolean visit( QualifiedName node ) {
Name name = node.getQualifier();
if( name != null ) {
ITypeBinding typeBinding = name.resolveTypeBinding();
if( typeBinding != null ) {
String className = typeBinding.getQualifiedName();
if( _protectedClasses.contains( className ) ) {
boolean hasCompileTimeValue = node.resolveConstantExpressionValue() != null;
// Fields with compileTime values will have their linking removed by the compiler so will not register a
// code signing warning UNLESS they are set to autoStartup.
CompilerToAppDescriptorManager.getInstance().onQualifiedNameFieldUsage(
createProblem( node, className, "Protected Class " + className, new String[] { IRIMMarker.DATA },
new Object[] { Boolean.valueOf( hasCompileTimeValue ) } ), _cu, hasCompileTimeValue );
return false;
}
}
}
return true;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SimpleName)
*/
@Override
public boolean visit( SimpleName node ) {
IBinding binding = node.resolveBinding();
if( binding != null ) {
if( binding instanceof ITypeBinding ) {
ITypeBinding typeBinding = (ITypeBinding) binding;
String className = typeBinding.getQualifiedName();
if( className != null ) {
if( _protectedClasses.contains( className ) ) {
_createdProblems.add( createProblem( node, className, "Protected Class " + className ) );
}
}
}
}
return true;
}
/**
* This method could be left out and the cases would be caught by SimpleName, however then we would not highlight the
* whole element. Additionally this will allow us to add back in hiddenMethods if this is ever a requirement.
*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodInvocation)
*/
@Override
public boolean visit( MethodInvocation node ) {
IMethodBinding methodBinding = node.resolveMethodBinding();
if( methodBinding != null ) {
ITypeBinding typeBinding = methodBinding.getDeclaringClass();
if( typeBinding != null ) {
String className = typeBinding.getQualifiedName();
if( className != null ) {
if( _protectedClasses.contains( className ) ) {
_createdProblems.add( createProblem( node, className, "Protected Class " + className ) );
return false;
}
/**
* This should be the logic necessary to check for hidden methods but these do not seem to be required by
* the signing tool.
*/
// String methodName = className+"."+methodBinding.getName()+"(";
// for(ITypeBinding methodParameterTypeBinding : methodBinding.getParameterTypes()){
// methodName += methodParameterTypeBinding.getKey();
// }
// methodName += ")"+methodBinding.getReturnType().getKey();
//
// if (_protectedMethods.contains(methodName) ){
// _createdProblems.add( createProblem( node, className, "Protected Method "+methodName ) );
// return false;
// }
}
}
}
return true;
}
}
/**
* The Class BBCategorizedProblem.
*/
class BBCategorizedProblem extends CategorizedProblem {
/** The _msg. */
private String _msg;
/** The _orig file name. */
private char[] _origFileName;
/** The _line num. */
private int _startPos, _endPos, _lineNum;
private String[] _extraAttributeNames;
private Object[] _extraAttributeValues;
/**
* Instantiates a new bB categorized problem.
*
* @param msg
* the msg
* @param origFileName
* the orig file name
* @param startPos
* the start pos
* @param endPos
* the end pos
* @param lineNum
* the line num
* @param keyID
* the key id
*/
public BBCategorizedProblem( String msg, char[] origFileName, int startPos, int endPos, int lineNum, Integer keyID ) {
this( msg, origFileName, startPos, endPos, lineNum, keyID, null, null );
}
/**
* Instantiates a new bB categorized problem.
*
* @param msg
* the msg
* @param origFileName
* the orig file name
* @param startPos
* the start pos
* @param endPos
* the end pos
* @param lineNum
* the line num
* @param keyID
* the key id
*/
public BBCategorizedProblem( String msg, char[] origFileName, int startPos, int endPos, int lineNum, Integer keyID,
String[] extraAttributeNames, Object[] extraAttributeValues ) {
super();
_msg = msg;
_origFileName = origFileName;
_startPos = startPos;
_endPos = endPos;
_lineNum = lineNum;
if( extraAttributeNames != null ) {
int length = extraAttributeNames.length;
_extraAttributeNames = new String[ 1 + length ];
for( int i = 0; i < length; i++ ) {
_extraAttributeNames[ i ] = extraAttributeNames[ i ];
}
_extraAttributeNames[ length ] = IRIMMarker.KEY;
} else {
_extraAttributeNames = new String[] { IRIMMarker.KEY };
}
if( extraAttributeValues != null ) {
int length = extraAttributeValues.length;
_extraAttributeValues = new Object[ 1 + length ];
for( int i = 0; i < length; i++ ) {
_extraAttributeValues[ i ] = extraAttributeValues[ i ];
}
_extraAttributeValues[ length ] = keyID;
} else {
_extraAttributeValues = new Object[] { keyID };
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.IProblem#getArguments()
*/
@Override
public String[] getArguments() {
// We have no arguments currently
return new String[ 0 ];
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.CategorizedProblem#getCategoryID()
*/
@Override
public int getCategoryID() {
return IRIMMarker.CODE_SIGN_CATEGORY_ID;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.CategorizedProblem#getExtraMarkerAttributeNames()
*/
@Override
public String[] getExtraMarkerAttributeNames() {
return _extraAttributeNames;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.CategorizedProblem#getExtraMarkerAttributeValues()
*/
@Override
public Object[] getExtraMarkerAttributeValues() {
return _extraAttributeValues;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.IProblem#getID()
*/
@Override
public int getID() {
return IRIMMarker.CODE_SIGN_PROBLEM_ID;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.CategorizedProblem#getMarkerType()
*/
@Override
public String getMarkerType() {
return IRIMMarker.CODE_SIGN_PROBLEM_MARKER;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.IProblem#getMessage()
*/
@Override
public String getMessage() {
return _msg;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.IProblem#getOriginatingFileName()
*/
@Override
public char[] getOriginatingFileName() {
return _origFileName;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.IProblem#getSourceEnd()
*/
@Override
public int getSourceEnd() {
return _endPos;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.IProblem#getSourceLineNumber()
*/
@Override
public int getSourceLineNumber() {
return _lineNum;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.IProblem#getSourceStart()
*/
@Override
public int getSourceStart() {
return _startPos;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.IProblem#isError()
*/
@Override
public boolean isError() {
return false;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.IProblem#isWarning()
*/
@Override
public boolean isWarning() {
return true;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.IProblem#setSourceEnd(int)
*/
@Override
public void setSourceEnd( int sourceEnd ) {
_endPos = sourceEnd;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.IProblem#setSourceLineNumber(int)
*/
@Override
public void setSourceLineNumber( int lineNumber ) {
_lineNum = lineNumber;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.IProblem#setSourceStart(int)
*/
@Override
public void setSourceStart( int sourceStart ) {
_startPos = sourceStart;
}
}
/** The Constant _log. */
private static final Logger _log = Logger.getLogger( BBCompilationParticipant.class );
// private static long _totalTimeParsing = 0;
/** The _current project. */
private IJavaProject _currentProject;
/**
* Instantiates a new BB compilation participant.
*/
public BBCompilationParticipant() {
super();
_currentProject = null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.CompilationParticipant#aboutToBuild(org.eclipse.jdt.core.IJavaProject)
*/
@Override
public int aboutToBuild( IJavaProject project ) {
_currentProject = project;
return READY_FOR_BUILD;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.CompilationParticipant#buildFinished(org.eclipse.jdt.core.IJavaProject)
*/
@Override
public void buildFinished( IJavaProject project ) {
// check run on startup property
CompilerToAppDescriptorManager.onProjectPropertiesChange( project );
// _buildStop = System.currentTimeMillis();
// _log.debug( "*********************************** BUILD STOPPING ***********************************" );
// _totalBuiltTime += ( _buildStop - _buildStart );
// _totalBuiltTimeWClean += ( _buildStop - _cleanStart );
// _log.debug( "Total build time: " + _totalBuiltTime );
// _log.debug( "Time lost this session: " + _builtTimeLost );
// double result = ( _builtTimeLost / ( _totalBuiltTime * 1.0 ) ) * 100;
// _log.debug( "Percentage of Build Time Lost: " + result );
// _log.debug( "*********************************** LOST TIME STATS *********************************" );
// _log.debug( "Time time lost: " + _totalTimeLost );
// _log.debug( "Total time finding Java Element: " + _totalTimeFindingJE );
// result = ( _totalTimeFindingJE / ( _totalTimeLost * 1.0 ) ) * 100;
// _log.debug( "Total time Parsing: " + _totalTimeParsing );
// result = ( _totalTimeParsing / ( _totalTimeLost * 1.0 ) ) * 100;
// _log.debug( "Percentage of Lost Time Parsing: " + result );
// _log.debug( "Total time Visiting: " + _totalTimeVisiting );
// result = ( _totalTimeVisiting / ( _totalTimeLost * 1.0 ) ) * 100;
// _log.debug( "Percentage of Lost Time Visiting: " + result );
// _log.debug( "*********************************** OVERALL STATS ***********************************" );
// _log.debug( "Total build time: " + _totalBuiltTime );
// _log.debug( "Total build time from clean: " + _totalBuiltTimeWClean );
// _log.debug( "Time time lost: " + _totalTimeLost );
// result = ( _totalTimeLost / ( _totalBuiltTime * 1.0 ) ) * 100;
// _log.debug( "Percentage of Build Time Lost: " + result );
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.CompilationParticipant#buildStarting(org.eclipse.jdt.core.compiler.BuildContext[],
* boolean)
*/
@Override
public void buildStarting( BuildContext[] files, boolean isBatch ) {
// _log.debug( "*********************************** BUILD STARTING ***********************************" );
// 5 files determined experimentally to be about the difference to make batch processing worth it.
boolean runBatch = isBatch && ( files.length > 5 );
// _log.trace( "Entering BBCompilationParticipant buildStarting();Running As Batch=" + runBatch ); //$NON-NLS-1$
// long start = System.currentTimeMillis();
if( files.length > 0 ) {
// mark this project is built by the java builder
IProject project = files[ 0 ].getFile().getProject();
// remove the packaging problems
try {
ResourceBuilderUtils.cleanProblemMarkers( project, new String[] { IRIMMarker.SIGNATURE_TOOL_PROBLEM_MARKER,
IRIMMarker.PACKAGING_PROBLEM }, IResource.DEPTH_INFINITE );
} catch( CoreException e ) {
_log.error( e.getMessage() );
}
PackagingJob.setBuiltByJavaBuilders( project, true );
try {
IJavaElement javaElem;
Map< ICompilationUnit, BuildContext > buildContextMap = new HashMap< ICompilationUnit, BuildContext >();
IVMInstall vm = JavaRuntime.getVMInstall( _currentProject );
if( ( vm == null ) || !VMUtils.isBlackBerryVM( vm ) ) {
throw ProblemFactory.create_VM_MISSING_exception( _currentProject.getElementName() );
}
BBSigningKeys signingKeys = null;
List< String > protectedClasses = null;
boolean needParse = false;
// No need to parse when there are no protected classes
for( BuildContext file : files ) {
if( !NatureUtils.hasBBNature( file.getFile().getProject() ) ) {
continue;
}
// we only initialize these variables if there is any file in a BB project
if( ( signingKeys == null ) || ( protectedClasses == null ) ) {
signingKeys = VMUtils.addSignKeysToCache( vm );
protectedClasses = VMUtils.getHiddenClassesFilteredByPreferences( vm.getName() );
needParse = protectedClasses.size() > 0;
}
IPath srcFilePath = file.getFile().getProjectRelativePath();
// For some odd reason we must remove the original pkg name or it won't be found.
if( srcFilePath.segmentCount() > 1 ) {
srcFilePath = srcFilePath.removeFirstSegments( 1 );
}
javaElem = _currentProject.findElement( srcFilePath );
if( ( javaElem != null ) && ( javaElem instanceof ICompilationUnit ) ) {
ICompilationUnit cu = (ICompilationUnit) javaElem;
CompilerToAppDescriptorManager.getInstance().onCompilationUnitCompile( cu );
if( needParse ) {
if( runBatch ) {
buildContextMap.put( cu, file );
} else {
ASTParser parser = ASTParser.newParser( AST.JLS3 );
parser.setResolveBindings( true );
parser.setSource( cu );
parser.setProject( _currentProject );
CompilationUnit astRoot = (CompilationUnit) parser.createAST( new NullProgressMonitor() );
BBASTVisitor visitor = new BBASTVisitor( cu, astRoot, signingKeys, protectedClasses );
astRoot.accept( visitor );
file.recordNewProblems( visitor.getProblems() );
}
}
} else {
// Will be the case for Resources
// _log.error( "buildStarting: Error retrieving source to Parse for file " + file.getFile().getName()
// );
}
}
if( needParse && runBatch ) {
ASTParser parser = ASTParser.newParser( AST.JLS3 );
parser.setResolveBindings( true );
parser.setProject( _currentProject );
BBASTRequestor astRequestor = new BBASTRequestor( buildContextMap, signingKeys, protectedClasses );
parser.createASTs( buildContextMap.keySet().toArray( new ICompilationUnit[ buildContextMap.size() ] ),
astRequestor.getKeys(), astRequestor, new NullProgressMonitor() );
}
} catch( JavaModelException jme ) {
_log.error( "buildStarting: Error Parsing for Access Restrictions", jme );
} catch( CoreException ce ) {
_log.error( "buildStarting: " + ce.getMessage() );
}
// long stop = System.currentTimeMillis();
// long result = stop - start;
// _totalTimeParsing += result;
// _log.debug( _currentProject.getElementName() + " \t " + result + " \t Total \t " + _totalTimeParsing );
// _log.debug( "*******************************************************************************" );
// _log.trace( "Leaving BBCompilationParticipant buildStarting()" ); //$NON-NLS-1$
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.CompilationParticipant#cleanStarting(org.eclipse.jdt.core.IJavaProject)
*/
@Override
public void cleanStarting( IJavaProject project ) {
// _log.debug( "*********************************** CLEAN STARTING ***********************************" );
// _cleanStart = System.currentTimeMillis();
try {
PackagingUtils.cleanProjectOutputFolder( project );
CompilerToAppDescriptorManager.getInstance().onProjectClean( project );
// remove the packaging problems
try {
ResourceBuilderUtils.cleanProblemMarkers( project.getProject(), new String[] {
IRIMMarker.SIGNATURE_TOOL_PROBLEM_MARKER, IRIMMarker.PACKAGING_PROBLEM }, IResource.DEPTH_INFINITE );
} catch( CoreException e ) {
_log.error( e.getMessage() );
}
} catch( CoreException e ) {
_log.error( e );
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.compiler.CompilationParticipant#isActive(org.eclipse.jdt.core.IJavaProject)
*/
@Override
public boolean isActive( IJavaProject project ) {
if( NatureUtils.hasBBNature( project.getProject() ) || ProjectUtils.isDependedByBBProject( project.getProject() ) ) {
return true;
}
return false;
}
}