blob: 6185bb1be2f1e1846b2e6fb9dadac38ebffff565 [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*******************************************************************************/
package org.apache.ofbiz.entity.model;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import org.apache.ofbiz.base.util.StringUtil;
import org.apache.ofbiz.base.util.UtilMisc;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.entity.model.ModelViewEntity.ModelAlias;
/**
* Generic Entity - General Utilities
*
*/
public final class ModelUtil {
public static final String module = ModelUtil.class.getName();
private static final String vowelBag = "aeiouyAEIOUY";
private ModelUtil () {}
/**
* Changes the first letter of the passed String to upper case.
* @param string The passed String
* @return A String with an upper case first letter
*/
public static String upperFirstChar(String string) {
if (string == null) return null;
if (string.length() <= 1) return string.toLowerCase();
StringBuilder sb = new StringBuilder(string);
sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
return sb.toString();
}
/**
* Changes the first letter of the passed String to lower case.
*
* @param string The passed String
* @return A String with a lower case first letter
*/
public static String lowerFirstChar(String string) {
if (string == null) return null;
if (string.length() <= 1) return string.toLowerCase();
StringBuilder sb = new StringBuilder(string);
sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
return sb.toString();
}
/** Converts a database name to a Java class name.
* The naming conventions used to allow for this are as follows: a database name (table or
* column) is in all capital letters, and the words are separated by an underscore
* (for example: NEAT_ENTITY_NAME or RANDOM_FIELD_NAME); a Java name (ejb or field) is in all
* lower case letters, except the letter at the beginning of each word (for example:
* NeatEntityName or RandomFieldName). The convention of using a capital letter at
* the beginning of a class name in Java, or a lower-case letter for the beginning of a
* variable name in Java is also used along with the Java name convention above.
* @param columnName The database name
* @return The Java class name
*/
public static String dbNameToClassName(String columnName) {
return upperFirstChar(dbNameToVarName(columnName));
}
/** Converts a database name to a Java variable name.
* The naming conventions used to allow for this are as follows: a database name (table or
* column) is in all capital letters, and the words are separated by an underscore
* (for example: NEAT_ENTITY_NAME or RANDOM_FIELD_NAME); a Java name (ejb or field) is in all
* lower case letters, except the letter at the beginning of each word (for example:
* NeatEntityName or RandomFieldName). The convention of using a capital letter at
* the beginning of a class name in Java, or a lower-case letter for the beginning of a
* variable name in Java is also used along with the Java name convention above.
* @param columnName The database name
* @return The Java variable name
*/
public static String dbNameToVarName(String columnName) {
if (columnName == null) return null;
StringBuilder fieldName = new StringBuilder(columnName.length());
boolean toUpper = false;
for (int i=0; i < columnName.length(); i++) {
char ch = columnName.charAt(i);
if (ch == '_') {
toUpper = true;
} else if (toUpper) {
fieldName.append(Character.toUpperCase(ch));
toUpper = false;
} else {
fieldName.append(Character.toLowerCase(ch));
}
}
return fieldName.toString();
}
/**
* Converts a Java variable name to a database name.
* The naming conventions used to allow for this are as follows: a database name (table or
* column) is in all capital letters, and the words are separated by an underscore
* (for example: NEAT_ENTITY_NAME or RANDOM_FIELD_NAME); a Java name (ejb or field) is in all
* lower case letters, except the letter at the beginning of each word (for example:
* NeatEntityName or RandomFieldName). The convention of using a capital letter at
* the beginning of a class name in Java, or a lower-case letter for the beginning of a
* variable name in Java is also used along with the Java name convention above.
* @param javaName The Java variable name
* @return The database name
*/
public static String javaNameToDbName(String javaName) {
if (javaName == null) return null;
if (javaName.length() <= 0) return "";
StringBuilder dbName = new StringBuilder();
dbName.append(Character.toUpperCase(javaName.charAt(0)));
int namePos = 1;
while (namePos < javaName.length()) {
char curChar = javaName.charAt(namePos);
if (Character.isUpperCase(curChar)) dbName.append('_');
dbName.append(Character.toUpperCase(curChar));
namePos++;
}
return dbName.toString();
}
/** Start by removing all vowels, then pull 1 letter at a time off the end of each _ separated segment, go until it is less than or equal to the desired length
*
* @param dbName
* @param desiredLength
* @return shortened String
*/
public static String shortenDbName(String dbName, int desiredLength) {
StringBuilder dbBuf = new StringBuilder(dbName);
if (dbBuf.length() > desiredLength) {
// remove one vowel at a time, starting at beginning
for (int i = dbBuf.length() - 1; i > 0; i--) {
// don't remove vowels that are at the beginning of the string (taken care of by the i > 0) or right after an underscore
if (dbBuf.charAt(i - 1) == '_') {
continue;
}
char curChar = dbBuf.charAt(i);
if (vowelBag.indexOf(curChar) > 0) {
dbBuf.deleteCharAt(i);
}
}
}
// remove all double underscores
while (dbBuf.indexOf("__") > 0) {
dbBuf.deleteCharAt(dbBuf.indexOf("__"));
}
while (dbBuf.length() > desiredLength) {
boolean removedChars = false;
int usIndex = dbBuf.lastIndexOf("_");
while (usIndex > 0 && dbBuf.length() > desiredLength) {
// if this is the first word in the group, don't pull letters off unless it is 4 letters or more
int prevUsIndex = dbBuf.lastIndexOf("_", usIndex - 1);
if (prevUsIndex < 0 && usIndex < 4) {
break;
}
// don't remove characters to reduce the size two less than three characters between underscores
if (prevUsIndex >= 0 && (usIndex - prevUsIndex) <= 4) {
usIndex = prevUsIndex;
continue;
}
// delete the second to last character instead of the last, better chance of being unique
dbBuf.deleteCharAt(usIndex - 2);
removedChars = true;
if (usIndex > 2) {
usIndex = dbBuf.lastIndexOf("_", usIndex - 2);
} else {
break;
}
}
// now delete the char at the end of the string if necessary
if (dbBuf.length() > desiredLength) {
int removeIndex = dbBuf.length() - 1;
int prevRemoveIndex = dbBuf.lastIndexOf("_", removeIndex - 1);
// don't remove characters to reduce the size two less than two characters between underscores
if (prevRemoveIndex < 0 || (removeIndex - prevRemoveIndex) >= 3) {
// delete the second to last character instead of the last, better chance of being unique
dbBuf.deleteCharAt(removeIndex - 1);
removedChars = true;
}
}
// remove all double underscores
while (dbBuf.indexOf("__") > 0) {
dbBuf.deleteCharAt(dbBuf.indexOf("__"));
removedChars = true;
}
// if we didn't remove anything break out to avoid an infinite loop
if (!removedChars) {
break;
}
}
// remove all double underscores
while (dbBuf.indexOf("__") > 0) {
dbBuf.deleteCharAt(dbBuf.indexOf("__"));
}
while (dbBuf.length() > desiredLength) {
// still not short enough, get more aggressive
// don't remove the first segment, just remove the second over and over until we are short enough
int firstUs = dbBuf.indexOf("_");
if (firstUs > 0) {
int nextUs = dbBuf.indexOf("_", firstUs + 1);
if (nextUs > 0) {
//Debug.logInfo("couldn't shorten enough normally, removing second segment from " + dbBuf, module);
dbBuf.delete(firstUs, nextUs);
}
}
}
//Debug.logInfo("Shortened " + dbName + " to " + dbBuf.toString(), module);
return dbBuf.toString();
}
/**
* Converts a package name to a path by replacing all '.' characters with the File.separatorChar character.
* Is therefore platform independent.
* @param packageName The package name.
* @return The path name corresponding to the specified package name.
*/
public static String packageToPath(String packageName) {
// just replace all of the '.' characters with the folder separater character
return packageName.replace('.', File.separatorChar);
}
/**
* Replaces all occurances of oldString in mainString with newString
* @param mainString The original string
* @param oldString The string to replace
* @param newString The string to insert in place of the old
* @return mainString with all occurances of oldString replaced by newString
*/
public static String replaceString(String mainString, String oldString, String newString) {
return StringUtil.replaceString(mainString, oldString, newString);
}
public static String induceFieldType(String sqlTypeName, int length, int precision, ModelFieldTypeReader fieldTypeReader) {
if (sqlTypeName == null) return "invalid";
if (sqlTypeName.equalsIgnoreCase("VARCHAR") || sqlTypeName.equalsIgnoreCase("VARCHAR2") || (sqlTypeName.equalsIgnoreCase("CHAR") && length > 1)) {
if (length <= 10) return "very-short";
if (length <= 60) return "short-varchar";
if (length <= 255) return "long-varchar";
return "very-long";
} else if (sqlTypeName.equalsIgnoreCase("TEXT")) {
return "very-long";
} else if (sqlTypeName.equalsIgnoreCase("INT") || sqlTypeName.equalsIgnoreCase("SMALLINT") ||
sqlTypeName.equalsIgnoreCase("DECIMAL") || sqlTypeName.equalsIgnoreCase("NUMERIC")) {
if (length > 18 || precision > 6) return "invalid-" + sqlTypeName + ":" + length + ":" + precision;
if (precision == 0) return "numeric";
if (precision == 2) return "currency-amount";
if (precision <= 6) return "floating-point";
return "invalid-" + sqlTypeName + ":" + length + ":" + precision;
} else if (sqlTypeName.equalsIgnoreCase("BLOB") || sqlTypeName.equalsIgnoreCase("OID")) {
return "blob";
} else if (sqlTypeName.equalsIgnoreCase("DATETIME") || sqlTypeName.equalsIgnoreCase("TIMESTAMP")) {
return "date-time";
} else if (sqlTypeName.equalsIgnoreCase("DATE")) {
return "date";
} else if (sqlTypeName.equalsIgnoreCase("TIME")) {
return "time";
} else if (sqlTypeName.equalsIgnoreCase("CHAR") && length == 1) {
return "indicator";
} else {
return "invalid-" + sqlTypeName + ":" + length + ":" + precision;
}
}
/**
* Check is a ModelEntity have a default resource associate to resolve localized value
* When the ModelEntity is a ModelViewEntity, check with the field to resolve the related entity
* @param modelEntity
* @param fieldName
* @return
*/
public static boolean isPotentialLocalizedField(ModelEntity modelEntity, String fieldName) {
return isPotentialLocalizedFields(modelEntity, UtilMisc.toList(fieldName));
}
/**
* Check is a ModelEntity have a default resource associate to resolve localized value
* When the ModelEntity is a ModelViewEntity, check with the list fields to resolve these related entities
* @param modelEntity
* @param fieldName
* @return
*/
public static boolean isPotentialLocalizedFields(ModelEntity modelEntity, List<String> fieldNames) {
if (modelEntity == null) return false;
if (modelEntity instanceof ModelViewEntity) {
// now try to retrieve with the field heading from the real entity linked to the view
ModelViewEntity modelViewEntity = (ModelViewEntity) modelEntity;
Iterator<ModelAlias> it = modelViewEntity.getAliasesIterator();
while (it.hasNext()) {
ModelAlias modelAlias = it.next();
if (fieldNames.contains(modelAlias.getName())) {
ModelEntity memberModelEntity = modelViewEntity.getMemberModelEntity(modelAlias.getEntityAlias());
if (UtilValidate.isNotEmpty(memberModelEntity.getDefaultResourceName())) {
return true;
}
}
}
return false;
} else {
return UtilValidate.isNotEmpty(modelEntity.getDefaultResourceName());
}
}
}