/**
* This file is part of alf.io.
*
* alf.io is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* alf.io is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with alf.io. If not, see <http://www.gnu.org/licenses/>.
*/
package alfio.manager;
import alfio.repository.EventRepository;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Locale;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@Component
public class EventNameManager {
private static final Pattern NUMBER_MATCHER = Pattern.compile("^\\d+$");
private static final String SPACES_AND_PUNCTUATION = "[\\s\\p{Punct}]";
private static final String FIND_EVIL_CHARACTERS = "[^\\sA-Z\\-a-z0-9]";
private final EventRepository eventRepository;
@Autowired
public EventNameManager(EventRepository eventRepository) {
this.eventRepository = eventRepository;
}
/**
* Generates and returns a short name based on the given display name.<br>
* The generated short name will be returned only if it was not already used.<br>
* The input parameter will be clean from "evil" characters such as punctuation and accents
*
* 1) if the {@code displayName} is a one-word name, then no further calculation will be done and it will be returned as it is, to lower case
* 2) the {@code displayName} will be split by word and transformed to lower case. If the total length is less than 15, then it will be joined using "-" and returned
* 3) the first letter of each word will be taken, excluding numbers
* 4) a random code will be returned
*
* @param displayName
* @return
*/
public String generateShortName(String displayName) {
Validate.isTrue(StringUtils.isNotBlank(displayName));
String cleanDisplayName = StringUtils.stripAccents(StringUtils.normalizeSpace(displayName)).toLowerCase(Locale.ENGLISH).replaceAll(FIND_EVIL_CHARACTERS, "-");
if(!StringUtils.containsWhitespace(cleanDisplayName) && isUnique(cleanDisplayName)) {
return cleanDisplayName;
}
Optional<String> dashedName = getDashedName(cleanDisplayName);
if(dashedName.isPresent()) {
return dashedName.get();
}
Optional<String> croppedName = getCroppedName(cleanDisplayName);
if (croppedName.isPresent()) {
return croppedName.get();
}
return generateRandomName();
}
private String generateRandomName() {
return IntStream.range(0, 5)
.mapToObj(i -> RandomStringUtils.randomAlphanumeric(15))
.filter(this::isUnique)
.limit(1)
.findFirst()
.orElse("");
}
private Optional<String> getCroppedName(String cleanDisplayName) {
String candidate = Arrays.stream(cleanDisplayName.split(SPACES_AND_PUNCTUATION))
.map(w -> Pair.of(NUMBER_MATCHER.matcher(w).matches(), w))
.map(p -> p.getKey() ? p.getValue() : StringUtils.left(p.getValue(), 1))
.collect(Collectors.joining());
if(isUnique(candidate)) {
return Optional.of(candidate);
}
return Optional.empty();
}
private Optional<String> getDashedName(String cleanDisplayName) {
if(cleanDisplayName.length() < 15) {
String candidate = cleanDisplayName.replaceAll(SPACES_AND_PUNCTUATION, "-");
if(isUnique(candidate)) {
return Optional.of(candidate);
}
}
return Optional.empty();
}
public boolean isUnique(String shortName) {
return eventRepository.countByShortName(shortName) == 0;
}
}