package de.pbauerochse.worklogviewer.util;
import de.pbauerochse.worklogviewer.domain.ReportTimerange;
import de.pbauerochse.worklogviewer.youtrack.connector.YouTrackAuthenticationMethod;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.time.DayOfWeek;
import java.util.Properties;
import static java.time.DayOfWeek.SATURDAY;
import static java.time.DayOfWeek.SUNDAY;
/**
* @author Patrick Bauerochse
* @since 01.04.15
*/
public class SettingsUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(SettingsUtil.class);
private static final File CONFIG_FILE_LOCATION = new File(System.getProperty("user.home"), "youtrack-worklog.properties");
private static final String WINDOW_X_PROPERTY = "window.x";
private static final String WINDOW_Y_PROPERTY = "window.y";
private static final String WINDOW_WIDTH_PROPERTY = "window.width";
private static final String WINDOW_HEIGHT_PROPERTY = "window.height";
private static final String WORK_HOURS_PROPERTY = "workhours";
private static final String YOUTRACK_AUTHENITCATION_METHOD_PROPERTY = "auth_method";
private static final String YOUTRACK_USERNAME_PROPERTY = "username";
private static final String YOUTRACK_PASSWORD_PROPERTY = "password";
private static final String YOUTRACK_URL_PROPERTY = "youtrackurl";
private static final String YOUTRACK_OAUTH_SERVICE_ID_PROPERTY = "oauth_service_id";
private static final String YOUTRACK_OAUTH_SERVICE_SECRET = "oauth_service_secret";
private static final String YOUTRACK_OAUTH_HUB_URL = "oauth_hub_url";
private static final String SHOW_ALL_WORKLOGS_PROPERTY = "showonlyowntimelogs.enabled";
private static final String SHOW_STATISTICS_PROPERTY = "statistics.enabled";
private static final String AUTOLOAD_DATA_PROPERTY = "autoload.enabled";
private static final String AUTOLOAD_DATA_TIMERANGE_PROPERTY = "autoload.timerange";
private static final String SHOW_DECIMAL_HOURS_IN_EXCEL_REPORT = "excel.decimaltimes";
private static final String COLLAPSE_STATE_PROPERTY = "collapse.state";
private static final String HIGHLIGHT_STATE_PROPERTY = "highlight.state";
private static Settings settings;
/**
* Loads the user settings for the config file if present
* if not it returns the default settings
*
* @return the loaded Settings object
*/
public static Settings loadSettings() {
if (settings == null) {
settings = new Settings();
if (CONFIG_FILE_LOCATION.exists()) {
LOGGER.debug("Loading configuration from {}", CONFIG_FILE_LOCATION.getAbsolutePath());
Properties properties = new Properties();
try {
properties.load(new FileInputStream(CONFIG_FILE_LOCATION));
applyFromPropertiesToSettings(settings, properties);
} catch (IOException e) {
LOGGER.error("Could not read settings from {}", CONFIG_FILE_LOCATION.getAbsolutePath(), e);
throw ExceptionUtil.getRuntimeException("exceptions.settings.read", e, CONFIG_FILE_LOCATION.getAbsolutePath());
}
}
}
return settings;
}
/**
* Saves the current Settings properties to the
* config file
*/
public static void saveSettings() {
if (!CONFIG_FILE_LOCATION.exists()) {
try {
LOGGER.debug("Trying to create new settings file at {}", CONFIG_FILE_LOCATION.getAbsolutePath());
CONFIG_FILE_LOCATION.createNewFile();
} catch (IOException e) {
LOGGER.error("Could not create settings file at {}", CONFIG_FILE_LOCATION.getAbsolutePath(), e);
throw ExceptionUtil.getRuntimeException("exceptions.settings.create", e, CONFIG_FILE_LOCATION.getAbsolutePath());
}
}
Properties properties = getAsProperties(settings);
try {
LOGGER.debug("Saving properties to settings file");
properties.store(new FileOutputStream(CONFIG_FILE_LOCATION), "Settings file for YouTrack worklog viewer");
} catch (IOException e) {
LOGGER.error("Could not save settings to {}", CONFIG_FILE_LOCATION.getAbsolutePath(), e);
throw ExceptionUtil.getRuntimeException("exceptions.settings.write", e, CONFIG_FILE_LOCATION.getAbsolutePath());
}
}
private static void applyFromPropertiesToSettings(Settings settings, Properties properties) {
String windowXAsString = properties.getProperty(WINDOW_X_PROPERTY);
if (StringUtils.isNotBlank(windowXAsString)) {
try {
settings.setWindowX(Integer.parseInt(windowXAsString));
} catch (NumberFormatException e) {
// ignore
LOGGER.warn("Could not convert {} to Integer for setting {}", windowXAsString, WINDOW_X_PROPERTY);
}
}
String windowYAsString = properties.getProperty(WINDOW_Y_PROPERTY);
if (StringUtils.isNotBlank(windowYAsString)) {
try {
settings.setWindowY(Integer.parseInt(windowYAsString));
} catch (NumberFormatException e) {
// ignore
LOGGER.warn("Could not convert {} to Integer for setting {}", windowYAsString, WINDOW_Y_PROPERTY);
}
}
String windowWidthAsString = properties.getProperty(WINDOW_WIDTH_PROPERTY);
if (StringUtils.isNotBlank(windowWidthAsString)) {
try {
settings.setWindowWidth(Integer.parseInt(windowWidthAsString));
} catch (NumberFormatException e) {
// ignore
LOGGER.warn("Could not convert {} to Integer for setting {}", windowWidthAsString, WINDOW_WIDTH_PROPERTY);
}
}
String windowHeightAsString = properties.getProperty(WINDOW_HEIGHT_PROPERTY);
if (StringUtils.isNotBlank(windowHeightAsString)) {
try {
settings.setWindowHeight(Integer.parseInt(windowHeightAsString));
} catch (NumberFormatException e) {
// ignore
LOGGER.warn("Could not convert {} to Integer for setting {}", windowHeightAsString, WINDOW_HEIGHT_PROPERTY);
}
}
String workHoursAsString = properties.getProperty(WORK_HOURS_PROPERTY);
if (StringUtils.isNotBlank(workHoursAsString)) {
try {
settings.setWorkHoursADay(Integer.parseInt(workHoursAsString));
} catch (NumberFormatException e) {
// ignore
LOGGER.warn("Could not convert {} to Integer for setting {}", workHoursAsString, WORK_HOURS_PROPERTY);
}
}
// youtrack authentication
String youtrackAuthenticationMethodAsString = properties.getProperty(YOUTRACK_AUTHENITCATION_METHOD_PROPERTY);
if (StringUtils.isNotBlank(youtrackAuthenticationMethodAsString)) {
try {
YouTrackAuthenticationMethod method = YouTrackAuthenticationMethod.valueOf(youtrackAuthenticationMethodAsString);
settings.setYouTrackAuthenticationMethod(method);
} catch (IllegalArgumentException e) {
LOGGER.warn("Could not determine AuthenticationMethod by settings value {}", youtrackAuthenticationMethodAsString);
}
}
settings.setYoutrackUrl(properties.getProperty(YOUTRACK_URL_PROPERTY));
settings.setYoutrackUsername(properties.getProperty(YOUTRACK_USERNAME_PROPERTY));
settings.setYoutrackOAuthServiceId(properties.getProperty(YOUTRACK_OAUTH_SERVICE_ID_PROPERTY));
settings.setYoutrackOAuthHubUrl(properties.getProperty(YOUTRACK_OAUTH_HUB_URL));
String encryptedUserPassword = properties.getProperty(YOUTRACK_PASSWORD_PROPERTY);
if (StringUtils.isNotBlank(encryptedUserPassword)) {
try {
settings.setYoutrackPassword(EncryptionUtil.decryptEncryptedString(encryptedUserPassword));
} catch (GeneralSecurityException e) {
LOGGER.error("Could not decrypt password from settings file", e);
throw ExceptionUtil.getIllegalStateException("exceptions.settings.password.decrypt", e);
}
}
String encryptedOAuthServiceSecret = properties.getProperty(YOUTRACK_OAUTH_SERVICE_SECRET);
if (StringUtils.isNotBlank(encryptedOAuthServiceSecret)) {
try {
settings.setYoutrackOAuthServiceSecret(EncryptionUtil.decryptEncryptedString(encryptedOAuthServiceSecret));
} catch (GeneralSecurityException e) {
LOGGER.error("Could not decrypt oauth secret from settings file", e);
throw ExceptionUtil.getIllegalStateException("exceptions.settings.oauthsecret.decrypt", e);
}
}
String showOnlyOwnWorklogsAsString = properties.getProperty(SHOW_ALL_WORKLOGS_PROPERTY);
if (StringUtils.isNotBlank(showOnlyOwnWorklogsAsString)) {
settings.setShowAllWorklogs(Boolean.valueOf(showOnlyOwnWorklogsAsString));
}
String showStatisticsAsString = properties.getProperty(SHOW_STATISTICS_PROPERTY);
if (StringUtils.isNotBlank(showStatisticsAsString)) {
settings.setShowStatistics(Boolean.valueOf(showStatisticsAsString));
}
String autoloadDataAsString = properties.getProperty(AUTOLOAD_DATA_PROPERTY);
if (StringUtils.isNotBlank(autoloadDataAsString)) {
settings.setLoadDataAtStartup(Boolean.valueOf(autoloadDataAsString));
}
String showDecimalHoursInExcel = properties.getProperty(SHOW_DECIMAL_HOURS_IN_EXCEL_REPORT);
if (StringUtils.isNotBlank(showDecimalHoursInExcel)) {
settings.setShowDecimalHourTimesInExcelReport(Boolean.valueOf(showDecimalHoursInExcel));
}
String autoloadDataTimerangeAsString = properties.getProperty(AUTOLOAD_DATA_TIMERANGE_PROPERTY);
if (StringUtils.isNotBlank(autoloadDataTimerangeAsString)) {
try {
ReportTimerange reportTimerange = ReportTimerange.valueOf(autoloadDataTimerangeAsString);
settings.setLastUsedReportTimerange(reportTimerange);
} catch (IllegalArgumentException e) {
LOGGER.warn("Could not determine ReportTimerange by settings value {}", autoloadDataTimerangeAsString);
}
}
String collapseStateAsString = properties.getProperty(COLLAPSE_STATE_PROPERTY);
if (StringUtils.isNotBlank(collapseStateAsString)) {
try {
settings.setCollapseState(Integer.parseInt(collapseStateAsString));
} catch (NumberFormatException e) {
LOGGER.warn("Could not get collapse state from {}", collapseStateAsString);
}
}
String highlightStateAsString = properties.getProperty(HIGHLIGHT_STATE_PROPERTY);
if (StringUtils.isNotBlank(highlightStateAsString)) {
try {
settings.setHighlightState(Integer.parseInt(highlightStateAsString));
} catch (NumberFormatException e) {
LOGGER.warn("Could not get highlight state from {}", highlightStateAsString);
}
}
}
private static Properties getAsProperties(Settings settings) {
Properties properties = new Properties();
properties.setProperty(WINDOW_X_PROPERTY, String.valueOf(settings.getWindowX()));
properties.setProperty(WINDOW_Y_PROPERTY, String.valueOf(settings.getWindowY()));
properties.setProperty(WINDOW_WIDTH_PROPERTY, String.valueOf(settings.getWindowWidth()));
properties.setProperty(WINDOW_HEIGHT_PROPERTY, String.valueOf(settings.getWindowHeight()));
properties.setProperty(WORK_HOURS_PROPERTY, String.valueOf(settings.getWorkHoursADay()));
properties.setProperty(SHOW_ALL_WORKLOGS_PROPERTY, String.valueOf(settings.isShowAllWorklogs()));
properties.setProperty(SHOW_STATISTICS_PROPERTY, String.valueOf(settings.isShowStatistics()));
properties.setProperty(AUTOLOAD_DATA_PROPERTY, String.valueOf(settings.isLoadDataAtStartup()));
properties.setProperty(SHOW_DECIMAL_HOURS_IN_EXCEL_REPORT, String.valueOf(settings.isShowDecimalHourTimesInExcelReport()));
if (StringUtils.isNotBlank(settings.getYoutrackUrl())) {
properties.setProperty(YOUTRACK_URL_PROPERTY, settings.getYoutrackUrl());
}
if (StringUtils.isNotBlank(settings.getYoutrackOAuthHubUrl())) {
properties.setProperty(YOUTRACK_OAUTH_HUB_URL, settings.getYoutrackOAuthHubUrl());
}
if (StringUtils.isNotBlank(settings.getYoutrackUsername())) {
properties.setProperty(YOUTRACK_USERNAME_PROPERTY, settings.getYoutrackUsername());
}
properties.setProperty(YOUTRACK_AUTHENITCATION_METHOD_PROPERTY, settings.getYouTrackAuthenticationMethod().name());
if (StringUtils.isNotBlank(settings.getYoutrackOAuthServiceId())) {
properties.setProperty(YOUTRACK_OAUTH_SERVICE_ID_PROPERTY, settings.getYoutrackOAuthServiceId());
}
if (StringUtils.isNotBlank(settings.getYoutrackOAuthServiceSecret())) {
try {
properties.setProperty(YOUTRACK_OAUTH_SERVICE_SECRET, EncryptionUtil.encryptCleartextString(settings.getYoutrackOAuthServiceSecret()));
} catch (GeneralSecurityException e) {
LOGGER.error("Could not encrypt oauth service secret for settings file", e);
throw ExceptionUtil.getIllegalStateException("exceptions.settings.oauthsecret.encrypt", e);
}
}
if (StringUtils.isNotBlank(settings.getYoutrackPassword())) {
try {
properties.setProperty(YOUTRACK_PASSWORD_PROPERTY, EncryptionUtil.encryptCleartextString(settings.getYoutrackPassword()));
} catch (GeneralSecurityException e) {
LOGGER.error("Could not encrypt password for settings file", e);
throw ExceptionUtil.getIllegalStateException("exceptions.settings.password.encrypt", e);
}
}
if (settings.getLastUsedReportTimerange() != null) {
properties.setProperty(AUTOLOAD_DATA_TIMERANGE_PROPERTY, settings.getLastUsedReportTimerange().name());
}
properties.setProperty(COLLAPSE_STATE_PROPERTY, String.valueOf(settings.collapseState));
properties.setProperty(HIGHLIGHT_STATE_PROPERTY, String.valueOf(settings.highlightState));
return properties;
}
public static class Settings {
private int windowWidth = 800;
private int windowHeight = 600;
private int windowX = 0;
private int windowY = 0;
private int workHoursADay = 8;
private YouTrackAuthenticationMethod youTrackAuthenticationMethod = YouTrackAuthenticationMethod.HTTP_API;
private String youtrackOAuthHubUrl;
private String youtrackOAuthServiceId;
private String youtrackOAuthServiceSecret;
private String youtrackUrl;
private String youtrackUsername;
private String youtrackPassword;
private boolean loadDataAtStartup = false;
private ReportTimerange lastUsedReportTimerange = ReportTimerange.THIS_WEEK;
private boolean showStatistics = true;
private boolean showAllWorklogs = true;
private boolean showDecimalHourTimesInExcelReport = false;
private int collapseState = createBitMaskState(SATURDAY, SUNDAY);
private int highlightState = createBitMaskState(SATURDAY, SUNDAY);
public int getWindowWidth() {
return windowWidth;
}
public void setWindowWidth(int windowWidth) {
this.windowWidth = windowWidth;
}
public int getWindowHeight() {
return windowHeight;
}
public void setWindowHeight(int windowHeight) {
this.windowHeight = windowHeight;
}
public int getWindowX() {
return windowX;
}
public void setWindowX(int windowX) {
this.windowX = windowX;
}
public int getWindowY() {
return windowY;
}
public void setWindowY(int windowY) {
this.windowY = windowY;
}
public int getWorkHoursADay() {
return workHoursADay;
}
public void setWorkHoursADay(int workHoursADay) {
this.workHoursADay = workHoursADay;
}
public YouTrackAuthenticationMethod getYouTrackAuthenticationMethod() {
return youTrackAuthenticationMethod;
}
public void setYouTrackAuthenticationMethod(YouTrackAuthenticationMethod youTrackAuthenticationMethod) {
this.youTrackAuthenticationMethod = youTrackAuthenticationMethod;
}
public String getYoutrackOAuthHubUrl() {
return youtrackOAuthHubUrl;
}
public void setYoutrackOAuthHubUrl(String youtrackOAuthHubUrl) {
this.youtrackOAuthHubUrl = youtrackOAuthHubUrl;
}
public String getYoutrackOAuthServiceId() {
return youtrackOAuthServiceId;
}
public void setYoutrackOAuthServiceId(String youtrackOAuthServiceId) {
this.youtrackOAuthServiceId = youtrackOAuthServiceId;
}
public String getYoutrackOAuthServiceSecret() {
return youtrackOAuthServiceSecret;
}
public void setYoutrackOAuthServiceSecret(String youtrackOAuthServiceSecret) {
this.youtrackOAuthServiceSecret = youtrackOAuthServiceSecret;
}
public String getYoutrackUrl() {
return youtrackUrl;
}
public void setYoutrackUrl(String youtrackUrl) {
this.youtrackUrl = youtrackUrl;
}
public String getYoutrackUsername() {
return youtrackUsername;
}
public void setYoutrackUsername(String youtrackUsername) {
this.youtrackUsername = youtrackUsername;
}
public String getYoutrackPassword() {
return youtrackPassword;
}
public void setYoutrackPassword(String youtrackPassword) {
this.youtrackPassword = youtrackPassword;
}
public boolean isLoadDataAtStartup() {
return loadDataAtStartup;
}
public void setLoadDataAtStartup(boolean loadDataAtStartup) {
this.loadDataAtStartup = loadDataAtStartup;
}
public ReportTimerange getLastUsedReportTimerange() {
return lastUsedReportTimerange;
}
public void setLastUsedReportTimerange(ReportTimerange lastUsedReportTimerange) {
this.lastUsedReportTimerange = lastUsedReportTimerange;
}
public boolean isShowStatistics() {
return showStatistics;
}
public void setShowStatistics(boolean showStatistics) {
this.showStatistics = showStatistics;
}
public boolean isShowAllWorklogs() {
return showAllWorklogs;
}
public void setShowAllWorklogs(boolean showAllWorklogs) {
this.showAllWorklogs = showAllWorklogs;
}
public boolean isShowDecimalHourTimesInExcelReport() {
return showDecimalHourTimesInExcelReport;
}
public void setShowDecimalHourTimesInExcelReport(boolean showDecimalHourTimesInExcelReport) {
this.showDecimalHourTimesInExcelReport = showDecimalHourTimesInExcelReport;
}
public int getCollapseState() {
return collapseState;
}
public void setCollapseState(int collapseState) {
this.collapseState = collapseState;
}
public int getHighlightState() {
return highlightState;
}
public void setHighlightState(int highlightState) {
this.highlightState = highlightState;
}
public boolean hasMissingConnectionParameters() {
return StringUtils.isEmpty(youtrackUrl) ||
StringUtils.isEmpty(youtrackUsername) ||
StringUtils.isEmpty(youtrackPassword) ||
(
youTrackAuthenticationMethod == YouTrackAuthenticationMethod.OAUTH2 &&
(StringUtils.isEmpty(youtrackOAuthHubUrl) || StringUtils.isEmpty(youtrackOAuthServiceId) || StringUtils.isEmpty(youtrackOAuthServiceSecret))
);
}
public boolean hasHighlightState(DayOfWeek day) {
return hasBitValue(highlightState, day);
}
public boolean hasCollapseState(DayOfWeek day) {
return hasBitValue(collapseState, day);
}
public void setHighlightState(DayOfWeek day, boolean selected) {
highlightState = setBitValue(highlightState, day, selected);
}
public void setCollapseState(DayOfWeek day, boolean selected) {
collapseState = setBitValue(collapseState, day, selected);
}
public int createBitMaskState(DayOfWeek... setDays) {
int bitmask = 0;
for (DayOfWeek day : setDays) {
bitmask = setBitValue(bitmask,day, true);
}
return bitmask;
}
public int setBitValue(int state, DayOfWeek day, boolean selected) {
if (selected) {
return state | (1 << day.ordinal());
} else {
return state & ~(1 << day.ordinal());
}
}
public boolean hasBitValue(int state, DayOfWeek day) {
int bitValue = (1 << day.ordinal());
return (state & bitValue) == bitValue;
}
public int getConnectionParametersHashCode() {
int result = getHashOrZero(youtrackUrl);
result = 31 * result + getHashOrZero(youtrackUsername);
result = 31 * result + getHashOrZero(youtrackPassword);
result = 31 * result + getHashOrZero(youTrackAuthenticationMethod);
result = 31 * result + getHashOrZero(youtrackOAuthServiceId);
result = 31 * result + getHashOrZero(youtrackOAuthServiceSecret);
result = 31 * result + getHashOrZero(youtrackOAuthHubUrl);
return result;
}
private int getHashOrZero(Object o) {
if (o == null) return 0;
return o.hashCode();
}
}
}