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
.update
.MergeRootInfo
;
36 import org
.jetbrains
.idea
.svn
.update
.UpdateRootInfo
;
37 import org
.tmatesoft
.svn
.core
.SVNDepth
;
38 import org
.tmatesoft
.svn
.core
.auth
.ISVNAuthenticationManager
;
39 import org
.tmatesoft
.svn
.core
.internal
.wc
.ISVNAuthenticationStorage
;
40 import org
.tmatesoft
.svn
.core
.internal
.wc
.SVNConfigFile
;
41 import org
.tmatesoft
.svn
.core
.internal
.wc
.SVNFileUtil
;
42 import org
.tmatesoft
.svn
.core
.wc
.ISVNOptions
;
43 import org
.tmatesoft
.svn
.core
.wc
.SVNWCUtil
;
46 import java
.io
.FilenameFilter
;
49 public class SvnConfiguration
implements ProjectComponent
, JDOMExternalizable
{
50 private static final Logger LOG
= Logger
.getInstance("org.jetbrains.idea.svn.SvnConfiguration");
52 private final static String SERVERS_FILE_NAME
= "servers";
54 public static final String UPGRADE_AUTO
= "auto";
55 public static final String UPGRADE_AUTO_15
= "auto1.5";
56 public static final String UPGRADE_AUTO_16
= "auto1.6";
57 public static final String UPGRADE_NONE
= "none";
59 public String USER
= "";
60 public String PASSWORD
= "";
61 public String
[] ADD_PATHS
= null;
63 private String myConfigurationDirectory
;
64 private boolean myIsUseDefaultConfiguration
;
65 private boolean myIsUseDefaultProxy
;
66 private ISVNOptions myOptions
;
67 private boolean myIsKeepLocks
;
68 private boolean myRemoteStatus
;
69 private final Project myProject
;
70 private SvnAuthenticationManager myAuthManager
;
71 private String myUpgradeMode
;
72 private SvnSupportOptions mySupportOptions
;
74 public static final AuthStorage RUNTIME_AUTH_CACHE
= new AuthStorage();
75 public String LAST_MERGED_REVISION
= null;
76 public boolean UPDATE_RUN_STATUS
= false;
77 public SVNDepth UPDATE_DEPTH
= SVNDepth
.INFINITY
;
79 public boolean MERGE_DRY_RUN
= false;
80 public boolean MERGE_DIFF_USE_ANCESTRY
= true;
81 public boolean UPDATE_LOCK_ON_DEMAND
= false;
82 public boolean IGNORE_SPACES_IN_MERGE
= false;
83 public boolean DETECT_NESTED_COPIES
= false;
84 public boolean IGNORE_SPACES_IN_ANNOTATE
= true;
85 public boolean SHOW_MERGE_SOURCES_IN_ANNOTATE
= true;
87 private final Map
<File
, MergeRootInfo
> myMergeRootInfos
= new HashMap
<File
, MergeRootInfo
>();
88 private final Map
<File
, UpdateRootInfo
> myUpdateRootInfos
= new HashMap
<File
, UpdateRootInfo
>();
89 private final List
<AnnotationListener
> myAnnotationListeners
;
91 public static SvnConfiguration
getInstance(Project project
) {
92 return project
.getComponent(SvnConfiguration
.class);
95 public static SvnConfiguration
getInstanceChecked(final Project project
) {
96 return ApplicationManager
.getApplication().runReadAction(new Computable
<SvnConfiguration
>() {
97 public SvnConfiguration
compute() {
98 if (project
.isDisposed()) throw new ProcessCanceledException();
99 return project
.getComponent(SvnConfiguration
.class);
104 public SvnConfiguration(final Project project
) {
106 myAnnotationListeners
= new ArrayList
<AnnotationListener
>();
110 public void addAnnotationListener(final AnnotationListener listener
) {
111 myAnnotationListeners
.add(listener
);
114 public void removeAnnotationListener(final AnnotationListener listener
) {
115 myAnnotationListeners
.remove(listener
);
118 public void setIgnoreSpacesInAnnotate(final boolean value
) {
119 final boolean changed
= IGNORE_SPACES_IN_ANNOTATE
!= value
;
120 IGNORE_SPACES_IN_ANNOTATE
= value
;
122 fireForAnnotationListeners();
126 private void fireForAnnotationListeners() {
127 final AnnotationListener
[] listeners
= myAnnotationListeners
.toArray(new AnnotationListener
[myAnnotationListeners
.size()]);
128 ApplicationManager
.getApplication().invokeLater(new Runnable() {
130 for (int i
= 0; i
< listeners
.length
; i
++) {
131 final AnnotationListener listener
= listeners
[i
];
132 listener
.onAnnotationChanged();
135 }, ModalityState
.NON_MODAL
);
138 public class SvnSupportOptions
{
140 * version of "support SVN in IDEA". for features tracking. should grow
142 private Long myVersion
;
144 public SvnSupportOptions(final Long version
) {
146 // will be set to SvnSupportOptions.CHANGELIST_SUPPORT after sync
147 if (myVersion
== null || myVersion
.longValue() < SvnSupportOptions
.CHANGELIST_SUPPORT
) {
148 myVersion
= SvnSupportOptions
.UPGRADE_TO_15_VERSION_ASKED
;
152 private final static long UPGRADE_TO_15_VERSION_ASKED
= 123;
153 private final static long CHANGELIST_SUPPORT
= 124;
154 private final static long UPGRADE_TO_16_VERSION_ASKED
= 125;
156 public boolean upgradeTo16Asked() {
157 return (myVersion
!= null) && (UPGRADE_TO_16_VERSION_ASKED
<= myVersion
);
160 public boolean changeListsSynchronized() {
161 return (myVersion
!= null) && (CHANGELIST_SUPPORT
<= myVersion
);
164 public void upgrade() {
165 myVersion
= UPGRADE_TO_16_VERSION_ASKED
;
169 public SvnSupportOptions
getSupportOptions() {
170 if (mySupportOptions
== null) {
171 // used to be kept in SvnBranchConfigurationManager
172 mySupportOptions
= new SvnSupportOptions(SvnBranchConfigurationManager
.getInstance(myProject
).getSupportValue());
174 return mySupportOptions
;
177 public String
getConfigurationDirectory() {
178 if (myConfigurationDirectory
== null || isUseDefaultConfiguation()) {
179 myConfigurationDirectory
= SVNWCUtil
.getDefaultConfigurationDirectory().getAbsolutePath();
181 return myConfigurationDirectory
;
184 public boolean isUseDefaultConfiguation() {
185 return myIsUseDefaultConfiguration
;
188 public void setConfigurationDirectory(String path
) {
189 myConfigurationDirectory
= path
;
190 File dir
= path
== null ? SVNWCUtil
.getDefaultConfigurationDirectory() : new File(path
);
191 SVNConfigFile
.createDefaultConfiguration(dir
);
194 myAuthManager
= null;
195 RUNTIME_AUTH_CACHE
.clear();
198 public void setUseDefaultConfiguation(boolean useDefault
) {
199 myIsUseDefaultConfiguration
= useDefault
;
201 myAuthManager
= null;
202 RUNTIME_AUTH_CACHE
.clear();
205 public ISVNOptions
getOptions(Project project
) {
206 if (myOptions
== null) {
207 File path
= new File(getConfigurationDirectory());
208 myOptions
= SVNWCUtil
.createDefaultOptions(path
.getAbsoluteFile(), true);
213 public ISVNAuthenticationManager
getAuthenticationManager(final SvnVcs svnVcs
) {
214 if (myAuthManager
== null) {
215 // reloaded when configuration directory changes
216 myAuthManager
= new SvnAuthenticationManager(myProject
, new File(getConfigurationDirectory()));
217 myAuthManager
.setAuthenticationProvider(new SvnAuthenticationProvider(svnVcs
));
218 myAuthManager
.setRuntimeStorage(RUNTIME_AUTH_CACHE
);
220 return myAuthManager
;
223 public void getServerFilesManagers(final Ref
<SvnServerFileManager
> systemManager
, final Ref
<SvnServerFileManager
> userManager
) {
224 // created only if does not exist
225 SVNConfigFile
.createDefaultConfiguration(new File(getConfigurationDirectory()));
227 systemManager
.set(new SvnServerFileManagerImpl(new IdeaSVNConfigFile(new File(SVNFileUtil
.getSystemConfigurationDirectory(), SERVERS_FILE_NAME
))));
228 userManager
.set(new SvnServerFileManagerImpl(new IdeaSVNConfigFile(new File(getConfigurationDirectory(), SERVERS_FILE_NAME
))));
231 public String
getUpgradeMode() {
232 return myUpgradeMode
;
235 public void setUpgradeMode(String upgradeMode
) {
236 myUpgradeMode
= upgradeMode
;
239 @SuppressWarnings({"HardCodedStringLiteral"})
240 public void readExternal(Element element
) throws InvalidDataException
{
241 DefaultJDOMExternalizer
.readExternal(this, element
);
242 List elems
= element
.getChildren("addpath");
243 LOG
.debug(elems
.toString());
244 ADD_PATHS
= new String
[elems
.size()];
245 for (int i
= 0; i
< elems
.size(); i
++) {
246 Element elem
= (Element
)elems
.get(i
);
247 ADD_PATHS
[i
] = elem
.getAttributeValue("path");
249 Element configurationDirectory
= element
.getChild("configuration");
250 if (configurationDirectory
!= null) {
251 myConfigurationDirectory
= configurationDirectory
.getText();
252 Attribute defaultAttr
= configurationDirectory
.getAttribute("useDefault");
254 myIsUseDefaultConfiguration
= defaultAttr
!= null && defaultAttr
.getBooleanValue();
256 catch (DataConversionException e
) {
257 myIsUseDefaultConfiguration
= false;
261 myIsUseDefaultConfiguration
= true;
263 // compatibility: this setting was moved from .iws to global settings
264 List urls
= element
.getChildren("checkoutURL");
265 for (Object url1
: urls
) {
266 Element child
= (Element
)url1
;
267 String url
= child
.getText();
269 SvnApplicationSettings
.getInstance().addCheckoutURL(url
);
272 myIsKeepLocks
= element
.getChild("keepLocks") != null;
273 myRemoteStatus
= element
.getChild("remoteStatus") != null;
274 myUpgradeMode
= element
.getChild("upgradeMode") != null ? element
.getChild("upgradeMode").getText() : null;
275 final Element useProxy
= element
.getChild("myIsUseDefaultProxy");
276 if (useProxy
== null) {
277 myIsUseDefaultProxy
= false;
279 myIsUseDefaultProxy
= Boolean
.parseBoolean(useProxy
.getText());
281 final Element supportedVersion
= element
.getChild("supportedVersion");
282 if (supportedVersion
!= null) {
283 mySupportOptions
= new SvnSupportOptions(Long
.parseLong(supportedVersion
.getText()));
287 @SuppressWarnings({"HardCodedStringLiteral"})
288 public void writeExternal(Element element
) throws WriteExternalException
{
289 DefaultJDOMExternalizer
.writeExternal(this, element
);
290 if (ADD_PATHS
!= null) {
291 for (String aADD_PATHS
: ADD_PATHS
) {
292 Element elem
= new Element("addpath");
293 elem
.setAttribute("path", aADD_PATHS
);
294 element
.addContent(elem
);
297 if (myConfigurationDirectory
!= null) {
298 Element configurationDirectory
= new Element("configuration");
299 configurationDirectory
.setText(myConfigurationDirectory
);
300 configurationDirectory
.setAttribute("useDefault", myIsUseDefaultConfiguration ?
"true" : "false");
301 element
.addContent(configurationDirectory
);
304 element
.addContent(new Element("keepLocks"));
306 if (myRemoteStatus
) {
307 element
.addContent(new Element("remoteStatus"));
309 if (myUpgradeMode
!= null) {
310 element
.addContent(new Element("upgradeMode").setText(myUpgradeMode
));
312 element
.addContent(new Element("myIsUseDefaultProxy").setText(myIsUseDefaultProxy ?
"true" : "false"));
313 if (mySupportOptions
!= null) {
314 element
.addContent(new Element("supportedVersion").setText("" + mySupportOptions
.myVersion
));
318 public void projectOpened() {
321 public void projectClosed() {
324 public String
getComponentName() {
325 return "SvnConfiguration";
328 public void initComponent() {
331 public void disposeComponent() {
334 public boolean isKeepLocks() {
335 return myIsKeepLocks
;
338 public void setKeepLocks(boolean keepLocks
) {
339 myIsKeepLocks
= keepLocks
;
342 public boolean isRemoteStatus() {
343 return myRemoteStatus
;
346 public void setRemoteStatus(boolean remote
) {
347 myRemoteStatus
= remote
;
350 public boolean isIsUseDefaultProxy() {
351 return myIsUseDefaultProxy
;
354 public void setIsUseDefaultProxy(final boolean isUseDefaultProxy
) {
355 myIsUseDefaultProxy
= isUseDefaultProxy
;
358 public static class AuthStorage
implements ISVNAuthenticationStorage
{
360 private final Map
<String
, Object
> myStorage
= new Hashtable
<String
, Object
>();
362 public void clear() {
366 public void putData(String kind
, String realm
, Object data
) {
368 myStorage
.remove(kind
+ "$" + realm
);
370 myStorage
.put(kind
+ "$" + realm
, data
);
374 public Object
getData(String kind
, String realm
) {
375 return myStorage
.get(kind
+ "$" + realm
);
379 public MergeRootInfo
getMergeRootInfo(final File file
, final SvnVcs svnVcs
) {
380 if (!myMergeRootInfos
.containsKey(file
)) {
381 myMergeRootInfos
.put(file
, new MergeRootInfo(file
, svnVcs
));
383 return myMergeRootInfos
.get(file
);
386 public UpdateRootInfo
getUpdateRootInfo(File file
, final SvnVcs svnVcs
) {
387 if (!myUpdateRootInfos
.containsKey(file
)) {
388 myUpdateRootInfos
.put(file
, new UpdateRootInfo(file
, svnVcs
));
390 return myUpdateRootInfos
.get(file
);
393 public Map
<File
, UpdateRootInfo
> getUpdateInfosMap() {
394 return Collections
.unmodifiableMap(myUpdateRootInfos
);
397 private static final List
<String
> ourAuthKinds
= Arrays
.asList(ISVNAuthenticationManager
.PASSWORD
, ISVNAuthenticationManager
.SSH
,
398 ISVNAuthenticationManager
.SSL
, ISVNAuthenticationManager
.USERNAME
, "svn.ssl.server");
400 public void clearAuthenticationDirectory() {
401 final File authDir
= new File(getConfigurationDirectory(), "auth");
402 if (authDir
.exists()) {
403 ProgressManager
.getInstance().runProcessWithProgressSynchronously(new Runnable() {
405 final ProgressIndicator ind
= ProgressManager
.getInstance().getProgressIndicator();
407 ind
.setIndeterminate(true);
408 ind
.setText("Clearing stored credentials in " + authDir
.getAbsolutePath());
410 final File
[] files
= authDir
.listFiles(new FilenameFilter() {
411 public boolean accept(File dir
, String name
) {
412 return ourAuthKinds
.contains(name
);
416 for (File dir
: files
) {
418 ind
.setText("Deleting " + dir
.getAbsolutePath());
420 FileUtil
.delete(dir
);
423 }, "button.text.clear.authentication.cache", false, myProject
);