/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
* <p>
*/
package org.olat.core.commons.persistence;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import org.olat.core.id.Persistable;
import org.olat.core.util.Formatter;
import org.olat.core.util.filter.FilterFactory;
/**
* Description:<BR>
* Helper methods to work with persistable objects
* <P>
* Initial Date: Nov 30, 2004
*
* @author gnaegi
*/
public class PersistenceHelper {
private static boolean charCountNativeUTF8 = true;
/**
* Spring only constructor. Use static methods to access the helper
* @param firstKeyValues init properties from spring
*/
public PersistenceHelper(Properties firstKeyValues) {
// OLAT-6439 Init flag to know if the database understands native UTF-8
// Only necessary for old MySQL database of UZH
if (firstKeyValues != null) {
String dbvendor = firstKeyValues.getProperty("dbvendor");
String dboptions = firstKeyValues.getProperty("mysqloptions");
if ("mysql".equals(dbvendor) && dboptions != null && dboptions.contains("characterEncoding=UTF-8") && dboptions.contains("useOldUTF8Behavior=true")) {
charCountNativeUTF8 = false;
}
}
}
/**
* Truncate the given original string to the defined max length. The method
* does also check if some legacy UTF-8 conversion is done by the database
* driver and shortens the allowed length to two third to be on the save
* side
*
* @param original
* The original String
* @param maxLength
* The max length allowed by the database schema
* @param showThreeDots
* Replace the last three characters with ... to indicate that
* the string has been truncated
* @return The truncated string
*/
public static String truncateStringDbSave(final String original, int maxLength, final boolean showThreeDots) {
if (original == null) {
return null;
}
if (!charCountNativeUTF8) {
// When using legacy UTF-8 conversion we have actually no idea how
// long the string can be since this is a hidden information to
// MySQL. In that case we subtract 1/3 and cross our fingers
maxLength = maxLength - (maxLength/3);
}
// Check if too long
int length = original.length();
if (length <= maxLength) {
return original;
}
// 1) Remove all HTML markup first as truncating could lead to invalid HTML code.
String result = FilterFactory.getHtmlTagAndDescapingFilter().filter(original);
if (length <= maxLength) {
return original;
}
// 2) Truncate to maxLength
if (showThreeDots) {
result = Formatter.truncate(result, maxLength);
} else {
result = Formatter.truncateOnly(result, maxLength);
}
return result;
}
public static boolean appendAnd(StringBuilder sb, boolean where) {
if(where) {
sb.append(" and ");
} else {
sb.append(" where ");
}
return true;
}
public static boolean appendAnd(NativeQueryBuilder sb, boolean where) {
if(where) {
sb.append(" and ");
} else {
sb.append(" where ");
}
return true;
}
public static boolean appendGroupBy(StringBuilder sb, String dbRef, SortKey... orderBy) {
boolean appended = false;
if(orderBy != null && orderBy.length > 0 && orderBy[0] != null) {
sb.append(" order by ");
for(SortKey sort:orderBy) {
sb.append(dbRef).append(".").append(sort.getKey());
if(sort.isAsc()) {
sb.append(" asc ");
} else {
sb.append(" desc ");
}
appended = true;
}
}
return appended;
}
public static final void appendFuzzyLike(StringBuilder sb, String var, String key, String dbVendor) {
if(dbVendor.equals("mysql")) {
sb.append(" ").append(var).append(" like :").append(key);
} else {
sb.append(" lower(").append(var).append(") like :").append(key);
}
if(dbVendor.equals("oracle")) {
sb.append(" escape '\\'");
}
}
/**
* Helper method that replaces * with % and appends and
* prepends % to the string to make fuzzy SQL match when using like.
* Use "" to disable this feature and use exact match
* @param email
* @return fuzzized string
*/
public static final String makeFuzzyQueryString(String string) {
// By default only fuzzy at the end. Usually it makes no sense to do a
// fuzzy search with % at the beginning, but it makes the query very very
// slow since it can not use any index and must perform a fulltext search.
// User can always use * to make it a really fuzzy search query
if (string.length() > 1 && string.startsWith("\"") && string.endsWith("\"")) {
string = string.substring(1, string.length()-1);
} else {
if(!string.startsWith("*") && !string.endsWith("*")) {
string = "%" + string + "%";
}
string = string.replace('*', '%');
}
// with 'LIKE' the character '_' is a wildcard which matches exactly one character.
// To test for literal instances of '_', we have to escape it.
string = string.replace("_", "\\_");
return string.toLowerCase();
}
public static String makeEndFuzzyQueryString(String string) {
// By default only fuzzy at the end. Usually it makes no sense to do a
// fuzzy search with % at the beginning, but it makes the query very very
// slow since it can not use any index and must perform a fulltext search.
// User can always use * to make it a really fuzzy search query
string = string.replace('*', '%');
string = string + "%";
// with 'LIKE' the character '_' is a wildcard which matches exactly one character.
// To test for literal instances of '_', we have to escape it.
string = string.replace("_", "\\_");
return string.toLowerCase();
}
/**
*
* Checks if the given persistable is in the given iterator (database identity).
* If so, the persistable from the iterator is returned. If not, null is returned.
* @param iter Iterator of persistable objects
* @param persistable The persistable object that is looked for in the iterator
* @return null if not found or the persistable object that has the same key as the given
* persistable object. The object might also have object identity, but this is not guaranteed.
*/
public static Persistable getPersistableByPersistableKey(Iterator<? extends Persistable> iter, Persistable persistable) {
while (iter.hasNext()) {
Persistable persistableFromIterator = iter.next();
if (persistable.equalsByPersistableKey(persistableFromIterator))
return persistableFromIterator;
}
return null;
}
/**
* Iterates over a list to see if the given persistable object is already in this list.
* This differs from list.contains() in the way that it does not check object identity
* but hibernate identity. The object list contains the object if it contains any
* object with object.getKey() equals persistable.getKey()
* @param objects List of persistable objects
* @param persistable Persistable object
* @return boolean
*/
public static boolean listContainsObjectByKey(List<? extends Persistable> objects, Persistable persistable) {
return listContainsObjectByKey(objects, persistable.getKey());
}
/**
* Iterates over a list to see if there is an object in the list with the given persistable
* key that is used by the hibernate layer.
* @param objects List of persistable objects
* @param persistable Persistable object
* @return boolean
*/
public static boolean listContainsObjectByKey(List<? extends Persistable> objects, Long key) {
for (Iterator<? extends Persistable> iter = objects.iterator(); iter.hasNext();) {
try {
Persistable listObject = iter.next();
if (listObject.getKey().equals(key)) {
return true;
}
} catch (ClassCastException e) {
throw new AssertionError("Class cast exception: objects list must contain object only of type persistable!");
}
}
return false;
}
/**
* Returns the position of the object in the list. An object is found when it has
* the same hibernate key as the given object (!= java object identity)
* @param objects
* @param persistable
* @return int position of object in list
*/
public static int indexOf(List<? extends Persistable> objects, Persistable persistable){
return indexOf(objects, persistable.getKey());
}
/**
* Returns the position of the object in the list. An object is found when it has
* the same hibernate key as the given object (!= java object identity)
* @param objects
* @param key
* @return int position of object in list
*/
public static int indexOf(List<? extends Persistable> objects, Long key) {
for (Iterator<? extends Persistable> iter = objects.iterator(); iter.hasNext();) {
try {
Persistable listObject = iter.next();
if (listObject.getKey().equals(key)) {
return objects.indexOf(listObject);
}
} catch (ClassCastException e) {
throw new AssertionError("Class cast exception: objects list must contain object only of type persistable!");
}
}
return -1;
}
/**
* Replace an object in the given list that has the same persistance key.
* @param objects List ob original objects
* @param toBeReplacedObject The object that should be searched for in the list and the
* replace value
* @return boolean true: object replaced; false: object was not found in list
*/
public static boolean replaceObjectInListByKey(List<Persistable> objects, Persistable toBeReplacedObject) {
int i = indexOf(objects, toBeReplacedObject);
// return false when object was not found in list
if (i < 0) return false;
// otherwhise replace the object and return true
objects.remove(i);
objects.add(i, toBeReplacedObject);
return true;
}
/**
* Removes a list of persistable objects from another list with
* persistable objects by comparing the hibernate key instead of the
* object identity.
* @param originalList
* @param toBeRemovedObjects
* @return int number of objects removed from the originalList
* After calling this operation the originalList will contain less or the same amount of
* objects
*/
public static int removeObjectsFromList(List<? extends Persistable> originalList, List<? extends Persistable> toBeRemovedObjects) {
int counter = 0;
Iterator<? extends Persistable> removeIter = toBeRemovedObjects.iterator();
while (removeIter.hasNext()) {
Persistable toBeRemoved = removeIter.next();
Iterator<? extends Persistable> originalIter = originalList.iterator();
while (originalIter.hasNext()) {
Persistable fromOriginal = originalIter.next();
if (fromOriginal.getKey().equals(toBeRemoved.getKey())) {
originalList.remove(fromOriginal);
counter++;
break;
}
}
}
return counter;
}
public static Persistable findInListByKey(List<? extends Persistable> persistables, Persistable persistable) {
Long key = persistable.getKey();
for (Iterator<? extends Persistable> iter = persistables.iterator(); iter.hasNext();) {
Persistable ppit = iter.next();
if (ppit.getKey().equals(key)) {
return ppit;
}
}
return null;
}
/**
* @param listOfPersistables
* @param persistable
* @return True if listOfPersistable contains persistable
*/
public static boolean containsPersistable(List<? extends Persistable> listOfPersistables, Persistable persistable) {
Long key = persistable.getKey();
for (Iterator<? extends Persistable> iter = listOfPersistables.iterator(); iter.hasNext();) {
Persistable entry = iter.next();
if (entry.getKey().equals(key)) {
return true;
}
}
return false;
}
/**
*
* @param list
* @return
*/
public static List<Long> toKeys(Collection<? extends Persistable> list) {
List<Long> keys = new ArrayList<Long>();
for(Persistable obj:list) {
keys.add(obj.getKey());
}
return keys;
}
/**
*
* @param list
* @return
*/
public static List<Long> toKeys(Persistable... list) {
List<Long> keys = new ArrayList<Long>();
if(list != null && list.length > 0) {
for(Persistable obj:list) {
keys.add(obj.getKey());
}
}
return keys;
}
public static Long extractLong(Object[] results, int pos) {
if(results == null || pos >= results.length) return null;
Object obj = results[pos];
return obj == null ? null : ((Number)obj).longValue();
}
public static String extractString(Object[] results, int pos) {
if(results == null || pos >= results.length ) return null;
Object obj = results[pos];
return obj == null ? null : (obj instanceof String ? (String)obj : obj.toString());
}
}