/*
* OpenClinica is distributed under the GNU Lesser General Public License (GNU
* LGPL).
*
* For details see: http://www.openclinica.org/license copyright 2003-2005 Akaza
* Research
*
* Created on May, 2008
*/
package org.akaza.openclinica.bean.extract;
import java.util.TreeSet;
/**
* Validate if a customized SASFormatName is valid.
*
* <p>
* A valid customized SASFormatName should follow the rule that
* <ul>
* <li> it can be up to eight characters long
* <li> it can consist of only letters, digits and underscore characters, but
* there are exceptions for its first character.
* <li> if it formats a character variable, it should start with a dollar sign,
* i.e. "$"
* <li> its first character cannot be a digit and underscore is not recommended
* <li> a character format name cannot end with a number.
* </ul>
* <br>
* <p>
* Rules for creating a valid customized SASFormatName:
* <ul>
* <li> Replace any invalid character with an underscore
* <li> If the first character is a digit, in front of it puts letter "X".
* <li> If a name is longer than 8 characters, it will be truncated to 8
* characters. If it results in non-unique name in a data file, the last 3
* characters will be changed as the pattern sequentialNumber-englishAlphabet.
* The size of sequential number is 2.
* </ul>
*
*
* @auther ywang (May, 2008)
*/
public class ODMSASFormatNameValidator {
private TreeSet<String> uniqueNameTable = new TreeSet<String>();
private int digitSize;
private char[] tails;
private int sequential;
private char firstChar;
private char replacingChar;
private int nameMaxLength;// 8;// 32;
private int largestValue;
public ODMSASFormatNameValidator() {
this.digitSize = 2;
this.largestValue = 100;
this.sequential = 1;
this.firstChar = 'X';
this.replacingChar = '_';
this.nameMaxLength = 8;
this.initTails();
}
/**
* Given a name, this methods returns a valid SASFormatName and it
* guarantees the uniqueness of this name
*
* @param name
* String
* @return String
*/
public String getValidSASFormatName(String name, boolean isCharacter) {
// if passed name is null, automatically generate
if (name == null || name.trim().length() == 0) {
String sas = this.firstChar + getNextSequentialString() + this.firstChar;
return isCharacter ? "$" + sas : sas;
}
// get all chars from the string first
String temp = isCharacter && !name.trim().startsWith("$") ? "$" + name.trim().toUpperCase() : name.trim().toUpperCase();
char c[] = temp.length() > this.nameMaxLength ? temp.substring(0, this.nameMaxLength).toCharArray() : temp.toCharArray();
// replacing every invalid character with the replacingChar
int j = isCharacter ? 1 : 0;
int i;
for (i = j; i < c.length; ++i) {
if (!isValid(c[i])) {
c[i] = this.replacingChar;
}
}
// if the first one is a digit or underscore
char f = isCharacter ? c[1] : c[0];
if (f >= '0' && f <= '9' || f == '_') {
// if there is already max length characters
if (c.length >= this.nameMaxLength) {
for (i = c.length - 1; i >= 1; --i) {
c[i] = c[i - 1];
}
c[0] = this.firstChar;
} else {
char cc[] = new char[c.length + 1];
cc[0] = this.firstChar;
for (i = 1; i < cc.length; ++i) {
cc[i] = c[i - 1];
}
c = cc;
}
}
if (isCharacter && c[1] == '$') {
char t = c[0];
c[0] = c[1];
c[1] = t;
}
int mysize = this.nameMaxLength - this.digitSize - 1;
String s = new String(c);
// handle last character as a number
if (Character.isDigit(c[c.length - 1])) {
if (s.length() < this.nameMaxLength) {
s += "X";
} else {
s =
Character.isDigit(c[this.nameMaxLength - 2]) ? s.substring(0, this.nameMaxLength - 1) + "X" : s.substring(0, mysize)
+ this.getNextSequentialString() + 'X';
}
}
String s2 = s;
int index = 0;
// if not unique
while (this.uniqueNameTable.contains(s2)) {
String seq = this.getNextSequentialString();
s2 = s.length() > mysize ? s.substring(0, mysize) + seq + tails[index] : s + seq + tails[index];
index = Integer.valueOf(seq) >= this.largestValue - 1 ? index + 1 : index;
}
uniqueNameTable.add(s2);
return s2;
}
private void initTails() {
String a = "XYZABCDEFGHIJKLMNOPQRSTUVW";
this.tails = a.toCharArray();
}
// only alphabets, digits, and _ are valid
private static boolean isValid(char c) {
return c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_';
}
/**
* Get next sequential String
*
* @return String
*/
public String getNextSequentialString() {
String s = "" + sequential;
for (int i = s.length(); i < this.digitSize; ++i) {
s = "0" + s;
}
this.sequential++;
if (this.sequential >= this.largestValue) {
this.sequential = 1;
}
return s;
}
public void setDigitSizeAndLargestValue(int size) {
this.digitSize = size;
this.largestValue = (int) Math.pow(10, this.digitSize);
}
/**
* @return int
*/
public int getDigitSize() {
return this.digitSize;
}
/**
* @param c
* char
*/
public void setReplacingChar(char c) {
this.replacingChar = c;
}
/**
* @return char
*/
public char getReplacingChar() {
return this.replacingChar;
}
public void setFirstChar(char c) {
this.firstChar = c;
}
public char getFirstChar() {
return this.firstChar;
}
public void setLargestValue(int v) {
this.largestValue = v;
}
public int getLargestValue() {
return this.largestValue;
}
/**
*
* @return int
*/
public int getMaxNameLength() {
return this.nameMaxLength;
}
/**
* @param len
* int
*/
public void setMaxNameLength(int len) {
this.nameMaxLength = len;
}
public void setUniqueNameTable(TreeSet<String> nameTable) {
this.uniqueNameTable = nameTable;
}
public TreeSet<String> getUniqueNameTable() {
return this.uniqueNameTable;
}
}