/*
* Copyright (C) 2015 Google Inc. All Rights Reserved.
*
* Licensed 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 apps.provisioning.server.account;
import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import apps.provisioning.util.Utils;
/**
* Generates username suggestions. This is designed as an Iterator, so it can be controlled by
* hasNext and next methods.
*/
public class UsernameIterator implements Iterator<String> {
// Matches every pattern, where each pattern is a string like "[fieldName]" for any value of
// "fieldName" (e.g., [firstname], [lastname], [studentId]) with optional "C#" in front
// which indicates where to shorten the field (e.g., [C3_firstname] would be the first three
// characters of the first name.
// Also matches [#], which is used for an auto-incrementing number.
private final Pattern PATTERN_REGEX = Pattern.compile("\\[(?:C(\\d+)_)?([#\\w]+)\\]");
private final String DEFAULT_PATTERN = "[C9_firstname][C9_lastname][#]";
private final String AUTONUMERIC_PATTERN = "[#]";
private final Logger logger = Logger.getLogger(UsernameIterator.class.getName());
private HashMap<String, String> userData;
private Integer autonumeric;
private Integer patternIndex;
private String currentPattern;
private String nextSuggestion;
private String[] patterns;
/**
* Creates a UsernameIterator object.
*
* @param patterns The patterns to be evaluated as a String array.
* @param userData The user information, the required fields are at least first name and last
* name.
* @throws Exception
*/
public UsernameIterator(String[] patterns, HashMap<String, String> userData) throws Exception {
if (userData == null || patterns == null) {
throw new Exception("The patterns and userData parameters can't be set as null.");
}
if (!userData.containsKey(UsernameManager.FIRST_NAME)
|| !userData.containsKey(UsernameManager.LAST_NAME)) {
throw new Exception(
"The userData parameter must cointain at least firstname and lastname fields.");
}
String firstname = userData.get(UsernameManager.FIRST_NAME);
String lastname = userData.get(UsernameManager.LAST_NAME);
if (firstname.length() > UsernameManager.MAX_NAME_LENGTH
|| lastname.length() > UsernameManager.MAX_NAME_LENGTH) {
throw new Exception("One of the fields exceds the maximum length. 60 (firstname,lastname).");
}
this.patterns = patterns;
this.userData = userData;
autonumeric = 1;
patternIndex = 0;
}
/**
* Retrieves the following pattern to be evaluated. If the pattern contains the autonumeric symbol
* it returns the same pattern.
*/
private String getNextPattern() {
if (currentPattern != null && currentPattern.contains(AUTONUMERIC_PATTERN)) {
// Retrieves the previous generated pattern.
return currentPattern;
}
if (patternIndex < patterns.length) {
return patterns[patternIndex++];
} else {
// No other pattern is available, so it returns the default.
return DEFAULT_PATTERN;
}
}
/**
* Evaluates the current pattern and replaces the tags with user fields.
*
* @return Evaluated suggestion.
*/
private String processPattern() {
currentPattern = getNextPattern();
String suggestion = currentPattern;
Matcher matcher = PATTERN_REGEX.matcher(suggestion);
String fieldValue = "";
while (matcher.find()) {
String key = matcher.group(0);
String fieldName = matcher.group(2);
if (key.equals(AUTONUMERIC_PATTERN)) {
fieldValue = "" + autonumeric++;
} else {
fieldValue = Utils.replaceSpecialChars(userData.get(fieldName));
if (fieldValue == null) {
logger.log(
Level.WARNING,
"Field " + fieldName + " was not provided in the " + key + " pattern for user "
+ userData.get(UsernameManager.FIRST_NAME) + " "
+ userData.get(UsernameManager.LAST_NAME));
return processPattern();
}
// Gets the matched string and retrieves the number of characters to be extracted.
String substring_matcher = matcher.group(1);
if (substring_matcher != null) {
Integer characters = Integer.parseInt(substring_matcher);
if (fieldValue.length() > characters) {
fieldValue = fieldValue.substring(0, characters);
}
}
}
suggestion = matcher.replaceFirst(fieldValue);
matcher.reset(suggestion);
}
if (suggestion.isEmpty()) {
// This case happens when all the characters are invalid.
return processPattern();
}
return suggestion;
}
/**
* Checks if the next element is not longer than 64 characters.
*/
public boolean hasNext() {
nextSuggestion = processPattern();
if (currentPattern == DEFAULT_PATTERN
&& nextSuggestion.length() > UsernameManager.MAX_USERNAME_LENGTH) {
logger.log(Level.WARNING, "The user name has exceeded the maximum length.");
nextSuggestion = null;
return false;
}
return true;
}
/**
* Retrieves the next suggestion
*/
public String next() {
if (nextSuggestion != null) {
String next = nextSuggestion;
nextSuggestion = null;
return next;
}
return processPattern();
}
/**
* This is not necessary.
*/
public void remove() {
throw new RuntimeException("Remove method doesn't apply to UsernameIterator.");
}
}