2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 package org
.jetbrains
.idea
.svn
;
20 import com
.intellij
.openapi
.application
.ApplicationManager
;
21 import com
.intellij
.openapi
.application
.ModalityState
;
22 import com
.intellij
.openapi
.components
.ProjectComponent
;
23 import com
.intellij
.openapi
.diagnostic
.Logger
;
24 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
25 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
26 import com
.intellij
.openapi
.progress
.ProgressManager
;
27 import com
.intellij
.openapi
.project
.Project
;
28 import com
.intellij
.openapi
.util
.*;
29 import com
.intellij
.openapi
.util
.io
.FileUtil
;
30 import com
.intellij
.openapi
.vcs
.annotate
.AnnotationListener
;
31 import org
.jdom
.Attribute
;
32 import org
.jdom
.DataConversionException
;
33 import org
.jdom
.Element
;
34 import org
.jetbrains
.idea
.svn
.dialogs
.SvnAuthenticationProvider
;
35 import org
.jetbrains
.idea
.svn
.dialogs
.SvnInteractiveAuthenticationProvider
;
36 import org
.jetbrains
.idea
.svn
.update
.MergeRootInfo
;
37 import org
.jetbrains
.idea
.svn
.update
.UpdateRootInfo
;
38 import org
.tmatesoft
.svn
.core
.SVNDepth
;
39 import org
.tmatesoft
.svn
.core
.auth
.ISVNAuthenticationManager
;
40 import org
.tmatesoft
.svn
.core
.internal
.wc
.ISVNAuthenticationStorage
;
41 import org
.tmatesoft
.svn
.core
.internal
.wc
.SVNConfigFile
;
42 import org
.tmatesoft
.svn
.core
.internal
.wc
.SVNFileUtil
;
43 import org
.tmatesoft
.svn
.core
.wc
.ISVNOptions
;
44 import org
.tmatesoft
.svn
.core
.wc
.SVNWCUtil
;
47 import java
.io
.FilenameFilter
;
50 public class SvnConfiguration
implements ProjectComponent
, JDOMExternalizable
{
51 private static final Logger LOG
= Logger
.getInstance("org.jetbrains.idea.svn.SvnConfiguration");
53 private final static String SERVERS_FILE_NAME
= "servers";
55 public static final String UPGRADE_AUTO
= "auto";
56 public static final String UPGRADE_AUTO_15
= "auto1.5";
57 public static final String UPGRADE_AUTO_16
= "auto1.6";
58 public static final String UPGRADE_NONE
= "none";
60 public String USER
= "";
61 public String PASSWORD
= "";
62 public String
[] ADD_PATHS
= null;
64 private String myConfigurationDirectory
;
65 private boolean myIsUseDefaultConfiguration
;
66 private boolean myIsUseDefaultProxy
;
67 private ISVNOptions myOptions
;
68 private boolean myIsKeepLocks
;
69 private boolean myRemoteStatus
;
70 private final Project myProject
;
71 private SvnAuthenticationManager myAuthManager
;
72 private SvnAuthenticationManager myPassiveAuthManager
;
73 private SvnAuthenticationManager myInteractiveManager
;
74 private String myUpgradeMode
;
75 private SvnSupportOptions mySupportOptions
;
77 public static final AuthStorage RUNTIME_AUTH_CACHE
= new AuthStorage();
78 public String LAST_MERGED_REVISION
= null;
79 public boolean UPDATE_RUN_STATUS
= false;
80 public SVNDepth UPDATE_DEPTH
= SVNDepth
.INFINITY
;
82 public boolean MERGE_DRY_RUN
= false;
83 public boolean MERGE_DIFF_USE_ANCESTRY
= true;
84 public boolean UPDATE_LOCK_ON_DEMAND
= false;
85 public boolean IGNORE_SPACES_IN_MERGE
= false;
86 public boolean DETECT_NESTED_COPIES
= false;
87 public boolean IGNORE_SPACES_IN_ANNOTATE
= true;
88 public boolean SHOW_MERGE_SOURCES_IN_ANNOTATE
= true;
90 private final Map
<File
, MergeRootInfo
> myMergeRootInfos
= new HashMap
<File
, MergeRootInfo
>();
91 private final Map
<File
, UpdateRootInfo
> myUpdateRootInfos
= new HashMap
<File
, UpdateRootInfo
>();
92 private final List
<AnnotationListener
> myAnnotationListeners
;
94 public static SvnConfiguration
getInstance(Project project
) {
95 return project
.getComponent(SvnConfiguration
.class);
98 public static SvnConfiguration
getInstanceChecked(final Project project
) {
99 return ApplicationManager
.getApplication().runReadAction(new Computable
<SvnConfiguration
>() {
100 public SvnConfiguration
compute() {
101 if (project
.isDisposed()) throw new ProcessCanceledException();
102 return project
.getComponent(SvnConfiguration
.class);
107 public SvnConfiguration(final Project project
) {
109 myAnnotationListeners
= new ArrayList
<AnnotationListener
>();
113 public void addAnnotationListener(final AnnotationListener listener
) {
114 myAnnotationListeners
.add(listener
);
117 public void removeAnnotationListener(final AnnotationListener listener
) {
118 myAnnotationListeners
.remove(listener
);
121 public void setIgnoreSpacesInAnnotate(final boolean value
) {
122 final boolean changed
= IGNORE_SPACES_IN_ANNOTATE
!= value
;
123 IGNORE_SPACES_IN_ANNOTATE
= value
;
125 fireForAnnotationListeners();
129 private void fireForAnnotationListeners() {
130 final AnnotationListener
[] listeners
= myAnnotationListeners
.toArray(new AnnotationListener
[myAnnotationListeners
.size()]);
131 ApplicationManager
.getApplication().invokeLater(new Runnable() {
133 for (int i
= 0; i
< listeners
.length
; i
++) {
134 final AnnotationListener listener
= listeners
[i
];
135 listener
.onAnnotationChanged();
138 }, ModalityState
.NON_MODAL
);
141 public class SvnSupportOptions
{
143 * version of "support SVN in IDEA". for features tracking. should grow
145 private Long myVersion
;
147 public SvnSupportOptions(final Long version
) {
149 // will be set to SvnSupportOptions.CHANGELIST_SUPPORT after sync
150 if (myVersion
== null || myVersion
.longValue() < SvnSupportOptions
.CHANGELIST_SUPPORT
) {
151 myVersion
= SvnSupportOptions
.UPGRADE_TO_15_VERSION_ASKED
;
155 private final static long UPGRADE_TO_15_VERSION_ASKED
= 123;
156 private final static long CHANGELIST_SUPPORT
= 124;
157 private final static long UPGRADE_TO_16_VERSION_ASKED
= 125;
159 public boolean upgradeTo16Asked() {
160 return (myVersion
!= null) && (UPGRADE_TO_16_VERSION_ASKED
<= myVersion
);
163 public boolean changeListsSynchronized() {
164 return (myVersion
!= null) && (CHANGELIST_SUPPORT
<= myVersion
);
167 public void upgrade() {
168 myVersion
= UPGRADE_TO_16_VERSION_ASKED
;
172 public SvnSupportOptions
getSupportOptions() {
173 if (mySupportOptions
== null) {
174 // used to be kept in SvnBranchConfigurationManager
175 mySupportOptions
= new SvnSupportOptions(SvnBranchConfigurationManager
.getInstance(myProject
).getSupportValue());
177 return mySupportOptions
;
180 public String
getConfigurationDirectory() {
181 if (myConfigurationDirectory
== null || isUseDefaultConfiguation()) {
182 myConfigurationDirectory
= SVNWCUtil
.getDefaultConfigurationDirectory().getAbsolutePath();
184 return myConfigurationDirectory
;
187 public boolean isUseDefaultConfiguation() {
188 return myIsUseDefaultConfiguration
;
191 public void setConfigurationDirectory(String path
) {
192 myConfigurationDirectory
= path
;
193 File dir
= path
== null ? SVNWCUtil
.getDefaultConfigurationDirectory() : new File(path
);
194 SVNConfigFile
.createDefaultConfiguration(dir
);
197 myAuthManager
= null;
198 RUNTIME_AUTH_CACHE
.clear();
201 public void setUseDefaultConfiguation(boolean useDefault
) {
202 myIsUseDefaultConfiguration
= useDefault
;
204 myAuthManager
= null;
205 RUNTIME_AUTH_CACHE
.clear();
208 public ISVNOptions
getOptions(Project project
) {
209 if (myOptions
== null) {
210 File path
= new File(getConfigurationDirectory());
211 myOptions
= SVNWCUtil
.createDefaultOptions(path
.getAbsoluteFile(), true);
216 public ISVNAuthenticationManager
getAuthenticationManager(final SvnVcs svnVcs
) {
217 if (myAuthManager
== null) {
218 // reloaded when configuration directory changes
219 myAuthManager
= new SvnAuthenticationManager(myProject
, new File(getConfigurationDirectory()));
220 myAuthManager
.setAuthenticationProvider(new SvnAuthenticationProvider(svnVcs
));
221 myAuthManager
.setRuntimeStorage(RUNTIME_AUTH_CACHE
);
223 return myAuthManager
;
226 public ISVNAuthenticationManager
getPassiveAuthenticationManager() {
227 if (myPassiveAuthManager
== null) {
228 myPassiveAuthManager
= new SvnAuthenticationManager(myProject
, new File(getConfigurationDirectory()));
229 myPassiveAuthManager
.setRuntimeStorage(RUNTIME_AUTH_CACHE
);
231 return myPassiveAuthManager
;
234 public ISVNAuthenticationManager
getInteractiveManager(final SvnVcs svnVcs
) {
235 if (myInteractiveManager
== null) {
236 myInteractiveManager
= new SvnAuthenticationManager(myProject
, new File(getConfigurationDirectory()));
237 myInteractiveManager
.setRuntimeStorage(RUNTIME_AUTH_CACHE
);
238 myInteractiveManager
.setAuthenticationProvider(new SvnInteractiveAuthenticationProvider(svnVcs
));
240 return myInteractiveManager
;
243 public void getServerFilesManagers(final Ref
<SvnServerFileManager
> systemManager
, final Ref
<SvnServerFileManager
> userManager
) {
244 // created only if does not exist
245 SVNConfigFile
.createDefaultConfiguration(new File(getConfigurationDirectory()));
247 systemManager
.set(new SvnServerFileManagerImpl(new IdeaSVNConfigFile(new File(SVNFileUtil
.getSystemConfigurationDirectory(), SERVERS_FILE_NAME
))));
248 userManager
.set(new SvnServerFileManagerImpl(new IdeaSVNConfigFile(new File(getConfigurationDirectory(), SERVERS_FILE_NAME
))));
251 public String
getUpgradeMode() {
252 return myUpgradeMode
;
255 public void setUpgradeMode(String upgradeMode
) {
256 myUpgradeMode
= upgradeMode
;
259 @SuppressWarnings({"HardCodedStringLiteral"})
260 public void readExternal(Element element
) throws InvalidDataException
{
261 DefaultJDOMExternalizer
.readExternal(this, element
);
262 List elems
= element
.getChildren("addpath");
263 LOG
.debug(elems
.toString());
264 ADD_PATHS
= new String
[elems
.size()];
265 for (int i
= 0; i
< elems
.size(); i
++) {
266 Element elem
= (Element
)elems
.get(i
);
267 ADD_PATHS
[i
] = elem
.getAttributeValue("path");
269 Element configurationDirectory
= element
.getChild("configuration");
270 if (configurationDirectory
!= null) {
271 myConfigurationDirectory
= configurationDirectory
.getText();
272 Attribute defaultAttr
= configurationDirectory
.getAttribute("useDefault");
274 myIsUseDefaultConfiguration
= defaultAttr
!= null && defaultAttr
.getBooleanValue();
276 catch (DataConversionException e
) {
277 myIsUseDefaultConfiguration
= false;
281 myIsUseDefaultConfiguration
= true;
283 // compatibility: this setting was moved from .iws to global settings
284 List urls
= element
.getChildren("checkoutURL");
285 for (Object url1
: urls
) {
286 Element child
= (Element
)url1
;
287 String url
= child
.getText();
289 SvnApplicationSettings
.getInstance().addCheckoutURL(url
);
292 myIsKeepLocks
= element
.getChild("keepLocks") != null;
293 myRemoteStatus
= element
.getChild("remoteStatus") != null;
294 myUpgradeMode
= element
.getChild("upgradeMode") != null ? element
.getChild("upgradeMode").getText() : null;
295 final Element useProxy
= element
.getChild("myIsUseDefaultProxy");
296 if (useProxy
== null) {
297 myIsUseDefaultProxy
= false;
299 myIsUseDefaultProxy
= Boolean
.parseBoolean(useProxy
.getText());
301 final Element supportedVersion
= element
.getChild("supportedVersion");
302 if (supportedVersion
!= null) {
303 mySupportOptions
= new SvnSupportOptions(Long
.parseLong(supportedVersion
.getText()));
307 @SuppressWarnings({"HardCodedStringLiteral"})
308 public void writeExternal(Element element
) throws WriteExternalException
{
309 DefaultJDOMExternalizer
.writeExternal(this, element
);
310 if (ADD_PATHS
!= null) {
311 for (String aADD_PATHS
: ADD_PATHS
) {
312 Element elem
= new Element("addpath");
313 elem
.setAttribute("path", aADD_PATHS
);
314 element
.addContent(elem
);
317 if (myConfigurationDirectory
!= null) {
318 Element configurationDirectory
= new Element("configuration");
319 configurationDirectory
.setText(myConfigurationDirectory
);
320 configurationDirectory
.setAttribute("useDefault", myIsUseDefaultConfiguration ?
"true" : "false");
321 element
.addContent(configurationDirectory
);
324 element
.addContent(new Element("keepLocks"));
326 if (myRemoteStatus
) {
327 element
.addContent(new Element("remoteStatus"));
329 if (myUpgradeMode
!= null) {
330 element
.addContent(new Element("upgradeMode").setText(myUpgradeMode
));
332 element
.addContent(new Element("myIsUseDefaultProxy").setText(myIsUseDefaultProxy ?
"true" : "false"));
333 if (mySupportOptions
!= null) {
334 element
.addContent(new Element("supportedVersion").setText("" + mySupportOptions
.myVersion
));
338 public void projectOpened() {
341 public void projectClosed() {
344 public String
getComponentName() {
345 return "SvnConfiguration";
348 public void initComponent() {
351 public void disposeComponent() {
354 public boolean isKeepLocks() {
355 return myIsKeepLocks
;
358 public void setKeepLocks(boolean keepLocks
) {
359 myIsKeepLocks
= keepLocks
;
362 public boolean isRemoteStatus() {
363 return myRemoteStatus
;
366 public void setRemoteStatus(boolean remote
) {
367 myRemoteStatus
= remote
;
370 public boolean isIsUseDefaultProxy() {
371 return myIsUseDefaultProxy
;
374 public void setIsUseDefaultProxy(final boolean isUseDefaultProxy
) {
375 myIsUseDefaultProxy
= isUseDefaultProxy
;
378 public static class AuthStorage
implements ISVNAuthenticationStorage
{
380 private final Map
<String
, Object
> myStorage
= new Hashtable
<String
, Object
>();
382 public void clear() {
386 public void putData(String kind
, String realm
, Object data
) {
388 myStorage
.remove(kind
+ "$" + realm
);
390 myStorage
.put(kind
+ "$" + realm
, data
);
394 public Object
getData(String kind
, String realm
) {
395 return myStorage
.get(kind
+ "$" + realm
);
399 public MergeRootInfo
getMergeRootInfo(final File file
, final SvnVcs svnVcs
) {
400 if (!myMergeRootInfos
.containsKey(file
)) {
401 myMergeRootInfos
.put(file
, new MergeRootInfo(file
, svnVcs
));
403 return myMergeRootInfos
.get(file
);
406 public UpdateRootInfo
getUpdateRootInfo(File file
, final SvnVcs svnVcs
) {
407 if (!myUpdateRootInfos
.containsKey(file
)) {
408 myUpdateRootInfos
.put(file
, new UpdateRootInfo(file
, svnVcs
));
410 return myUpdateRootInfos
.get(file
);
413 public Map
<File
, UpdateRootInfo
> getUpdateInfosMap() {
414 return Collections
.unmodifiableMap(myUpdateRootInfos
);
417 private static final List
<String
> ourAuthKinds
= Arrays
.asList(ISVNAuthenticationManager
.PASSWORD
, ISVNAuthenticationManager
.SSH
,
418 ISVNAuthenticationManager
.SSL
, ISVNAuthenticationManager
.USERNAME
, "svn.ssl.server");
420 public void clearAuthenticationDirectory() {
421 final File authDir
= new File(getConfigurationDirectory(), "auth");
422 if (authDir
.exists()) {
423 ProgressManager
.getInstance().runProcessWithProgressSynchronously(new Runnable() {
425 final ProgressIndicator ind
= ProgressManager
.getInstance().getProgressIndicator();
427 ind
.setIndeterminate(true);
428 ind
.setText("Clearing stored credentials in " + authDir
.getAbsolutePath());
430 final File
[] files
= authDir
.listFiles(new FilenameFilter() {
431 public boolean accept(File dir
, String name
) {
432 return ourAuthKinds
.contains(name
);
436 for (File dir
: files
) {
438 ind
.setText("Deleting " + dir
.getAbsolutePath());
440 FileUtil
.delete(dir
);
443 }, "button.text.clear.authentication.cache", false, myProject
);
447 public boolean haveCredentialsFor(final String kind
, final String realm
) {
448 return RUNTIME_AUTH_CACHE
.getData(kind
, realm
) != null;
451 public void acknowledge(final String kind
, final String realm
, final Object object
) {
452 RUNTIME_AUTH_CACHE
.putData(kind
, realm
, object
);
455 public void clearCredentials(final String kind
, final String realm
) {
456 RUNTIME_AUTH_CACHE
.putData(kind
, realm
, null);