/*******************************************************************************
* Copyright (c) 2013 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.foundation.core.validate.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.jboss.tools.foundation.core.internal.FoundationCorePlugin;
import org.jboss.tools.foundation.core.validate.IFileNameValidator;
/**
* Validates a filename by creating a temporary file with the given name
* on the underlying filesystem. In addition, checks are made on reserved
* characters and words.
*/
public class FileNameValidator implements IFileNameValidator, FileNameValidatorConstants {
private static final String WINDOWS = "WINDOWS"; //$NON-NLS-1$
private static final String MAC = "MAC"; //$NON-NLS-1$
private class ValidatorStatus extends MultiStatus {
/**
* Create new instance
*/
public ValidatorStatus() {
super(FoundationCorePlugin.PLUGIN_ID, IStatus.OK, Messages.FileNameValidatorValidationSuccessful, null);
}
@Override
protected void setMessage(String message) {
super.setMessage(message);
}
}
/**
* Root directory for testing files
*/
private String parentDir = System.getProperty("java.io.tmpdir"); //$NON-NLS-1$
/**
* Collection of reserved characters that should not appear in filenames
*/
private Collection<Character> reservedChars = new ArrayList<Character>();
/**
* Collection of reserved words that should not appear as filenames
*/
private Collection<String> reservedWords = new ArrayList<String>();
/**
* @return given status is a failure
*/
private boolean isFailure(IStatus status) {
return status.matches(IStatus.ERROR);
}
/**
* Create a failure status
*/
private IStatus createFailureStatus(String message) {
return new Status(IStatus.ERROR, FoundationCorePlugin.PLUGIN_ID, message);
}
/**
* Set the warning message
*/
private IStatus createWarningStatus(String message) {
return new Status(IStatus.WARNING, FoundationCorePlugin.PLUGIN_ID, message);
}
/**
* @return true if this system is running a Window OS, otherwise false
*/
private boolean isWindows() {
return getOSValue().contains(WINDOWS);
}
/**
* @return true if this system is running a Window OS, otherwise false
*/
private boolean isMac() {
return getOSValue().contains(MAC);
}
/**
* @return value representation of host OS
*/
private String getOSValue() {
return System.getProperty("os.name").toUpperCase(); //$NON-NLS-1$
}
/**
* Should we create a test file for this validation
*
* @return
*/
private boolean shouldCreateTestFile(int systems) {
if( failLocalFilesystem(systems) ||
(isWindows() && failWindows(systems)) ||
(isMac() && failMac(systems))) {
return true;
}
return false;
}
/**
* Given the systems to fail on, should we fail on windows
*
* @param systems
* @return
*/
private boolean failLocalFilesystem(int systems) {
return (((systems & ERROR_ON_LOCAL_FS) == ERROR_ON_LOCAL_FS));
}
/**
* Given the systems to fail on, should we fail on windows
*
* @param systems
* @return
*/
private boolean failWindows(int systems) {
return (isWindows() && ((systems & ERROR_ON_LOCAL_FS) == ERROR_ON_LOCAL_FS)) ||
((systems & ERROR_ON_WINDOWS) == ERROR_ON_WINDOWS);
}
/**
* Given the systems to fail on, should we fail on mac
*
* @param systems
* @return
*/
private boolean failMac(int systems) {
return (isMac() && ((systems & ERROR_ON_LOCAL_FS) == ERROR_ON_LOCAL_FS)) ||
((systems & ERROR_ON_MAC) == ERROR_ON_MAC);
}
/**
* Test whether the given file name contains the nul character.
*
* This is applicable to all systems.
*
* @param fileName
*
* @return ok status if test passes, otherwise a failure status
*/
private IStatus testNullCharacter(String fileName) {
if (fileName.contains(NUL)) {
return createFailureStatus(Messages.FileNameValidatorNullCharacter);
}
return Status.OK_STATUS;
}
/**
* File name should be less than or equal to 255 characters
*
* This is applicable to all systems.
*
* @param fileName
*
* @return ok status if test passes, otherwise a failure status
*/
private IStatus testLength(String fileName) {
if (fileName.length() > 255) {
return createFailureStatus(Messages.FileNameValidatorTooLong);
}
return Status.OK_STATUS;
}
/**
* Test whether the given file name contains any of the set of
* reserved characters
*
* This is especially applicable to Windows and FAT file systems
*
* @param fileName
* @param systems an integer representing systems to fail on
*
* @return ok status if test passes, otherwise a failure status
*/
private IStatus testReservedChars(String fileName, int systems) {
/*
* User-added characters so test should fail if the file name
* contains them
*/
for (char c : reservedChars) {
if (fileName.indexOf(c) > -1) {
return createFailureStatus(Messages.FileNameValidatorReservedCharacter);
}
}
/*
* Test for universal reserved characters
*/
for (char c : RESERVED_CHARACTERS) {
if (fileName.indexOf(c) > -1) {
return createFailureStatus(Messages.FileNameValidatorReservedCharacter);
}
}
MultiStatus status = new MultiStatus(FoundationCorePlugin.PLUGIN_ID, IStatus.OK, "", null); //$NON-NLS-1$
/*
* Test for Mac reserved characters
*/
for (char c : MAC_RESERVED_CHARACTERS) {
if (fileName.indexOf(c) > -1) {
if (failMac(systems) ) {
return createFailureStatus(Messages.FileNameValidatorReservedCharacter);
} else {
status.add(createWarningStatus(Messages.FileNameValidatorReservedCharacter));
}
}
}
/*
* Test for Windows reserved characters
*/
for (char c : WIN_RESERVED_CHARACTERS) {
if (fileName.indexOf(c) > -1) {
if (failWindows(systems)) {
return createFailureStatus(Messages.FileNameValidatorReservedCharacter);
} else {
status.add(createWarningStatus(Messages.FileNameValidatorReservedCharacter));
}
}
}
return status;
}
/**
* Tests whether the given filename is one of the reserved words
*
* This is especially applicable to Windows
*
* @param fileName
* @param systems an integer representing systems to fail on
* @return ok status if test passes, otherwise a failure status
*/
private IStatus testReservedWord(String fileName, int systems) {
int lastDot = fileName.lastIndexOf(DOT);
if (lastDot > -1)
fileName = fileName.substring(0, lastDot);
/*
* User-added words so test should fail if the file name
* contains them
*/
for (String word : reservedWords) {
if (word.equalsIgnoreCase(fileName)) {
return createFailureStatus(Messages.FileNameValidatorReservedWord);
}
}
/*
* Test for universal reserved characters
*/
for (String word : RESERVED_WORDS) {
if (word.equalsIgnoreCase(fileName)) {
return createFailureStatus(Messages.FileNameValidatorReservedWord);
}
}
MultiStatus status = new MultiStatus(FoundationCorePlugin.PLUGIN_ID, IStatus.OK, "", null); //$NON-NLS-1$
/*
* Test for Windows reserved words
*/
for (String word : WIN_RESERVED_WORDS) {
if (word.equalsIgnoreCase(fileName)) {
if (failWindows(systems)) {
return createFailureStatus(Messages.FileNameValidatorReservedWord);
} else {
status.add(createWarningStatus(Messages.FileNameValidatorReservedWord));
}
}
}
return status;
}
/**
* Test whether given file name contains a dot implying the presence
* of a suffix.
*
* This is applicable to Windows.
*
* @param fileName
*
* @return ok status if test passes, otherwise a failure status
*/
private IStatus testSuffix(String fileName, int systems) {
int dotIndex = fileName.indexOf(DOT);
if (dotIndex < 1 || dotIndex >= fileName.length() - 1) {
// No suffix
if (failWindows(systems)) {
return createFailureStatus(Messages.FileNameValidatorNoSuffix);
} else {
return createWarningStatus(Messages.FileNameValidatorNoSuffix);
}
}
return Status.OK_STATUS;
}
/**
* File name cannot end with a period or space.
*
* This is applicable to Windows
*
* @param fileName
*
* @return ok status if test passes, otherwise a failure status
*/
private IStatus testIllegalFinalCharacter(String fileName, int systems) {
if (fileName.endsWith(DOT) || fileName.endsWith(SPACE)) {
if (failWindows(systems)) {
return createFailureStatus(Messages.FileNameValidatorEndsFinalCharacter);
} else {
return createWarningStatus(Messages.FileNameValidatorEndsFinalCharacter);
}
}
return Status.OK_STATUS;
}
private void evaluate(IStatus testStatus, ValidatorStatus outputStatus) {
if (! testStatus.isOK()) {
// Only care about failures and warnings
outputStatus.add(testStatus);
switch (outputStatus.getSeverity()) {
case IStatus.ERROR:
outputStatus.setMessage(Messages.FileNameValidatorValidationFailed);
break;
case IStatus.WARNING:
outputStatus.setMessage(Messages.FileNameValidatorValidationWarning);
break;
}
}
}
/**
* Validate the given filename
*
* @param fileName
*
* @return status indicating the result of the validity of the filename
*/
@Override
public IStatus validate(String fileName) {
return validate(fileName, ERROR_ON_LOCAL_FS);
}
public IStatus validate(String fileName, int systems) {
if (fileName == null) {
return createFailureStatus(Messages.FileNameValidatorNullFileName);
}
ValidatorStatus resultStatus = new ValidatorStatus();
evaluate(testLength(fileName), resultStatus);
if (isFailure(resultStatus))
return resultStatus;
evaluate(testNullCharacter(fileName), resultStatus);
if (isFailure(resultStatus))
return resultStatus;
evaluate(testIllegalFinalCharacter(fileName, systems), resultStatus);
if (isFailure(resultStatus))
return resultStatus;
evaluate(testReservedChars(fileName, systems), resultStatus);
if (isFailure(resultStatus))
return resultStatus;
evaluate(testReservedWord(fileName, systems), resultStatus);
if (isFailure(resultStatus))
return resultStatus;
evaluate(testSuffix(fileName, systems), resultStatus);
if (isFailure(resultStatus))
return resultStatus;
if( shouldCreateTestFile(systems)) {
/*
* Test a temporary file can be created with the file name
*/
File tempTestFile = new File(parentDir, fileName);
try {
if (tempTestFile.createNewFile()) {
tempTestFile.delete();
} else
resultStatus.add(createFailureStatus(Messages.FileNameValidatorFailedToCreateTestFile));
} catch (Exception ex) {
StringBuilder builder = new StringBuilder(Messages.FileNameValidatorFailedToCreateTestFile);
builder.append("\n"); //$NON-NLS-1$
builder.append(ex.getLocalizedMessage());
resultStatus.add(createFailureStatus(builder.toString()));
}
}
return resultStatus;
}
/**
* Set the parent directory which will be used in the filename validation.
* In most cases, the default of java's temporary directory should be
* sufficient but if the filename is going to be used on a different filesystem
* then in may be prudent to test on this alternative filesystem.
*
* @param directory
*/
@Override
public void setParentDirectory(String directory) {
this.parentDir = directory;
}
/**
* Adds a character that should not be used in a filename.
*
* This character will be checked in addition to the minimum
* list detailed in RESERVED_CHARACTERS.
*
* @param character
*/
@Override
public void addReservedCharacter(Character character) {
reservedChars.add(character);
}
/**
* Adds a word that should not be used as a filename.
*
* Once added, this word cannot appear as either a single filename or
* a filename with a suffix.
*
* @param reservedWord
*/
@Override
public void addReservedWord(String reservedWord) {
reservedWords .add(reservedWord);
}
}