blob: 0961b02e0780c7efe490c072ce11f4b70f3ffa84 [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.netbeans.modules.git;
import java.awt.Color;
import java.awt.EventQueue;
import java.io.File;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.prefs.Preferences;
import org.netbeans.libs.git.GitURI;
import org.netbeans.modules.git.FileInformation.Mode;
import org.netbeans.modules.git.ui.repository.RepositoryInfo;
import org.netbeans.modules.git.ui.repository.remote.ConnectionSettings;
import org.netbeans.modules.versioning.util.KeyringSupport;
import org.netbeans.modules.versioning.util.Utils;
import org.openide.util.NbPreferences;
/**
*
* @author ondra
*/
public final class GitModuleConfig {
private static GitModuleConfig instance;
private static final String AUTO_OPEN_OUTPUT_WINDOW = "autoOpenOutput"; // NOI18N
private static final String AUTO_REPLACE_INVALID_BRANCH_NAME_CHARACTERS = "autoReplaceInvalidBranchNameCharacters";
public static final String PROP_COMMIT_EXCLUSIONS = "commitExclusions"; // NOI18N
private static final String PROP_LAST_USED_MODE = "lastUsedMode"; // NOI18N
private static final String EXCLUDE_NEW_FILES = "excludeNewFiles"; // NOI18N
private static final String RECENT_COMMIT_AUTHORS = "recentCommitAuhtors";// NOI18N
private static final String RECENT_COMMITERS = "recentCommiters"; // NOI18N
private static final String RECENT_GURI = "recent_guri";
private static final String SIGN_OFF = "signOff"; // NOI18N
private static final String REVERT_ALL = "revertAll"; // NOI18N
private static final String REMOVE_ALL_NEW = "removeAllNew"; // NOI18N
private static final String REVERT_INDEX = "revertIndex"; // NOI18N
private static final String REVERT_WT = "revertWT"; // NOI18N
private static final String REMOVE_WT_NEW = "removeWTNew"; // NOI18N
private static final String PROP_LAST_USED_COMMIT_VIEW_MODE = "lastUsedCommitViewMode"; //NOI18N
private static final String AUTO_IGNORE_FILES = "autoIgnoreFiles"; //NOI18N
private static final String SHOW_CLONE_COMPLETED = "cloneCompleted.showCloneCompleted"; // NOI18N
private static final String GURI_PASSWORD = "guri_password";
private static final String GURI_PASSPHRASE = "guri_passphrase";
private static final String PROP_STATUS_VIEW_MODE = "statusViewMode"; //NOI18N
private static final String PROP_DIFF_VIEW_MODE = "diffViewMode"; //NOI18N
private static final String DELIMITER = "<=~=>"; // NOI18N
private static final String KEY_SHOW_HISTORY_MERGES = "showHistoryMerges"; //NOI18N
private static final String KEY_SHOW_FILE_INFO = "showFileInfo"; //NOI18N
private static final String KEY_SEARCH_ON_BRANCH = "searchOnBranch.enabled"; //NOI18N
private static final String PROP_ANNOTATIONFORMAT_PROJECT = "annotationFormat.project"; //NOI18N
private static final String KEY_ANNOTATION_DISPLAYED_FIELDS = "annotate.displayedFields"; //NOI18N
private String lastCanceledCommitMessage;
private static final String DEFAULT_ANNOTATION_PROJECT = Annotator.DEFAULT_ANNOTATION_PROJECT;
public static GitModuleConfig getDefault () {
if (instance == null) {
instance = new GitModuleConfig();
}
return instance;
}
private Set<String> exclusions;
private GitModuleConfig() { }
public boolean isExcludedFromCommit(String path) {
Set<String> commitExclusions = getCommitExclusions();
return commitExclusions.contains(path);
}
public Color getColor(String colorName, Color defaultColor) {
int colorRGB = getPreferences().getInt(colorName, defaultColor.getRGB());
return new Color(colorRGB);
}
public void setColor(String colorName, Color value) {
getPreferences().putInt(colorName, value.getRGB());
}
public Preferences getPreferences() {
return NbPreferences.forModule(GitModuleConfig.class);
}
public boolean getExludeNewFiles() {
return getPreferences().getBoolean(EXCLUDE_NEW_FILES, false);
}
public void setExcludeNewFiles(boolean value) {
getPreferences().putBoolean(EXCLUDE_NEW_FILES, value);
}
public String getLastCanceledCommitMessage() {
return lastCanceledCommitMessage == null ? "" : lastCanceledCommitMessage; //NOI18N
}
public void setLastCanceledCommitMessage(String message) {
lastCanceledCommitMessage = message;
}
/**
* @param paths collection of paths, of File.getAbsolutePath()
*/
public void addExclusionPaths(Collection<String> paths) {
Set<String> commitExclusions = getCommitExclusions();
if (commitExclusions.addAll(paths)) {
Utils.put(getPreferences(), PROP_COMMIT_EXCLUSIONS, new ArrayList<String>(commitExclusions));
}
}
/**
* @param paths collection of paths, File.getAbsolutePath()
*/
public void removeExclusionPaths(Collection<String> paths) {
Set<String> commitExclusions = getCommitExclusions();
if (commitExclusions.removeAll(paths)) {
Utils.put(getPreferences(), PROP_COMMIT_EXCLUSIONS, new ArrayList<String>(commitExclusions));
}
}
public String getProjectAnnotationFormat () {
return getPreferences().get(PROP_ANNOTATIONFORMAT_PROJECT, DEFAULT_ANNOTATION_PROJECT);
}
public void setProjectAnnotationFormat (String text) {
getPreferences().put(PROP_ANNOTATIONFORMAT_PROJECT, text);
}
public boolean getAutoSyncBranch (File repository, String branch) {
RepositoryInfo.NBGitConfig cfg = RepositoryInfo.getInstance(repository).getNetbeansConfig();
return cfg.getAutoSyncBranch(branch);
}
public void setAutoSyncBranch (File repository, String branch, boolean autoSync) {
RepositoryInfo.NBGitConfig cfg = RepositoryInfo.getInstance(repository).getNetbeansConfig();
cfg.setAutoSyncBranch(branch, autoSync);
}
synchronized Set<String> getCommitExclusions() {
if (exclusions == null) {
exclusions = new HashSet<String>(Utils.getStringList(getPreferences(), PROP_COMMIT_EXCLUSIONS));
}
return exclusions;
}
public Mode getLastUsedModificationContext () {
Mode mode;
try {
mode = Mode.valueOf(getPreferences().get(PROP_LAST_USED_MODE, Mode.HEAD_VS_WORKING_TREE.name()));
} catch (IllegalArgumentException ex) {
mode = null;
}
return mode == null ? Mode.HEAD_VS_WORKING_TREE : mode;
}
public void setLastUsedModificationContext (Mode mode) {
getPreferences().put(PROP_LAST_USED_MODE, mode.name());
}
public Mode getLastUsedCommitViewMode () {
Mode mode;
try {
mode = Mode.valueOf(getPreferences().get(PROP_LAST_USED_COMMIT_VIEW_MODE, Mode.HEAD_VS_WORKING_TREE.name()));
} catch (IllegalArgumentException ex) {
mode = null;
}
return mode == null ? Mode.HEAD_VS_WORKING_TREE : mode;
}
public void setLastUsedCommitViewMode (Mode mode) {
getPreferences().put(PROP_LAST_USED_COMMIT_VIEW_MODE, mode.name());
}
public boolean getAutoOpenOutput() {
return getPreferences().getBoolean(AUTO_OPEN_OUTPUT_WINDOW, true);
}
public void setAutoOpenOutput(boolean value) {
getPreferences().putBoolean(AUTO_OPEN_OUTPUT_WINDOW, value);
}
public boolean getAutoReplaceInvalidBranchNameCharacters() {
return getPreferences().getBoolean(AUTO_REPLACE_INVALID_BRANCH_NAME_CHARACTERS, false);
}
public void setAutoReplaceInvalidBranchNameCharacters(boolean value) {
getPreferences().putBoolean(AUTO_REPLACE_INVALID_BRANCH_NAME_CHARACTERS, value);
}
public void putRecentCommitAuthors(String author) {
if(author == null) return;
author = author.trim();
if(author.isEmpty()) return;
Utils.insert(getPreferences(), RECENT_COMMIT_AUTHORS, author, 10);
}
public void putRecentCommiter(String commiter) {
if(commiter == null) return;
commiter = commiter.trim();
if(commiter.isEmpty()) return;
Utils.insert(getPreferences(), RECENT_COMMITERS, commiter, 10);
}
public List<String> getRecentCommitAuthors() {
return Utils.getStringList(getPreferences(), RECENT_COMMIT_AUTHORS);
}
public List<String> getRecentCommiters() {
return Utils.getStringList(getPreferences(), RECENT_COMMITERS);
}
public void setSignOff(boolean value) {
getPreferences().putBoolean(SIGN_OFF, value);
}
public boolean getSignOff() {
return getPreferences().getBoolean(SIGN_OFF, false);
}
public void putRevertAll(boolean value) {
getPreferences().putBoolean(REVERT_ALL, value);
}
public void putRevertIndex(boolean value) {
getPreferences().putBoolean(REVERT_INDEX, value);
}
public void putRevertWT(boolean value) {
getPreferences().putBoolean(REVERT_WT, value);
}
public void putRemoveAllNew(boolean value) {
getPreferences().putBoolean(REMOVE_ALL_NEW, value);
}
public void putRemoveWTNew(boolean value) {
getPreferences().putBoolean(REMOVE_WT_NEW, value);
}
public boolean getRevertAll() {
return getPreferences().getBoolean(REVERT_ALL, true);
}
public boolean getRevertIndex() {
return getPreferences().getBoolean(REVERT_INDEX, false);
}
public boolean getRevertWT() {
return getPreferences().getBoolean(REVERT_WT, true);
}
public boolean getRemoveWTNew() {
return getPreferences().getBoolean(REMOVE_WT_NEW, false);
}
public boolean getRemoveAllNew() {
return getPreferences().getBoolean(REMOVE_ALL_NEW, false);
}
public boolean getAutoIgnoreFiles () {
return getPreferences().getBoolean(AUTO_IGNORE_FILES, true);
}
public void setAutoIgnoreFiles (boolean flag) {
getPreferences().putBoolean(AUTO_IGNORE_FILES, flag);
}
public boolean getShowCloneCompleted() {
return getPreferences().getBoolean(SHOW_CLONE_COMPLETED, true);
}
public void setShowCloneCompleted(boolean bl) {
getPreferences().putBoolean(SHOW_CLONE_COMPLETED, bl);
}
public boolean getShowHistoryMerges() {
return getPreferences().getBoolean(KEY_SHOW_HISTORY_MERGES, true);
}
public void setShowHistoryMerges(boolean bShowMerges) {
getPreferences().putBoolean(KEY_SHOW_HISTORY_MERGES, bShowMerges);
}
public boolean getShowFileInfo() {
return getPreferences().getBoolean(KEY_SHOW_FILE_INFO, false);
}
public void setShowFileInfo(boolean info) {
getPreferences().putBoolean(KEY_SHOW_FILE_INFO, info);
}
private final HashMap<String, ConnectionSettings> cachedConnectionSettings = new HashMap<String, ConnectionSettings>(5);
public void insertRecentConnectionSettings (ConnectionSettings toStore) {
assert !EventQueue.isDispatchThread();
String guriString = getUriStringWithoutCredentials(toStore.getUri());
if (guriString == null) {
return;
}
Preferences prefs = getPreferences();
removeStaleEntries(prefs, guriString);
if (toStore.isSaveCredentials() && (toStore.getPassphrase() != null || toStore.getPassword() != null)) {
// keep permanently and remove from cache
storeCredentials(toStore);
cachedConnectionSettings.remove(guriString);
} else {
// remove from perm storage, we should not keep it forever
deleteCredentials(toStore.getUri());
// but keep until end of session
cachedConnectionSettings.put(guriString, toStore);
}
if (!guriString.isEmpty()) {
Utils.insert(prefs, RECENT_GURI, new GitConnectionSettingsEntry(guriString, toStore).toString(), -1);
}
}
public void removeConnectionSettings (GitURI toRemove) {
assert !EventQueue.isDispatchThread();
String guriString = getUriStringWithoutCredentials(toRemove);
if (guriString == null) {
return;
}
Preferences prefs = getPreferences();
removeStaleEntries(prefs, guriString);
cachedConnectionSettings.remove(guriString);
deleteCredentials(toRemove);
}
private void removeStaleEntries (Preferences prefs, String guriString) {
List<String> urlValues = Utils.getStringList(prefs, RECENT_GURI);
for (Iterator<String> it = urlValues.iterator(); it.hasNext();) {
String rcOldString = it.next();
GitURI guriOld = null;
try {
GitConnectionSettingsEntry entry = GitConnectionSettingsEntry.create(rcOldString);
if(entry != null) {
guriOld = new GitURI(entry.guriString);
}
} catch (URISyntaxException ex) {
Git.LOG.log(Level.WARNING, rcOldString, ex);
}
if(guriString.equals(guriOld.toString())) {
Utils.removeFromArray(prefs, RECENT_GURI, rcOldString);
}
}
}
public List<ConnectionSettings> getRecentConnectionSettings () {
assert !EventQueue.isDispatchThread();
Preferences prefs = getPreferences();
List<String> urls = Utils.getStringList(prefs, RECENT_GURI);
List<ConnectionSettings> ret = new ArrayList<ConnectionSettings>(urls.size());
for (String guriString : urls) {
GitConnectionSettingsEntry entry = GitConnectionSettingsEntry.create(guriString);
if (entry == null) {
continue;
}
// before we contact keyring, check the cache:
ConnectionSettings connSettings = cachedConnectionSettings.get(entry.guriString);
if (connSettings == null) {
connSettings = entry.toConnectionSettings();
if (connSettings.isPrivateKeyAuth()) {
char[] passphrase = KeyringSupport.read(GURI_PASSPHRASE, connSettings.getUri().toString());
connSettings.setPassphrase(passphrase == null ? new char[0] : passphrase);
} else {
char[] password = KeyringSupport.read(GURI_PASSWORD, connSettings.getUri().toString());
connSettings.setPassword(password == null ? new char[0] : password);
}
}
ret.add(connSettings);
}
return ret;
}
public ConnectionSettings getConnectionSettings (String uriString) {
assert !EventQueue.isDispatchThread();
ConnectionSettings retval = null;
String username = null;
try {
GitURI uri = new GitURI(uriString);
username = uri.getUser();
uriString = getUriStringWithoutCredentials(uri);
} catch (URISyntaxException ex) {
//
}
// before we contact keyring, check the cache:
ConnectionSettings cachedSetting = cachedConnectionSettings.get(uriString);
if (cachedSetting != null && (username == null || cachedSetting.getUser() == null || username.equals(cachedSetting.getUser()))) {
return cachedSetting.copy();
}
Preferences prefs = getPreferences();
List<String> urls = Utils.getStringList(prefs, RECENT_GURI);
for (String guriString : urls) {
GitConnectionSettingsEntry entry = GitConnectionSettingsEntry.create(guriString);
if (entry == null) {
continue;
}
ConnectionSettings storedSettings = entry.toConnectionSettings();
if (uriString.equals(entry.guriString) && (username == null || storedSettings.getUser() == null || username.equals(storedSettings.getUser()))) {
if (storedSettings.isPrivateKeyAuth()) {
char[] passphrase = KeyringSupport.read(GURI_PASSPHRASE, storedSettings.getUri().toString());
storedSettings.setPassphrase(passphrase == null ? new char[0] : passphrase);
} else {
char[] password = KeyringSupport.read(GURI_PASSWORD, storedSettings.getUri().toString());
storedSettings.setPassword(password == null ? new char[0] : password);
}
retval = storedSettings;
break;
}
}
return retval;
}
private void storeCredentials (ConnectionSettings settings) {
assert !EventQueue.isDispatchThread();
GitURI uri = settings.getUri().setUser(settings.getUser());
KeyringSupport.save(GURI_PASSWORD, uri.toString(), settings.getPassword(), null);
KeyringSupport.save(GURI_PASSPHRASE, uri.toString(), settings.getPassphrase(), null);
}
private void deleteCredentials (GitURI guri) {
assert !EventQueue.isDispatchThread();
KeyringSupport.save(GURI_PASSWORD, guri.toString(), null, null);
KeyringSupport.save(GURI_PASSPHRASE, guri.toString(), null, null);
}
private String getUriStringWithoutCredentials (GitURI guri) {
String guriString = null;
if (guri != null) {
guriString = guri.setUser(null).setPass(null).toString();
}
return guriString;
}
public int getDiffViewMode (int def) {
return getPreferences().getInt(PROP_DIFF_VIEW_MODE, def);
}
public void setDiffViewMode (int value) {
getPreferences().putInt(PROP_DIFF_VIEW_MODE, value);
}
public int getStatusViewMode (int def) {
return getPreferences().getInt(PROP_STATUS_VIEW_MODE, def);
}
public void setStatusViewMode (int value) {
getPreferences().putInt(PROP_STATUS_VIEW_MODE, value);
}
public boolean isSearchOnlyCurrentBranchEnabled () {
return getPreferences().getBoolean(KEY_SEARCH_ON_BRANCH, true);
}
public void setSearchOnlyCurrentBranchEnabled (boolean enabled) {
getPreferences().putBoolean(KEY_SEARCH_ON_BRANCH, enabled);
}
public void setAnnotationDisplayedFields (int value) {
getPreferences().putInt(KEY_ANNOTATION_DISPLAYED_FIELDS, value);
}
public int getAnnotationDisplayedFields (int defaultValue) {
return getPreferences().getInt(KEY_ANNOTATION_DISPLAYED_FIELDS, defaultValue);
}
private static class GitConnectionSettingsEntry {
final String guriString;
private String stringValue;
private final ConnectionSettings settings;
static GitConnectionSettingsEntry create (String entryString) {
String[] s = entryString.split(DELIMITER);
assert s.length > 0;
try {
return new GitConnectionSettingsEntry(s[0], parse(s));
} catch (URISyntaxException ex) {
Git.LOG.log(Level.WARNING, "Cannot parse stored connection settings: {0}, {1}", new Object[] { s, ex.getMessage() });
return null;
}
}
GitConnectionSettingsEntry (String guriString, ConnectionSettings setts) {
this.guriString = guriString;
this.settings = setts;
}
@Override
public String toString() {
if(stringValue == null) {
StringBuilder sb = new StringBuilder();
sb.append(guriString);
sb.append(DELIMITER);
sb.append(settings.getUser() == null ? "" : settings.getUser());
sb.append(DELIMITER);
sb.append(settings.isSaveCredentials() ? "1" : "0"); //NOI18N
sb.append(DELIMITER);
sb.append(settings.isPrivateKeyAuth() ? "1" : "0"); //NOI18N
sb.append(DELIMITER);
sb.append(settings.getIdentityFile());
stringValue = sb.toString();
}
return stringValue;
}
private static ConnectionSettings parse (String[] s) throws URISyntaxException {
String uri = s[0].trim();
ConnectionSettings setts = new ConnectionSettings(new GitURI(uri));
if (s.length > 1) {
setts.setUser(s[1].isEmpty() ? null : s[1]);
}
if (s.length > 2) {
setts.setSaveCredentials("1".equals(s[2]));
}
if (s.length > 3) {
setts.setPrivateKeyAuth("1".equals(s[3]));
}
if (s.length > 4) {
setts.setIdentityFile(s[4]);
}
return setts;
}
private ConnectionSettings toConnectionSettings () {
return settings;
}
}
}