/*
* 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.validation;
import java.io.File;
import java.util.Map;
import java.util.Set;
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.model.BasicBlackBerryProperties.AlternateEntryPoint;
import net.rim.ejde.internal.model.BasicBlackBerryProperties.Icon;
import net.rim.ejde.internal.model.BlackBerryProject;
import net.rim.ejde.internal.model.BlackBerryProperties;
import net.rim.ejde.internal.util.Messages;
import net.rim.ejde.internal.util.NatureUtils;
import net.rim.ejde.internal.util.PackagingUtils;
import net.rim.ejde.internal.util.ProjectUtils;
import net.rim.ejde.internal.util.ResourceBuilderUtils;
import net.rim.ejde.internal.util.ProjectUtils.RRHFile;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
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.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaProject;
public class BBPropertiesValidator implements IBBComponentValidator {
private static final Logger _log = Logger.getLogger( BBPropertiesValidator.class );
public static final String VERSION_STRING_SEPARATOR = "."; //$NON-NLS-1$
public static final String VERSION_STRING_USER_VISIBLE = "#.#.#"; //$NON-NLS-1$
public static final char[] CHARS_WARN = new char[] { ' ', ',', '.', ';', ':', '\'', '"', '!', '@', '#', '$', '%', '^', '&',
'*', '|', '?', '\\', '(', ')', '+', '{', '}', '[', ']', '<', '>' };
/**
* Validates BlackBerry project properties
*
* @param validateThis
* - BlackBerryProperties to validate
* @return diagnostic result of validation
*
* @see net.rim.ejde.internal.validation.IBBComponentValidator#validate(Object)
*/
public BBDiagnostic validate( Object validateThis ) {
IProject project = (IProject) ( (IFile) validateThis ).getParent();
BlackBerryProperties properties = ContextManager.PLUGIN.getBBProperties( project.getName(), false );
BBDiagnostic combinedDiagnostics = AbstractDiagnosticFactory.createChainedDiagnostic();
BBDiagnostic systemModuleDiag = validateSystemModel( properties._application.isSystemModule(),
properties._application.getType() );
BBDiagnostic homeScreenDiag = validateHomeScreenPosition( properties._application.getHomeScreenPosition().toString() );
BBDiagnostic iconDiag = AbstractDiagnosticFactory.createChainedDiagnostic();
for( Icon icon : properties._resources.getIconFiles() ) {
iconDiag.merge( validateIconExists( project, icon ) );
}
BBDiagnostic fileNameDiag = validateHasValidOutputFileName( properties._packaging.getOutputFileName(), project );
BBDiagnostic folderDiag = validateHasValidFolderName( properties._packaging.getOutputFolder(), project );
BBDiagnostic alxDiag = AbstractDiagnosticFactory.createChainedDiagnostic();
for( String file : properties._packaging.getAlxFiles() ) {
alxDiag.merge( validateFileExists( project, new File( file ), false ) );
}
Map< String, RRHFile > resourceMap = ProjectUtils.getProjectResources( new BlackBerryProject( JavaCore.create( project ),
properties ) );
BBDiagnostic aepDiag = AbstractDiagnosticFactory.createChainedDiagnostic();
for( AlternateEntryPoint aep : properties.getAlternateEntryPoints() ) {
aepDiag.merge( validateHomeScreenPosition( aep.getHomeScreenPosition().toString() ) );
for( Icon icon : aep.getIconFiles() ) {
aepDiag.merge( validateIconExists( project, icon ) );
}
if( aep.getHasTitleResource() ) {
aepDiag.merge( validateResourceInfo( project, resourceMap, aep.getTitleResourceBundleClassName() ) );
aepDiag.merge( validateResourceKey( resourceMap.get( aep.getTitleResourceBundleClassName() ),
aep.getTitleResourceBundleKey() ) );
}
}
BBDiagnostic resourceDiag = AbstractDiagnosticFactory.createChainedDiagnostic();
if( properties._resources.hasTitleResource() ) {
resourceDiag.merge( validateResourceInfo( project, resourceMap,
properties._resources.getTitleResourceBundleClassName() ) );
resourceDiag.merge( validateResourceKey( resourceMap.get( properties._resources.getTitleResourceBundleClassName() ),
properties._resources.getTitleResourceBundleKey() ) );
}
combinedDiagnostics.merge( systemModuleDiag );
combinedDiagnostics.merge( homeScreenDiag );
combinedDiagnostics.merge( iconDiag );
combinedDiagnostics.merge( fileNameDiag );
combinedDiagnostics.merge( folderDiag );
combinedDiagnostics.merge( alxDiag );
combinedDiagnostics.merge( aepDiag );
combinedDiagnostics.merge( resourceDiag );
return combinedDiagnostics;
}
static public BBDiagnostic validateResourceInfo( IProject project, Map< String, RRHFile > resourceMap, String className ) {
BBDiagnostic resourceDiag = AbstractDiagnosticFactory.createChainedDiagnostic();
RRHFile rrhFile = resourceMap.get( className );
if( rrhFile == null ) {
resourceDiag.merge( DiagnosticFactory.create_RESOURCE_MISSING( className ) );
}
return resourceDiag;
}
static public BBDiagnostic validateResourceKey( RRHFile rrhFile, String keyName ) {
BBDiagnostic resourceDiag = AbstractDiagnosticFactory.createChainedDiagnostic();
if( rrhFile == null && !StringUtils.isBlank( keyName ) ) {
resourceDiag.merge( DiagnosticFactory.create_RESOURCE_KEY_INVALID( keyName ) );
} else if( !StringUtils.isBlank( keyName ) && rrhFile.getKeyTalbe().get( keyName ) == null ) {
resourceDiag.merge( DiagnosticFactory.create_RESOURCE_KEY_INVALID( keyName ) );
}
return resourceDiag;
}
/**
* Validates home screen position
*
* @param position
* @return
*/
public static BBDiagnostic validateHomeScreenPosition( String position ) {
if( !isParsableInt( position ) ) {
return DiagnosticFactory.create_HOME_SCREEN_POSITION_INVALID();
}
return AbstractDiagnosticFactory.getOK();
}
/**
* Validates whether the given icon exists within the project
*
* @param project
* @param icon
* @return
*/
public static BBDiagnostic validateIconExists( IProject project, Icon icon ) {
// TODO: See whether we can always construct the icon with forward slash.
if( !fileExists( project, new File( icon.getCanonicalFileName().replace( '\\', '/' ) ) ) ) {
return DiagnosticFactory.create_ICON_MISSING( new Path( icon.getCanonicalFileName() ).lastSegment() );
}
return AbstractDiagnosticFactory.getOK();
}
/**
* Validates if the given file exists in the given project or its dependent projects.
*
* @param project
* The project The main project
* @param file
* The file to be checked
* @param checkDependentProjects
* If dependent projects should be checked
*
* @return <code>BBDiagnostic</code>
*/
public static BBDiagnostic validateFileExists( IProject project, File file, boolean checkDependentProjects ) {
boolean found = false;
if( fileExists( project, file ) ) {
found = true;
} else if( checkDependentProjects ) {
// check dependent projects
try {
Set< IProject > projects = ProjectUtils.getAllReferencedProjects( project );
for( IProject proj : projects ) {
if( fileExists( proj, file ) ) {
found = true;
break;
}
}
} catch( CoreException e ) {
_log.error( e );
}
}
if( !found ) {
return DiagnosticFactory.create_FILE_MISSING( file.getName() );
}
return AbstractDiagnosticFactory.getOK();
}
private static Boolean fileExists( IProject project, File file ) {
IPath iconPath = new Path( file.getPath() );
if( iconPath.isAbsolute() ) {
if( !iconPath.toFile().exists() ) {
return false;
}
} else {
if( file.getPath().startsWith( ".." ) ) { //$NON-NLS-1$
// Do an external file check
File externalFile = project.getLocation().append( iconPath ).toFile();
if( externalFile != null && !externalFile.exists() ) {
return false;
}
} else {
// Do a local proj file check
IFile iFile = project.getFile( iconPath );
if( iFile == null || !iFile.exists() || iFile.isLinked() && !iFile.getLocation().toFile().exists() ) {
return false;
}
}
}
return true;
}
/**
* Validates whether the string has a value
*
* @param value
* @return
*/
public static BBDiagnostic validateHasValue( String value ) {
if( StringUtils.isEmpty( value ) ) {
return DiagnosticFactory.create_VALUE_REQUIRED();
}
return AbstractDiagnosticFactory.getOK();
}
/**
* Validates the given file name for illegal characters.
*
* @param value
* @return
*/
public static BBDiagnostic validateHasValidOutputFileName( String value, IProject proj ) {
if( StringUtils.isEmpty( value ) ) {
return DiagnosticFactory.create_VALUE_REQUIRED();
}
// Output file name must be a valid Java identifier
IStatus status = JavaConventions.validateJavaTypeName( value, JavaCore.VERSION_1_3, JavaCore.VERSION_1_3 );
if( status.getSeverity() == IStatus.ERROR ) {
return DiagnosticFactory.create_INVALID_OUTPUT_PATH_CHAR();
}
// Also, this value should not overlap with dependency projects outputFileName
if(!isValidNameWithDependencies(value, proj)) {
return DiagnosticFactory.create_OutputFN_MUST_DIFFER(value);
}
return AbstractDiagnosticFactory.getOK();
}
private static boolean isValidNameWithDependencies(String value, IProject proj) {
try {
IProject[] refs = proj.getReferencedProjects();
for (IProject ref : refs) {
if (NatureUtils.hasBBNature(ref)) {
if(PackagingUtils.replaceSpecialChars(ref.getName()).equals(value)
|| !isValidNameWithDependencies(value, ref)) {
return false;
}
}
}
} catch (CoreException e) {
_log.error(e.getMessage());
}
return true;
}
public static BBDiagnostic validateSystemModel( boolean systemModel, String projectType ) {
if( projectType.equals( BlackBerryProject.LIBRARY ) && !systemModel ) {
return DiagnosticFactory.create_SYSTEM_MODULE_PROBLEMATIC();
}
return AbstractDiagnosticFactory.getOK();
}
/**
* Validates the given folder name for illegal characters in context & basic URI syntax
*
* @param value
* @return
*/
public static BBDiagnostic validateHasValidFolderName( String value, IProject project ) {
// general project context validation (catches some issues...)
IPath ppath = project.getLocation();
if( !ppath.isValidPath( value ) ) {
return DiagnosticFactory.createDiagnostic( BBDiagnostic.ERROR, 0, Messages.BBPropertiesValidator_PATH_INVALID_ERROR );
}
IWorkspace workspace = project.getWorkspace();
IPath p = new Path( value );
if( p.isAbsolute() || value.indexOf( ':' ) >= 0 ) {
return DiagnosticFactory.createDiagnostic( BBDiagnostic.ERROR, 0,
Messages.BBPropertiesValidator_PATH_NOT_RELATIVE_ERROR );
}
// check more thoroughly the validity of each segment
for( String seg : p.segments() ) {
if( IConstants.DOUBLE_DOTS.equals( seg ) ) {
return DiagnosticFactory.createDiagnostic( BBDiagnostic.ERROR, 0,
Messages.BBPropertiesValidator_PATH_NOT_INWARDS_ERROR );
}
IStatus nameStatus = workspace.validateName( seg, IResource.FOLDER );
if( null != nameStatus && !nameStatus.isOK() ) {
return DiagnosticFactory.createDiagnostic( BBDiagnostic.ERROR, 0, nameStatus.getMessage() );
}
}
return AbstractDiagnosticFactory.getOK();
}
/**
* Validates whether the given string is a parsable int value
*
* @param i
* @return true if the string is parsable and false otherwise
*/
public static boolean isParsableInt( String i ) {
try {
Integer.parseInt( i );
return true;
} catch( NumberFormatException nfe ) {
return false;
}
}
}