diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2013-04-01 14:41:51 -0700 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2013-04-01 14:41:51 -0700 |
commit | 2bd2b7c2623d4266384e890271869efc044aabff (patch) | |
tree | 0b31f50e55975b6354ed458314e17b4441bb4e17 /plugins/svn4idea/bindSvn/src/org/jetbrains/idea | |
parent | 1d526b16d476792ca7ce47616d55833115e8d6ab (diff) | |
download | idea-2bd2b7c2623d4266384e890271869efc044aabff.tar.gz |
Snapshot ee98b298267d0e09d2cd2f0731b6480a56dd48e7 from master branch of git://git.jetbrains.org/idea/community.git
Change-Id: I4515f72af131fdea9fc6905a4dc0fe9532409a81
Diffstat (limited to 'plugins/svn4idea/bindSvn/src/org/jetbrains/idea')
13 files changed, 2438 insertions, 0 deletions
diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/AuthenticationCallback.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/AuthenticationCallback.java new file mode 100644 index 000000000000..0d3314f1ece5 --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/AuthenticationCallback.java @@ -0,0 +1,109 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn; + +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; + +/** + * Passed for authentication purpose to SvnLineCommand + * Called when svn command indicates that it needs credentials. It also means that credential was not found in standard place + * (Subversion config directory) + * + * Implementations should 1) ask credential from user or take it from any other storage (memory, for instance) + * 2) write credential in Subversion standard form into + * a) standard config directory if user allowed to save *all* credentials + * b) TMP directory and return path to the directory from getSpecialConfigDir() - if user rejected at least one credential storing + * + * Please note that several credentials could be asked during the command and therefore implementation class is used as + * keeping its state, and that TMP directory should be reused for all written credentials + * + * User: Irina.Chernushina + * Date: 2/26/13 + * Time: 1:05 PM + */ +public interface AuthenticationCallback { + /** + * Authenticate for realm and base file belonging to corresponding working copy + * + * @param realm - realm that should be used for credential retrieval/storage. + * @param base - file target of the operation + * @param previousFailed - whether previous credentials were correct + * @param passwordRequest - if true, password should be asked. Otherwise that may be a certificate (determined by the protocol) + * @return false if authentication canceled or was unsuccessful + */ + boolean authenticateFor(@Nullable String realm, File base, boolean previousFailed, boolean passwordRequest); + + /** + * @return config directory if TMP was created + */ + @Nullable + File getSpecialConfigDir(); + + /** + * Ask user or read from memory storage whether server certificate should be accepted + * + * @param url - that we used for request + * @param realm - realm that should be used for credential retrieval/storage. + * @return true is certificate was accepted + */ + boolean acceptSSLServerCertificate(String url, final String realm); + + /** + * Ask user or read from memory storage whether server certificate should be accepted + * + * @param file - that we used for request + * @param realm - realm that should be used for credential retrieval/storage. + * @return true is certificate was accepted + */ + boolean acceptSSLServerCertificate(File file, final String realm); + + /** + * Clear credentials stored anywhere - in case they were not full, wrong or anything else + * + * @param realm - required that credential + * @param base - file used in command + * @param password - whether password credential should be deleted or certificate, if protocol might demand certificate + */ + void clearPassiveCredentials(String realm, File base, boolean password); + + /** + * @return true if there's something from IDEA config that should be persisted into Subversion tmp config directory + * for successful call + * (now it's IDEA proxy settings) + */ + boolean haveDataForTmpConfig(); + + /** + * writes IDEA config settings (that should be written) into tmp config directory + * (now it's IDEA proxy settings) + * @return true if have written data, false if wasn't able to determine parameters etc + * @throws IOException + * @throws URISyntaxException + */ + boolean persistDataToTmpConfig(File baseFile) throws IOException, URISyntaxException; + + /** + * Ask for IDEA-defined proxy credentials, using standard authenticator + * Store data into tmp config + * + * @return false if authentication was canceled or related calculations were unsuccessful + */ + boolean askProxyCredentials(File base); +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventHandler.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventHandler.java new file mode 100644 index 000000000000..7f3bcf3bf4a5 --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventHandler.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn; + +import java.io.File; + +/** + * Used to listen to commit events to display progress to user + * + * User: Irina.Chernushina + * Date: 2/26/13 + * Time: 10:12 AM + */ +public interface CommitEventHandler { + void commitEvent(final CommitEventType type, final File target); + void committedRevision(final long revNum); +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventType.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventType.java new file mode 100644 index 000000000000..46609e40532c --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventType.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/25/13 + * Time: 6:51 PM + */ +public enum CommitEventType { + adding("Adding"), + deleting("Deleting"), + sending("Sending"), + replacing("Replacing"), + transmittingDeltas("Transmitting file data"), + committedRevision("Committed revision"); + + private final String myText; + + CommitEventType(String text) { + myText = text; + } + + public String getText() { + return myText; + } + + public static CommitEventType create(String text) { + text = text.trim(); + for (CommitEventType value : CommitEventType.values()) { + if (value.getText().equals(text)) return value; + } + return null; + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java new file mode 100644 index 000000000000..c155b78ddc53 --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java @@ -0,0 +1,925 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn; + +import org.tigris.subversion.javahl.*; + +import java.io.OutputStream; +import java.util.Map; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/5/13 + * Time: 3:08 PM + */ +public class SvnBindClient implements SVNClientInterface { + private final String myExecutablePath; + private CommitEventHandler myHandler; + private AuthenticationCallback myAuthenticationCallback; + + public SvnBindClient(String path) { + myExecutablePath = path; + } + + @Override + public void dispose() { + } + + @Override + public Version getVersion() { + // todo real version + throw new UnsupportedOperationException(); + } + + @Override + public String getAdminDirectoryName() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isAdminDirectory(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public String getLastPath() { + throw new UnsupportedOperationException(); + } + + @Override + public Status singleStatus(String path, boolean onServer) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public Status[] status(String path, boolean descend, boolean onServer, boolean getAll) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public Status[] status(String path, boolean descend, boolean onServer, boolean getAll, boolean noIgnore) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public Status[] status(String path, boolean descend, boolean onServer, boolean getAll, boolean noIgnore, boolean ignoreExternals) + throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void status(String path, + int depth, + boolean onServer, + boolean getAll, + boolean noIgnore, + boolean ignoreExternals, + String[] changelists, + StatusCallback callback) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public DirEntry[] list(String url, Revision revision, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public DirEntry[] list(String url, Revision revision, Revision pegRevision, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void list(String url, + Revision revision, + Revision pegRevision, + int depth, + int direntFields, + boolean fetchLocks, + ListCallback callback) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void username(String username) { + throw new UnsupportedOperationException(); + } + + @Override + public void password(String password) { + throw new UnsupportedOperationException(); + } + + @Override + public void setPrompt(PromptUserPassword prompt) { + throw new UnsupportedOperationException(); + } + + @Override + public LogMessage[] logMessages(String path, Revision revisionStart, Revision revisionEnd) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public LogMessage[] logMessages(String path, Revision revisionStart, Revision revisionEnd, boolean stopOnCopy) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public LogMessage[] logMessages(String path, Revision revisionStart, Revision revisionEnd, boolean stopOnCopy, boolean discoverPath) + throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public LogMessage[] logMessages(String path, + Revision revisionStart, + Revision revisionEnd, + boolean stopOnCopy, + boolean discoverPath, + long limit) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void logMessages(String path, + Revision pegRevision, + Revision revisionStart, + Revision revisionEnd, + boolean stopOnCopy, + boolean discoverPath, + boolean includeMergedRevisions, + String[] revProps, + long limit, + LogMessageCallback callback) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void logMessages(String path, + Revision pegRevision, + RevisionRange[] ranges, + boolean stopOnCopy, + boolean discoverPath, + boolean includeMergedRevisions, + String[] revProps, + long limit, + LogMessageCallback callback) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long checkout(String moduleName, String destPath, Revision revision, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long checkout(String moduleName, + String destPath, + Revision revision, + Revision pegRevision, + boolean recurse, + boolean ignoreExternals) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long checkout(String moduleName, + String destPath, + Revision revision, + Revision pegRevision, + int depth, + boolean ignoreExternals, + boolean allowUnverObstructions) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void notification(Notify notify) { + throw new UnsupportedOperationException(); + } + + @Override + public void notification2(Notify2 notify) { + throw new UnsupportedOperationException(); + } + + @Override + public void setConflictResolver(ConflictResolverCallback listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void setProgressListener(ProgressListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void commitMessageHandler(CommitMessage messageHandler) { + throw new UnsupportedOperationException(); + } + + @Override + public void remove(String[] path, String message, boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void remove(String[] path, String message, boolean force, boolean keepLocal, Map revpropTable) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void revert(String path, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void revert(String path, int depth, String[] changelists) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void add(String path, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void add(String path, boolean recurse, boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void add(String path, int depth, boolean force, boolean noIgnores, boolean addParents) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long update(String path, Revision revision, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long[] update(String[] path, Revision revision, boolean recurse, boolean ignoreExternals) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long update(String path, + Revision revision, + int depth, + boolean depthIsSticky, + boolean ignoreExternals, + boolean allowUnverObstructions) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long[] update(String[] path, + Revision revision, + int depth, + boolean depthIsSticky, + boolean ignoreExternals, + boolean allowUnverObstructions) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long commit(String[] path, String message, boolean recurse) throws ClientException { + return commit(path, message, recurse? 3 : 0, false, false, null, null); + } + + @Override + public long commit(String[] path, String message, boolean recurse, boolean noUnlock) throws ClientException { + return commit(path, message, recurse? 3 : 0, noUnlock, false, null, null); + } + + @Override + public long commit(String[] path, + String message, + int depth, + boolean noUnlock, + boolean keepChangelist, + String[] changelists, + Map revpropTable) throws ClientException { + final long commit = new SvnCommitRunner(myExecutablePath, myHandler, myAuthenticationCallback). + commit(path, message, depth, noUnlock, keepChangelist, changelists, revpropTable); + if (commit < 0) { + throw new BindClientException("Wrong committed revision number: " + commit, null, -1); + } + return commit; + } + + @Override + public void copy(CopySource[] sources, + String destPath, + String message, + boolean copyAsChild, + boolean makeParents, + boolean ignoreExternals, + Map revpropTable) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void copy(CopySource[] sources, String destPath, String message, boolean copyAsChild, boolean makeParents, Map revpropTable) + throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void copy(String srcPath, String destPath, String message, Revision revision) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void move(String[] srcPaths, + String destPath, + String message, + boolean force, + boolean moveAsChild, + boolean makeParents, + Map revpropTable) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void move(String srcPath, String destPath, String message, Revision ignored, boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void move(String srcPath, String destPath, String message, boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void mkdir(String[] path, String message, boolean makeParents, Map revpropTable) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void mkdir(String[] path, String message) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void cleanup(String path) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void resolve(String path, int depth, int conflictResult) throws SubversionException { + throw new UnsupportedOperationException(); + } + + @Override + public void resolved(String path, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long doExport(String srcPath, String destPath, Revision revision, boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long doExport(String srcPath, + String destPath, + Revision revision, + Revision pegRevision, + boolean force, + boolean ignoreExternals, + boolean recurse, + String nativeEOL) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long doExport(String srcPath, + String destPath, + Revision revision, + Revision pegRevision, + boolean force, + boolean ignoreExternals, + int depth, + String nativeEOL) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long doSwitch(String path, String url, Revision revision, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public long doSwitch(String path, + String url, + Revision revision, + Revision pegRevision, + int depth, + boolean depthIsSticky, + boolean ignoreExternals, + boolean allowUnverObstructions) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void doImport(String path, String url, String message, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void doImport(String path, + String url, + String message, + int depth, + boolean noIgnore, + boolean ignoreUnknownNodeTypes, + Map revpropTable) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public String[] suggestMergeSources(String path, Revision pegRevision) throws SubversionException { + throw new UnsupportedOperationException(); + } + + @Override + public void merge(String path1, Revision revision1, String path2, Revision revision2, String localPath, boolean force, boolean recurse) + throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void merge(String path1, + Revision revision1, + String path2, + Revision revision2, + String localPath, + boolean force, + boolean recurse, + boolean ignoreAncestry, + boolean dryRun) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void merge(String path1, + Revision revision1, + String path2, + Revision revision2, + String localPath, + boolean force, + int depth, + boolean ignoreAncestry, + boolean dryRun, + boolean recordOnly) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void merge(String path, + Revision pegRevision, + Revision revision1, + Revision revision2, + String localPath, + boolean force, + boolean recurse, + boolean ignoreAncestry, + boolean dryRun) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void merge(String path, + Revision pegRevision, + RevisionRange[] revisions, + String localPath, + boolean force, + int depth, + boolean ignoreAncestry, + boolean dryRun, + boolean recordOnly) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void mergeReintegrate(String path, Revision pegRevision, String localPath, boolean dryRun) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public Mergeinfo getMergeinfo(String path, Revision pegRevision) throws SubversionException { + throw new UnsupportedOperationException(); + } + + @Override + public void getMergeinfoLog(int kind, + String pathOrUrl, + Revision pegRevision, + String mergeSourceUrl, + Revision srcPegRevision, + boolean discoverChangedPaths, + int depth, + String[] revProps, + LogMessageCallback callback) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void getMergeinfoLog(int kind, + String pathOrUrl, + Revision pegRevision, + String mergeSourceUrl, + Revision srcPegRevision, + boolean discoverChangedPaths, + String[] revProps, + LogMessageCallback callback) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void diff(String target1, Revision revision1, String target2, Revision revision2, String outFileName, boolean recurse) + throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void diff(String target1, + Revision revision1, + String target2, + Revision revision2, + String outFileName, + boolean recurse, + boolean ignoreAncestry, + boolean noDiffDeleted, + boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void diff(String target1, + Revision revision1, + String target2, + Revision revision2, + String relativeToDir, + String outFileName, + int depth, + String[] changelists, + boolean ignoreAncestry, + boolean noDiffDeleted, + boolean force, + boolean copiesAsAdds) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void diff(String target1, + Revision revision1, + String target2, + Revision revision2, + String relativeToDir, + String outFileName, + int depth, + String[] changelists, + boolean ignoreAncestry, + boolean noDiffDeleted, + boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void diff(String target, + Revision pegRevision, + Revision startRevision, + Revision endRevision, + String outFileName, + boolean recurse, + boolean ignoreAncestry, + boolean noDiffDeleted, + boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void diff(String target, + Revision pegRevision, + Revision startRevision, + Revision endRevision, + String relativeToDir, + String outFileName, + int depth, + String[] changelists, + boolean ignoreAncestry, + boolean noDiffDeleted, + boolean force, + boolean copiesAsAdds) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void diff(String target, + Revision pegRevision, + Revision startRevision, + Revision endRevision, + String relativeToDir, + String outFileName, + int depth, + String[] changelists, + boolean ignoreAncestry, + boolean noDiffDeleted, + boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void diffSummarize(String target1, + Revision revision1, + String target2, + Revision revision2, + int depth, + String[] changelists, + boolean ignoreAncestry, + DiffSummaryReceiver receiver) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void diffSummarize(String target, + Revision pegRevision, + Revision startRevision, + Revision endRevision, + int depth, + String[] changelists, + boolean ignoreAncestry, + DiffSummaryReceiver receiver) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public PropertyData[] properties(String path) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public PropertyData[] properties(String path, Revision revision) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public PropertyData[] properties(String path, Revision revision, Revision pegRevision) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void properties(String path, Revision revision, Revision pegRevision, int depth, String[] changelists, ProplistCallback callback) + throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void propertySet(String path, String name, String value, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void propertySet(String path, String name, String value, boolean recurse, boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void propertySet(String path, String name, byte[] value, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void propertySet(String path, String name, byte[] value, boolean recurse, boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void propertySet(String path, String name, String value, int depth, String[] changelists, boolean force, Map revpropTable) + throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void propertyRemove(String path, String name, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void propertyRemove(String path, String name, int depth, String[] changelists) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void propertyCreate(String path, String name, String value, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void propertyCreate(String path, String name, String value, boolean recurse, boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void propertyCreate(String path, String name, byte[] value, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void propertyCreate(String path, String name, byte[] value, boolean recurse, boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void propertyCreate(String path, String name, String value, int depth, String[] changelists, boolean force) + throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public PropertyData revProperty(String path, String name, Revision rev) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public PropertyData[] revProperties(String path, Revision rev) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void setRevProperty(String path, String name, Revision rev, String value, boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void setRevProperty(String path, String name, Revision rev, String value, String originalValue, boolean force) + throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public PropertyData propertyGet(String path, String name) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public PropertyData propertyGet(String path, String name, Revision revision) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public PropertyData propertyGet(String path, String name, Revision revision, Revision pegRevision) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public byte[] fileContent(String path, Revision revision) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public byte[] fileContent(String path, Revision revision, Revision pegRevision) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void streamFileContent(String path, Revision revision, Revision pegRevision, int bufferSize, OutputStream stream) + throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void relocate(String from, String to, String path, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public byte[] blame(String path, Revision revisionStart, Revision revisionEnd) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void blame(String path, Revision revisionStart, Revision revisionEnd, BlameCallback callback) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void blame(String path, Revision pegRevision, Revision revisionStart, Revision revisionEnd, BlameCallback callback) + throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void blame(String path, + Revision pegRevision, + Revision revisionStart, + Revision revisionEnd, + boolean ignoreMimeType, + boolean includeMergedRevisions, + BlameCallback2 callback) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void blame(String path, + Revision pegRevision, + Revision revisionStart, + Revision revisionEnd, + boolean ignoreMimeType, + boolean includeMergedRevisions, + BlameCallback3 callback) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void setConfigDirectory(String configDir) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public String getConfigDirectory() throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void cancelOperation() throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public Info info(String path) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void addToChangelist(String[] paths, String changelist, int depth, String[] changelists) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void removeFromChangelists(String[] paths, int depth, String[] changelists) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void getChangelists(String rootPath, String[] changelists, int depth, ChangelistCallback callback) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void lock(String[] path, String comment, boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void unlock(String[] path, boolean force) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public Info2[] info2(String pathOrUrl, Revision revision, Revision pegRevision, boolean recurse) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void info2(String pathOrUrl, Revision revision, Revision pegRevision, int depth, String[] changelists, InfoCallback callback) + throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public String getVersionInfo(String path, String trailUrl, boolean lastChanged) throws ClientException { + throw new UnsupportedOperationException(); + } + + @Override + public void upgrade(String path) throws ClientException { + throw new UnsupportedOperationException(); + } + + public void setHandler(CommitEventHandler handler) { + myHandler = handler; + } + + public void setAuthenticationCallback(AuthenticationCallback authenticationCallback) { + myAuthenticationCallback = authenticationCallback; + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindUtil.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindUtil.java new file mode 100644 index 000000000000..1fec6b3bf865 --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindUtil.java @@ -0,0 +1,104 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn; + +import java.io.File; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/5/13 + * Time: 4:56 PM + */ +public class SvnBindUtil { + /** + * SVN_ASP_DOT_NET_HACK allows use of an alternate name for Subversion working copy + * administrative directories on Windows (which were formerly always + * named ".svn"), by setting the SVN_ASP_DOT_NET_HACK environment variable. + * When the variable is set (to any value), the administrative directory + * will be "_svn" instead of ".svn". + * + * http://svn.apache.org/repos/asf/subversion/trunk/notes/asp-dot-net-hack.txt + */ + public static final String ADM_NAME = System.getenv("SVN_ASP_DOT_NET_HACK") != null ? "_svn" : ".svn"; + private final static List<DateFormat> ourFormats = new ArrayList<DateFormat>(); + + static { + ourFormats.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS")); + ourFormats.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'000Z'")); + ourFormats.add(new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US)); + ourFormats.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z (EE, d MMM yyyy)", Locale.getDefault())); + ourFormats.add(new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss' 'ZZZZ' ('E', 'dd' 'MMM' 'yyyy')'")); + ourFormats.add(new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss'Z'")); + ourFormats.add(new SimpleDateFormat("EEE' 'MMM' 'dd' 'HH:mm:ss' 'yyyy")); + ourFormats.add(new SimpleDateFormat("MM' 'dd' 'yyyy")); + ourFormats.add(new SimpleDateFormat("MM' 'dd' 'HH:mm")); + ourFormats.add(new SimpleDateFormat("MM' 'dd' 'HH:mm:ss")); + } + + public static Date parseDate(final String date) { + for (DateFormat format : ourFormats) { + try { + return format.parse(date); + } + catch (ParseException e) { + continue; + } + } + return new Date(0); + } + + public static void changelistsToCommand(String[] changeLists, final List<String> list) { + if (changeLists != null) { + for (String name : changeLists) { + list.add("--cl"); + list.add(name); + } + } + } + + public static String getDepthName(final int i) { + return org.tigris.subversion.javahl.Depth.toADepth(i).name(); + } + + public static File correctUpToExistingParent(File base) { + while (base != null) { + if (base.exists() && base.isDirectory()) return base; + base = base.getParentFile(); + } + return null; + } + + public static File getWcRoot(File base) { + File current = base; + while (current != null) { + if (getWcDbUnder(current).exists()) return current; + current = current.getParentFile(); + } + return null; + } + + public static File getWcDbUnder(final File file) { + return new File(file, ADM_NAME + File.separator + "wc.db"); + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnCommitRunner.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnCommitRunner.java new file mode 100644 index 000000000000..525d675303fc --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnCommitRunner.java @@ -0,0 +1,217 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn; + +import com.intellij.execution.process.ProcessOutputTypes; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ArrayUtil; +import org.apache.subversion.javahl.types.Revision; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.commandLine.LineCommandListener; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.jetbrains.idea.svn.commandLine.SvnLineCommand; +import org.jetbrains.idea.svn.config.SvnBindException; +import org.tigris.subversion.javahl.BindClientException; +import org.tigris.subversion.javahl.ClientException; + +import java.io.File; +import java.util.*; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/25/13 + * Time: 4:56 PM + */ +public class SvnCommitRunner { + private final String myExePath; + @Nullable private final AuthenticationCallback myAuthenticationCallback; + private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.SvnCommitRunner"); + private SvnCommitRunner.CommandListener myCommandListener; + + public SvnCommitRunner(@NotNull String path, @Nullable CommitEventHandler handler, @Nullable AuthenticationCallback authenticationCallback) { + myExePath = path; + myCommandListener = new CommandListener(handler); + myAuthenticationCallback = authenticationCallback; + } + + public long commit(String[] paths, + String message, + int depth, + boolean noUnlock, + boolean keepChangelist, + String[] changelists, + Map revpropTable) throws ClientException { + if (paths.length == 0) return Revision.SVN_INVALID_REVNUM; + + final List<String> parameters = new ArrayList<String>(); + parameters.add("--depth"); + parameters.add(SvnBindUtil.getDepthName(depth)); + if (noUnlock) { + parameters.add("--no-unlock"); + } + if (keepChangelist) { + parameters.add("--keep-changelists"); + } + if (changelists != null && changelists.length > 0) { + SvnBindUtil.changelistsToCommand(changelists, parameters); + } + if (revpropTable != null && ! revpropTable.isEmpty()) { + final Set<Map.Entry<Object, Object>> set = revpropTable.entrySet(); + for (Map.Entry<Object, Object> entry : set) { + parameters.add("--with-revprop"); + parameters.add(entry.getKey() + "=" + entry.getValue()); + } + } + parameters.add("-m"); + parameters.add(message); + Arrays.sort(paths); + parameters.addAll(Arrays.asList(paths)); + + try { + SvnLineCommand.runWithAuthenticationAttempt(myExePath, new File(paths[0]), SvnCommandName.ci, + myCommandListener, myAuthenticationCallback, ArrayUtil.toStringArray(parameters)); + } + catch (SvnBindException e) { + throw BindClientException.create(e, Revision.SVN_INVALID_REVNUM); + } + myCommandListener.throwExceptionIfOccurred(); + + return myCommandListener.getCommittedRevision(); + } + + private static class CommandListener extends LineCommandListener { + @Nullable private final CommitEventHandler myHandler; + private SvnBindException myException; + private long myCommittedRevision = Revision.SVN_INVALID_REVNUM; + private File myBase; + + public CommandListener(@Nullable CommitEventHandler handler) { + myHandler = handler; + } + + public void throwExceptionIfOccurred() throws BindClientException { + if (myException != null) { + throw BindClientException.create(myException, Revision.SVN_INVALID_REVNUM); + } + } + + private long getCommittedRevision() { + return myCommittedRevision; + } + + @Override + public void baseDirectory(File file) { + myBase = file; + } + + @Override + public void onLineAvailable(String line, Key outputType) { + final String trim = line.trim(); + if (ProcessOutputTypes.STDOUT.equals(outputType)) { + try { + parseLine(trim); + } + catch (SvnBindException e) { + myException = e; + } + } + } + + private void parseLine(String line) throws SvnBindException { + if (StringUtil.isEmptyOrSpaces(line)) return; + if (line.startsWith(CommitEventType.transmittingDeltas.getText())) { + if (myHandler != null) { + myHandler.commitEvent(CommitEventType.transmittingDeltas, myBase); + } + return; + } + if (line.startsWith(CommitEventType.committedRevision.getText())) { + final String substring = line.substring(CommitEventType.committedRevision.getText().length()); + int cnt = 0; + while (StringUtil.isWhiteSpace(substring.charAt(cnt))) { + ++ cnt; + } + final StringBuilder num = new StringBuilder(); + while (Character.isDigit(substring.charAt(cnt))) { + num.append(substring.charAt(cnt)); + ++ cnt; + } + if (num.length() > 0) { + try { + myCommittedRevision = Long.parseLong(num.toString()); + if (myHandler != null) { + myHandler.committedRevision(myCommittedRevision); + } + } catch (NumberFormatException e) { + final String message = "Wrong committed revision number: " + num.toString() + ", string: " + line; + LOG.info(message, e); + throw new SvnBindException(message); + } + } else { + final String message = "Missing committed revision number: " + num.toString() + ", string: " + line; + LOG.info(message); + throw new SvnBindException(message); + } + } else { + if (myHandler == null) return; + final int idxSpace = line.indexOf(' '); + if (idxSpace == -1) { + LOG.info("Can not parse event type: " + line); + return; + } + final CommitEventType type = CommitEventType.create(line.substring(0, idxSpace)); + if (type == null) { + LOG.info("Can not parse event type: " + line); + return; + } + final File target = new File(myBase, new String(line.substring(idxSpace + 1).trim())); + myHandler.commitEvent(type, target); + } + } + } + +/*C:\TestProjects\sortedProjects\Subversion\local2\preRelease\mod2\src\com\test>sv + n st + D gggG + D gggG\Rrr.java + D gggG\and555.txt + D gggG\test.txt + A + gggGA + D + gggGA\Rrr.java + A + gggGA\RrrAA.java + D + gggGA\and.txt + M + gggGA\and555.txt + A gggGA\someNewFile.txt + + --- Changelist 'New changelistrwerwe': + A ddd.jpg + + C:\TestProjects\sortedProjects\Subversion\local2\preRelease\mod2\src\com\test>sv + n ci -m 123 + Adding ddd.jpg + Deleting gggG + Adding gggGA + Deleting gggGA\Rrr.java + Adding gggGA\RrrAA.java + Sending gggGA\and555.txt + Adding gggGA\someNewFile.txt + Transmitting file data .... + Committed revision 165.*/ +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java new file mode 100644 index 000000000000..ccbc5c37db61 --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn.commandLine; + +import com.intellij.openapi.vcs.LineProcessEventListenerAdapter; + +import java.io.File; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/26/13 + * Time: 10:38 AM + */ +public abstract class LineCommandListener extends LineProcessEventListenerAdapter { + private boolean myCanceled; + + public abstract void baseDirectory(final File file); + + public void cancel() { + myCanceled = true; + } + + protected boolean isCanceled() { + return myCanceled; + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java new file mode 100644 index 000000000000..a1e358b6f4a0 --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java @@ -0,0 +1,221 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn.commandLine; + +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.process.OSProcessHandler; +import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.process.ProcessListener; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vcs.ProcessEventListener; +import com.intellij.util.EventDispatcher; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.List; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 1/25/12 + * Time: 12:58 PM + */ +public abstract class SvnCommand { + static final Logger LOG = Logger.getInstance(SvnCommand.class.getName()); + + private boolean myIsDestroyed; + private int myExitCode; + protected final GeneralCommandLine myCommandLine; + private final File myWorkingDirectory; + private Process myProcess; + private OSProcessHandler myHandler; + private final Object myLock; + + private final EventDispatcher<ProcessEventListener> myListeners = EventDispatcher.create(ProcessEventListener.class); + + // todo check version + /*c:\Program Files (x86)\CollabNet\Subversion Client17>svn --version --quiet + 1.7.2*/ + + public SvnCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) { + this(workingDirectory, commandName, exePath, null); + } + + public SvnCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath, + @Nullable File configDir) { + myLock = new Object(); + myCommandLine = new GeneralCommandLine(); + myWorkingDirectory = workingDirectory; + myCommandLine.setExePath(exePath); + myCommandLine.setWorkDirectory(workingDirectory); + if (configDir != null) { + myCommandLine.addParameters("--config-dir", configDir.getPath()); + } + myCommandLine.addParameter(commandName.getName()); + } + + public void start() { + synchronized (myLock) { + checkNotStarted(); + + try { + myProcess = myCommandLine.createProcess(); + if (LOG.isDebugEnabled()) { + LOG.debug(myCommandLine.toString()); + } + myHandler = new OSProcessHandler(myProcess, myCommandLine.getCommandLineString()); + startHandlingStreams(); + } catch (Throwable t) { + myListeners.getMulticaster().startFailed(t); + } + } + } + + private void startHandlingStreams() { + final ProcessListener processListener = new ProcessListener() { + public void startNotified(final ProcessEvent event) { + // do nothing + } + + public void processTerminated(final ProcessEvent event) { + final int exitCode = event.getExitCode(); + try { + setExitCode(exitCode); + SvnCommand.this.processTerminated(exitCode); + } finally { + listeners().processTerminated(exitCode); + } + } + + public void processWillTerminate(final ProcessEvent event, final boolean willBeDestroyed) { + // do nothing + } + + public void onTextAvailable(final ProcessEvent event, final Key outputType) { + SvnCommand.this.onTextAvailable(event.getText(), outputType); + } + }; + + myHandler.addProcessListener(processListener); + myHandler.startNotify(); + } + + /** + * Wait for process termination + */ + public void waitFor() { + checkStarted(); + final OSProcessHandler handler; + synchronized (myLock) { + if (myIsDestroyed) return; + handler = myHandler; + } + handler.waitFor(); + } + + protected abstract void processTerminated(int exitCode); + protected abstract void onTextAvailable(final String text, final Key outputType); + + public void cancel() { + synchronized (myLock) { + checkStarted(); + destroyProcess(); + } + } + + protected void setExitCode(final int code) { + synchronized (myLock) { + myExitCode = code; + } + } + + public void addListener(final ProcessEventListener listener) { + synchronized (myLock) { + myListeners.addListener(listener); + } + } + + protected ProcessEventListener listeners() { + synchronized (myLock) { + return myListeners.getMulticaster(); + } + } + + public void addParameters(@NonNls @NotNull String... parameters) { + synchronized (myLock) { + checkNotStarted(); + myCommandLine.addParameters(parameters); + } + } + + public void addParameters(List<String> parameters) { + synchronized (myLock) { + checkNotStarted(); + myCommandLine.addParameters(parameters); + } + } + + public void destroyProcess() { + synchronized (myLock) { + myIsDestroyed = true; + myHandler.destroyProcess(); + } + } + + /** + * check that process is not started yet + * + * @throws IllegalStateException if process has been already started + */ + private void checkNotStarted() { + if (isStarted()) { + throw new IllegalStateException("The process has been already started"); + } + } + + /** + * check that process is started + * + * @throws IllegalStateException if process has not been started + */ + protected void checkStarted() { + if (! isStarted()) { + throw new IllegalStateException("The process is not started yet"); + } + } + + /** + * @return true if process is started + */ + public boolean isStarted() { + synchronized (myLock) { + return myProcess != null; + } + } + + protected int getExitCode() { + synchronized (myLock) { + return myExitCode; + } + } + + protected File getWorkingDirectory() { + return myWorkingDirectory; + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java new file mode 100644 index 000000000000..58c2ff2ab308 --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn.commandLine; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 1/25/12 + * Time: 1:49 PM + */ +public enum SvnCommandName { + version("--version", false), + info("info", false), + st("st", false), + up("up", true), + ci("commit", true), + cleanup("cleanup", true); + + private final String myName; + private final boolean myWriteable; + + private SvnCommandName(String name, boolean writeable) { + myName = name; + myWriteable = writeable; + } + + public String getName() { + return myName; + } + + public boolean isWriteable() { + return myWriteable; + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java new file mode 100644 index 000000000000..3554b1cd4d81 --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java @@ -0,0 +1,415 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn.commandLine; + +import com.intellij.execution.process.ProcessOutputTypes; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ApplicationNamesInfo; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.LineHandlerHelper; +import com.intellij.openapi.vcs.LineProcessEventListener; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.EventDispatcher; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.AuthenticationCallback; +import org.jetbrains.idea.svn.SvnBindUtil; +import org.jetbrains.idea.svn.config.SvnBindException; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 1/25/12 + * Time: 4:05 PM + * + * honestly stolen from GitLineHandler + */ +public class SvnLineCommand extends SvnCommand { + public static final String AUTHENTICATION_REALM = "Authentication realm:"; + public static final String CERTIFICATE_ERROR = "Error validating server certificate for"; + public static final String PASSPHRASE_FOR = "Passphrase for"; + public static final String UNABLE_TO_CONNECT = "svn: E170001:"; + public static final String CANNOT_AUTHENTICATE_TO_PROXY = "Could not authenticate to proxy server"; + + // kept for exact text + //public static final String CLIENT_CERTIFICATE_FILENAME = "Client certificate filename:"; + /** + * the partial line from stdout stream + */ + private final StringBuilder myStdoutLine = new StringBuilder(); + /** + * the partial line from stderr stream + */ + private final StringBuilder myStderrLine = new StringBuilder(); + private final EventDispatcher<LineProcessEventListener> myLineListeners; + private final AtomicReference<Integer> myExitCode; + private final StringBuffer myErr; + + public SvnLineCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) { + this(workingDirectory, commandName, exePath, null); + } + + public SvnLineCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath, File configDir) { + super(workingDirectory, commandName, exePath, configDir); + myLineListeners = EventDispatcher.create(LineProcessEventListener.class); + myExitCode = new AtomicReference<Integer>(); + myErr = new StringBuffer(); + } + + @Override + protected void processTerminated(int exitCode) { + // force newline + if (myStdoutLine.length() != 0) { + onTextAvailable("\n\r", ProcessOutputTypes.STDOUT); + } + else if (myStderrLine.length() != 0) { + onTextAvailable("\n\r", ProcessOutputTypes.STDERR); + } + } + + public static void runWithAuthenticationAttempt(final String exePath, + final File firstFile, + SvnCommandName commandName, + final LineCommandListener listener, + @Nullable AuthenticationCallback authenticationCallback, + final String... parameters) throws SvnBindException { + File base = firstFile.isDirectory() ? firstFile : firstFile.getParentFile(); + base = SvnBindUtil.correctUpToExistingParent(base); + + listener.baseDirectory(base); + + File configDir = null; + + // for IDEA proxy case + if (authenticationCallback != null && authenticationCallback.haveDataForTmpConfig()) { + try { + if (! authenticationCallback.persistDataToTmpConfig(base)) { + throw new SvnBindException("Can not persist " + ApplicationNamesInfo.getInstance().getProductName() + + " HTTP proxy information into tmp config directory"); + } + } + catch (IOException e) { + throw new SvnBindException(e); + } + catch (URISyntaxException e) { + throw new SvnBindException(e); + } + assert authenticationCallback.getSpecialConfigDir() != null; + configDir = authenticationCallback.getSpecialConfigDir(); + } + + while (true) { + final SvnLineCommand command = runCommand(exePath, commandName, listener, base, configDir, parameters); + if (command.myErr.length() > 0) { + final String errText = command.myErr.toString().trim(); + if (authenticationCallback != null) { + final AuthCallbackCase callback = createCallback(errText, authenticationCallback, base); + if (callback != null) { + cleanup(exePath, commandName, base); + if (callback.getCredentials(errText)) { + if (authenticationCallback.getSpecialConfigDir() != null) { + configDir = authenticationCallback.getSpecialConfigDir(); + } + continue; + } + } + } + throw new SvnBindException(errText); + } + final Integer exitCode = command.myExitCode.get(); + if (exitCode != 0) { + throw new SvnBindException("Svn process exited with error code: " + exitCode); + } + return; + } + //ok + } + + private static AuthCallbackCase createCallback(final String errText, final AuthenticationCallback callback, final File base) { + if (errText.startsWith(CERTIFICATE_ERROR)) { + return new CertificateCallbackCase(callback, base); + } + if (errText.startsWith(AUTHENTICATION_REALM)) { + return new CredentialsCallback(callback, base); + } + if (errText.startsWith(PASSPHRASE_FOR)) { + return new PassphraseCallback(callback, base); + } + if (errText.startsWith(UNABLE_TO_CONNECT) && errText.contains(CANNOT_AUTHENTICATE_TO_PROXY)) { + return new ProxyCallback(callback, base); + } + return null; + } + + private static class ProxyCallback extends AuthCallbackCase { + protected ProxyCallback(AuthenticationCallback callback, File base) { + super(callback, base); + } + + @Override + boolean getCredentials(String errText) throws SvnBindException { + return myAuthenticationCallback.askProxyCredentials(myBase); + } + } + + private static class CredentialsCallback extends AuthCallbackCase { + protected CredentialsCallback(AuthenticationCallback callback, File base) { + super(callback, base); + } + + @Override + boolean getCredentials(String errText) throws SvnBindException { + final String realm = cutFirstLine(errText).substring(AUTHENTICATION_REALM.length()).trim(); + final boolean isPassword = StringUtil.containsIgnoreCase(errText, "password"); + if (myTried) { + myAuthenticationCallback.clearPassiveCredentials(realm, myBase, isPassword); + } + myTried = true; + if (myAuthenticationCallback.authenticateFor(realm, myBase, myAuthenticationCallback.getSpecialConfigDir() != null, isPassword)) { + return true; + } + throw new SvnBindException("Authentication canceled for realm: " + realm); + } + } + + private static String cutFirstLine(final String text) { + final int idx = text.indexOf('\n'); + if (idx == -1) return text; + return text.substring(0, idx); + } + + private static class CertificateCallbackCase extends AuthCallbackCase { + private CertificateCallbackCase(AuthenticationCallback callback, File base) { + super(callback, base); + } + + @Override + public boolean getCredentials(final String errText) throws SvnBindException { + String realm = cutFirstLine(errText).substring(CERTIFICATE_ERROR.length()); + final int idx1 = realm.indexOf('\''); + if (idx1 == -1) { + throw new SvnBindException("Can not detect authentication realm name: " + errText); + } + final int idx2 = realm.indexOf('\'', idx1 + 1); + if (idx2== -1) { + throw new SvnBindException("Can not detect authentication realm name: " + errText); + } + realm = realm.substring(idx1 + 1, idx2); + if (! myTried && myAuthenticationCallback.acceptSSLServerCertificate(myBase, realm)) { + myTried = true; + return true; + } + throw new SvnBindException("Server SSL certificate rejected"); + } + } + + private static abstract class AuthCallbackCase { + protected boolean myTried = false; + protected final AuthenticationCallback myAuthenticationCallback; + protected final File myBase; + + protected AuthCallbackCase(AuthenticationCallback callback, final File base) { + myAuthenticationCallback = callback; + myBase = base; + } + + abstract boolean getCredentials(final String errText) throws SvnBindException; + } + + private static void cleanup(String exePath, SvnCommandName commandName, File base) throws SvnBindException { + File wcRoot = SvnBindUtil.getWcRoot(base); + if (wcRoot == null) throw new SvnBindException("Can not find working copy root for: " + base.getPath()); + + //cleanup -> check command type + if (commandName.isWriteable()) { + final SvnSimpleCommand command = new SvnSimpleCommand(wcRoot, SvnCommandName.cleanup, exePath); + try { + command.run(); + } + catch (VcsException e) { + throw new SvnBindException(e); + } + } + } + + /*svn: E170001: Commit failed (details follow): + svn: E170001: Unable to connect to a repository at URL 'htt../svn/secondRepo/local2/trunk/mod2/src/com/test/gggGA' + svn: E170001: OPTIONS of 'htt.../svn/secondRepo/local2/trunk/mod2/src/com/test/gggGA': authorization failed: Could not authenticate to server: rejected Basic challenge (ht)*/ + private final static String ourAuthFailed = "authorization failed"; + private final static String ourAuthFailed2 = "Could not authenticate to server"; + + private static boolean isAuthenticationFailed(String s) { + return s.trim().startsWith(AUTHENTICATION_REALM); + //return s.contains(ourAuthFailed) && s.contains(ourAuthFailed2); + } + + private static SvnLineCommand runCommand(String exePath, + SvnCommandName commandName, + final LineCommandListener listener, + File base, File configDir, + String... parameters) throws SvnBindException { + final SvnLineCommand command = new SvnLineCommand(base, commandName, exePath, configDir) { + int myErrCnt = 0; + + @Override + protected void onTextAvailable(String text, Key outputType) { + + // we won't stop right now if got "authentication realm" -> since we want to get "password" string (that would mean password is expected + // or certificate maybe is expected + // but for certificate passphrase we get just "Passphrase for ..." - one line without line feed + + // for client certificate (when no path in servers file) we get + // Authentication realm: <text> + // Client certificate filename: + if (ProcessOutputTypes.STDERR.equals(outputType)) { + ++ myErrCnt; + final String trim = text.trim(); + if (trim.startsWith(UNABLE_TO_CONNECT)) { + // wait for 3 lines of text then + if (myErrCnt >= 3) { + destroyProcess(); + } + } else if (trim.startsWith(PASSPHRASE_FOR) || myErrCnt >= 2) { + destroyProcess(); + } + } + super.onTextAvailable(text, outputType); + } + }; + + //command.addParameters("--non-interactive"); + command.addParameters(parameters); + final AtomicReference<Throwable> exceptionRef = new AtomicReference<Throwable>(); + // several threads + command.addLineListener(new LineProcessEventListener() { + @Override + public void onLineAvailable(String line, Key outputType) { + if (SvnCommand.LOG.isDebugEnabled()) { + SvnCommand.LOG.debug("==> " + line); + } + if (ApplicationManager.getApplication().isUnitTestMode()) { + System.out.println("==> " + line); + } + listener.onLineAvailable(line, outputType); + if (listener.isCanceled()) { + command.destroyProcess(); + return; + } + if (ProcessOutputTypes.STDERR.equals(outputType)) { + if (command.myErr.length() > 0) { + command.myErr.append('\n'); + } + command.myErr.append(line); + } + } + + @Override + public void processTerminated(int exitCode) { + listener.processTerminated(exitCode); + command.myExitCode.set(exitCode); + } + + @Override + public void startFailed(Throwable exception) { + listener.startFailed(exception); + exceptionRef.set(exception); + } + }); + command.start(); + command.waitFor(); + if (exceptionRef.get() != null) { + throw new SvnBindException(exceptionRef.get()); + } + return command; + } + + @Override + protected void onTextAvailable(String text, Key outputType) { + Iterator<String> lines = LineHandlerHelper.splitText(text).iterator(); + if (ProcessOutputTypes.STDOUT == outputType) { + notifyLines(outputType, lines, myStdoutLine); + } + else if (ProcessOutputTypes.STDERR == outputType) { + notifyLines(outputType, lines, myStderrLine); + } + } + + private void notifyLines(final Key outputType, final Iterator<String> lines, final StringBuilder lineBuilder) { + if (!lines.hasNext()) return; + if (lineBuilder.length() > 0) { + lineBuilder.append(lines.next()); + if (lines.hasNext()) { + // line is complete + final String line = lineBuilder.toString(); + notifyLine(line, outputType); + lineBuilder.setLength(0); + } + } + while (true) { + String line = null; + if (lines.hasNext()) { + line = lines.next(); + } + + if (lines.hasNext()) { + notifyLine(line, outputType); + } + else { + if (line != null && line.length() > 0) { + lineBuilder.append(line); + } + break; + } + } + } + + private void notifyLine(final String line, final Key outputType) { + String trimmed = LineHandlerHelper.trimLineSeparator(line); + myLineListeners.getMulticaster().onLineAvailable(trimmed, outputType); + } + + public void addLineListener(LineProcessEventListener listener) { + myLineListeners.addListener(listener); + super.addListener(listener); + } + + private static class PassphraseCallback extends AuthCallbackCase { + public PassphraseCallback(AuthenticationCallback callback, File base) { + super(callback, base); + } + + @Override + boolean getCredentials(String errText) throws SvnBindException { + // try to get from file + /*if (myTried) { + myAuthenticationCallback.clearPassiveCredentials(null, myBase); + }*/ + myTried = true; + if (myAuthenticationCallback.authenticateFor(null, myBase, myAuthenticationCallback.getSpecialConfigDir() != null, false)) { + return true; + } + throw new SvnBindException("Authentication canceled for : " + errText.substring(PASSPHRASE_FOR.length())); + } + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java new file mode 100644 index 000000000000..da5df9e6d299 --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java @@ -0,0 +1,106 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn.commandLine; + +import com.intellij.execution.process.ProcessOutputTypes; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vcs.ProcessEventListener; +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 1/25/12 + * Time: 4:04 PM + */ +public class SvnSimpleCommand extends SvnCommand { + private final StringBuilder myStderr; + private final StringBuilder myStdout; + private VcsException myException; + private final Object myDataLock; + + public SvnSimpleCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) { + super(workingDirectory, commandName, exePath); + + myDataLock = new Object(); + myStderr = new StringBuilder(); + myStdout = new StringBuilder(); + } + + @Override + protected void processTerminated(int exitCode) { + // + } + + @Override + protected void onTextAvailable(String text, Key outputType) { + synchronized (myDataLock) { + if (ProcessOutputTypes.STDOUT.equals(outputType)) { + myStdout.append(text); + } else if (ProcessOutputTypes.STDERR.equals(outputType)) { + myStderr.append(text); + } + } + } + + public StringBuilder getStderr() { + synchronized (myDataLock) { + return myStderr; + } + } + + public StringBuilder getStdout() { + synchronized (myDataLock) { + return myStdout; + } + } + + public String run() throws VcsException { + addListener(new ProcessEventListener() { + @Override + public void processTerminated(int exitCode) { + } + + @Override + public void startFailed(Throwable exception) { + synchronized (myDataLock) { + myException = new VcsException("Process failed to start (" + myCommandLine.getCommandLineString() + "): " + exception.toString(), exception); + } + } + }); + start(); + if (isStarted()) {//if wasn't started, exception is stored into a field, don't wait for process + waitFor(); + } + + synchronized (myDataLock) { + if (myException != null) throw myException; + final int code = getExitCode(); + if (code == 0) { + return myStdout.toString(); + } else { + final String msg = new StringBuilder("Svn process exited with error code: ").append(code).append("\n") + .append("stderr: ").append(myStderr.toString()).append("\nstdout: ").append(getStdout().toString()) + .append("\nCommand was: ").append(myCommandLine.getCommandLineString()).toString(); + throw new VcsException(msg); + } + } + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/config/SvnBindException.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/config/SvnBindException.java new file mode 100644 index 000000000000..8957843e911c --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/config/SvnBindException.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn.config; + +import com.intellij.openapi.vcs.VcsException; + +import java.util.Collection; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/25/13 + * Time: 5:57 PM + * + * Marker exception + */ +public class SvnBindException extends VcsException { + public SvnBindException(String message) { + super(message); + } + + public SvnBindException(Throwable throwable, boolean isWarning) { + super(throwable, isWarning); + } + + public SvnBindException(Throwable throwable) { + super(throwable); + } + + public SvnBindException(String message, Throwable cause) { + super(message, cause); + } + + public SvnBindException(String message, boolean isWarning) { + super(message, isWarning); + } + + public SvnBindException(Collection<String> messages) { + super(messages); + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java new file mode 100644 index 000000000000..f4e0f4cf2269 --- /dev/null +++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java @@ -0,0 +1,121 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.svn.status; + +import org.apache.subversion.javahl.types.Lock; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Date; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/21/12 + * Time: 2:33 PM + */ +public class LockWrapper { + private String myPath; + private String myID; + private String myOwner; + private String myComment; + private Date myCreationDate; + private Date myExpirationDate; + + public LockWrapper(String path, String ID, String owner, String comment, Date creationDate, Date expirationDate) { + myPath = path; + myID = ID; + myOwner = owner; + myComment = comment; + myCreationDate = creationDate; + myExpirationDate = expirationDate; + } + + public LockWrapper() { + } + + public String getPath() { + return myPath; + } + + public void setPath(String path) { + myPath = path; + } + + public String getID() { + return myID; + } + + public void setID(String ID) { + myID = ID; + } + + public String getOwner() { + return myOwner; + } + + public void setOwner(String owner) { + myOwner = owner; + } + + public String getComment() { + return myComment; + } + + public void setComment(String comment) { + myComment = comment; + } + + public Date getCreationDate() { + return myCreationDate; + } + + public void setCreationDate(Date creationDate) { + myCreationDate = creationDate; + } + + public Date getExpirationDate() { + return myExpirationDate; + } + + public void setExpirationDate(Date expirationDate) { + myExpirationDate = expirationDate; + } + + public org.tigris.subversion.javahl.Lock create() { + final Date creation = getCreationDate(); + final Date expiration = getExpirationDate(); + final Lock newLock = new Lock(getOwner(), getPath(), getID(), getComment(), creation == null ? 0 : creation.getTime(), + expiration == null ? 0 : expiration.getTime()); + try { + final Constructor<org.tigris.subversion.javahl.Lock> constructor = org.tigris.subversion.javahl.Lock.class.getConstructor(Lock.class); + constructor.setAccessible(true); + return constructor.newInstance(newLock); + } + catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + catch (InstantiationException e) { + throw new RuntimeException(e); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } +} |