package org.molgenis.omx.decorators;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.molgenis.core.Identifiable;
import org.molgenis.framework.db.DatabaseException;
/**
* This class contains functions to enforce the XGAP naming policy for entities
* and storing files that belong to certain entities.
*
* @author joerivandervelde
*
*/
public class NameConvention
{
/**
* Converts file name into its safe version, so that they can be safely
* stored on any file system. Trims, then converts uppercase alphabetic
* characters to lowercase. Then only leaves alphabetic characters, numbers
* and underscore in the output. The output is then truncated at 50
* characters. If the output ends up with length 0, a DatabaseException is
* thrown. Note that filenames are allowed to start with numerals, while
* entitynames are not. In practice however, to-be filenames are already
* checked as being valid entitynames first.
*
* @param input
* @return
* @throws Exception
*/
public static String escapeFileName(String name) throws DatabaseException
{
// trim whitespace
name = name.trim();
// leave only lowercase alphbetics
name = name.toLowerCase();
// remove bad characters
Pattern p1 = Pattern.compile("([a-z_0-9])");
String output = "";
for (char s : name.toCharArray())
{
Matcher m = p1.matcher(s + "");
if (m.matches())
{
output += m.group();
}
}
// truncate at 50 chars
if (output.length() > 50)
{
output = output.substring(0, 50);
}
// check final length, may not be empty
if (output.length() == 0)
{
throw new DatabaseException("Escaped filename of '" + name + "' is an empty string.");
}
return output;
}
/**
* Converts an entity name into its safe version, so that they can be used
* in contexts such as programming environments where strict names are
* needed. Trims, then leaves only alphabetic characters, numerals and
* underscores. Then removes any heading numerals. A DatabaseException is
* thrown if the final output has length 0. Eg. "123name" becomes "name",
* "x123nAmE" stays "x123nAmE", and "@#23" becomes "", which will throw an
* exception. This is the inverse of 'validateEntityNameStrict'.
*
* @param name
* @return
* @throws Exception
*/
public static String escapeEntityNameStrict(String name) throws DatabaseException
{
// trim whitespace
name = name.trim();
// remove bad characters
String output = "";
Pattern p1 = Pattern.compile("([a-zA-Z0-9_])");
for (char s : name.toCharArray())
{
Matcher m = p1.matcher(s + "");
if (m.matches())
{
output += m.group();
}
}
// remove any heading numerals
Pattern p2 = Pattern.compile("^[0-9]+(.+)");
Matcher m = p2.matcher(output);
if (m.matches())
{
output = m.group(1);
}
// FIX: when a number is the only character left, it will not be
// matched! performing extra check here
if (output.length() == 1)
{
Pattern p3 = Pattern.compile("([0-9])");
Matcher m1 = p3.matcher(output);
if (m1.matches())
{
output = "";
}
}
// check final length, may not be empty
if (output.length() == 0)
{
throw new DatabaseException("Escaped name of '" + name + "' is an empty string.");
}
return output;
}
/**
* Validates a file name, so that they can be safely stored on any file
* system. Throws a DatabaseException when input is empty or untrimmed, if
* other characters than lowercase alphabetics, numbers or underscore are
* used, or when the name is longer than 50 characters. Note that filenames
* are allowed to start with numerals, while entitynames are not. In
* practice however, to-be filenames are already checked as being valid
* entitynames first.
*
* @throws DatabaseException
*/
public static void validateFileName(String name) throws DatabaseException
{
if (name.length() == 0)
{
throw new DatabaseException("File name is empty.");
}
if (name.length() != name.trim().length())
{
throw new DatabaseException("File name '" + name + "' is untrimmed.");
}
if (name.length() > 50)
{
throw new DatabaseException("File name is longer than 50 characters.");
}
// check for illegal characters
Pattern p1 = Pattern.compile("([a-z_0-9])");
for (char s : name.toCharArray())
{
Matcher m = p1.matcher(s + "");
if (!m.matches())
{
throw new DatabaseException("Illegal character (" + s + ") in file name '" + name
+ "'. Use only a-z, 0-9, and underscore.");
}
}
}
/**
* Validates an entity name, checking that only characters from the set
* [<>/a-zA-Z0-9_\\s\\-:.(),;\\+] are used.
*
* @param name
* @throws DatabaseException
*/
public static void validateEntityName(String name) throws DatabaseException
{
// pattern for bbmri ([a-zA-Z0-9_\\s\\-:.(),;\\+])
String pattern = "([<>/a-zA-Z0-9_\\s\\-:.(),;\\+\\*])";
if (name == null || name.length() == 0)
{
throw new DatabaseException("Name is empty.");
}
if (name.length() != name.trim().length())
{
throw new DatabaseException("Name '" + name + "' is untrimmed.");
}
// check for illegal characters
Pattern p2 = Pattern.compile(pattern);
for (char s : name.toCharArray())
{
Matcher m2 = p2.matcher(s + "");
if (!m2.matches())
{
throw new DatabaseException("Illegal character (" + s + ") in name '" + name
+ "'. Use only allowed characters from the set " + pattern);
}
}
}
/**
* Validates an entity name, so that they can be used in contexts such as
* programming environments where strict names are needed. Throws a
* DatabaseException when this name is empty or untrimmed, if other
* characters than alphabetics, numerals or underscore are used, or when the
* name starts with a numeral.
*
* @param name
* @throws DatabaseException
*/
public static void validateEntityNameStrict(String name) throws DatabaseException
{
if (name.length() == 0)
{
throw new DatabaseException("Name is empty.");
}
if (name.length() != name.trim().length())
{
throw new DatabaseException("Name '" + name + "' is untrimmed.");
}
// check if first character is a number
Pattern p1 = Pattern.compile("([0-9])");
String firstChar = name.substring(0, 1);
Matcher m1 = p1.matcher(firstChar);
if (m1.matches())
{
throw new DatabaseException("Name '" + name + "' is not allowed to start with a numeral (" + firstChar
+ ").");
}
// check for illegal characters
Pattern p2 = Pattern.compile("([a-zA-Z0-9_])");
for (char s : name.toCharArray())
{
Matcher m2 = p2.matcher(s + "");
if (!m2.matches())
{
throw new DatabaseException("Illegal character (" + s + ") in name '" + name
+ "'. Use only a-z, A-Z, 0-9, and underscore.");
}
}
}
/**
* Validate names of Entities by wrapping
* NameConvention.validateEntityName(name)
*
* @param <E>
* @param entities
* @throws DatabaseException
*/
public static <E extends Identifiable> void validateEntityNames(List<E> entities) throws DatabaseException
{
for (E i : entities)
{
String name = i.getName();
NameConvention.validateEntityName(name);
}
}
/**
* Validate names of Entities by wrapping
* NameConvention.validateEntityNameStrict(name)
*
* @param <E>
* @param entities
* @throws DatabaseException
*/
public static <E extends Identifiable> void validateEntityNamesStrict(List<E> entities) throws DatabaseException
{
for (E i : entities)
{
String name = i.getName();
NameConvention.validateEntityNameStrict(name);
}
}
}