| /* |
| * Copyright 2000-2012 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.diagnostic.Logger; |
| import com.intellij.openapi.util.Getter; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vcs.VcsException; |
| import com.intellij.openapi.vfs.CharsetToolkit; |
| import com.intellij.util.containers.Convertor; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.idea.svn.SvnUtil; |
| import org.jetbrains.idea.svn.SvnVcs; |
| import org.jetbrains.idea.svn.portable.PortableStatus; |
| import org.jetbrains.idea.svn.portable.SvnExceptionWrapper; |
| import org.jetbrains.idea.svn.portable.SvnStatusClientI; |
| import org.tmatesoft.svn.core.*; |
| import org.tmatesoft.svn.core.internal.util.SVNPathUtil; |
| import org.tmatesoft.svn.core.wc.*; |
| import org.tmatesoft.svn.core.wc2.SvnTarget; |
| import org.xml.sax.SAXException; |
| |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.parsers.SAXParser; |
| import javax.xml.parsers.SAXParserFactory; |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.*; |
| |
| /** |
| * Created with IntelliJ IDEA. |
| * User: Irina.Chernushina |
| * Date: 1/25/12 |
| * Time: 5:21 PM |
| */ |
| public class SvnCommandLineStatusClient implements SvnStatusClientI { |
| private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient"); |
| |
| private final SvnCommandLineInfoClient myInfoClient; |
| @NotNull private final SvnVcs myVcs; |
| |
| public SvnCommandLineStatusClient(@NotNull SvnVcs vcs) { |
| myVcs = vcs; |
| myInfoClient = new SvnCommandLineInfoClient(vcs); |
| } |
| |
| @Override |
| public long doStatus(File path, boolean recursive, boolean remote, boolean reportAll, boolean includeIgnored, ISVNStatusHandler handler) |
| throws SVNException { |
| return doStatus(path, recursive, remote, reportAll, includeIgnored, false, handler); |
| } |
| |
| @Override |
| public long doStatus(File path, |
| boolean recursive, |
| boolean remote, |
| boolean reportAll, |
| boolean includeIgnored, |
| boolean collectParentExternals, |
| ISVNStatusHandler handler) throws SVNException { |
| return doStatus(path, SVNRevision.UNDEFINED, recursive, remote, reportAll, includeIgnored, collectParentExternals, handler); |
| } |
| |
| @Override |
| public long doStatus(File path, |
| SVNRevision revision, |
| boolean recursive, |
| boolean remote, |
| boolean reportAll, |
| boolean includeIgnored, |
| boolean collectParentExternals, |
| ISVNStatusHandler handler) throws SVNException { |
| return doStatus(path, revision, recursive ? SVNDepth.INFINITY : SVNDepth.EMPTY, remote, reportAll, includeIgnored, |
| collectParentExternals, handler, null); |
| } |
| |
| @Override |
| public long doStatus(final File path, |
| final SVNRevision revision, |
| final SVNDepth depth, |
| boolean remote, |
| boolean reportAll, |
| boolean includeIgnored, |
| boolean collectParentExternals, |
| final ISVNStatusHandler handler, |
| final Collection changeLists) throws SVNException { |
| File base = path.isDirectory() ? path : path.getParentFile(); |
| base = SvnBindUtil.correctUpToExistingParent(base); |
| |
| final SVNInfo infoBase = myInfoClient.doInfo(base, revision); |
| List<String> parameters = new ArrayList<String>(); |
| |
| putParameters(parameters, path, depth, remote, reportAll, includeIgnored, changeLists); |
| |
| SvnCommand command; |
| try { |
| command = CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.st, parameters, null); |
| } |
| catch (VcsException e) { |
| throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); |
| } |
| parseResult(path, revision, handler, base, infoBase, command); |
| return 0; |
| } |
| |
| private void parseResult(final File path, |
| SVNRevision revision, |
| ISVNStatusHandler handler, |
| File base, |
| SVNInfo infoBase, |
| SvnCommand command) throws SVNException { |
| String result = command.getOutput(); |
| |
| if (StringUtil.isEmptyOrSpaces(result)) { |
| throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_GENERAL, "Status request returned nothing for command: " + |
| command.myCommandLine.getCommandLineString())); |
| } |
| |
| try { |
| final SvnStatusHandler[] svnHandl = new SvnStatusHandler[1]; |
| svnHandl[0] = createStatusHandler(revision, handler, base, infoBase, svnHandl); |
| SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); |
| parser.parse(new ByteArrayInputStream(result.getBytes(CharsetToolkit.UTF8_CHARSET)), svnHandl[0]); |
| if (!svnHandl[0].isAnythingReported()) { |
| if (!SvnUtil.isSvnVersioned(myVcs, path)) { |
| throw new SVNException( |
| SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "Command - " + command.getCommandText() + ". Result - " + result)); |
| } else { |
| // return status indicating "NORMAL" state |
| // typical output would be like |
| // <status> |
| // <target path="1.txt"></target> |
| // </status> |
| // so it does not contain any <entry> element and current parsing logic returns null |
| |
| PortableStatus status = new PortableStatus(); |
| status.setPath(path.getAbsolutePath()); |
| status.setContentsStatus(SVNStatusType.STATUS_NORMAL); |
| status.setInfoGetter(new Getter<SVNInfo>() { |
| @Override |
| public SVNInfo get() { |
| return createInfoGetter(null).convert(path); |
| } |
| }); |
| handler.handleStatus(status); |
| } |
| } |
| } |
| catch (SvnExceptionWrapper e) { |
| throw (SVNException) e.getCause(); |
| } catch (IOException e) { |
| throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); |
| } |
| catch (ParserConfigurationException e) { |
| throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); |
| } |
| catch (SAXException e) { |
| throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); |
| } |
| } |
| |
| private static void putParameters(@NotNull List<String> parameters, |
| @NotNull File path, |
| @Nullable SVNDepth depth, |
| boolean remote, |
| boolean reportAll, |
| boolean includeIgnored, |
| @Nullable Collection changeLists) { |
| CommandUtil.put(parameters, path); |
| CommandUtil.put(parameters, depth); |
| CommandUtil.put(parameters, remote, "-u"); |
| CommandUtil.put(parameters, reportAll, "--verbose"); |
| CommandUtil.put(parameters, includeIgnored, "--no-ignore"); |
| // TODO: Fix this check - update corresponding parameters in SvnStatusClientI |
| CommandUtil.putChangeLists(parameters, changeLists); |
| parameters.add("--xml"); |
| } |
| |
| public SvnStatusHandler createStatusHandler(final SVNRevision revision, |
| final ISVNStatusHandler handler, |
| final File base, |
| final SVNInfo infoBase, final SvnStatusHandler[] svnHandl) { |
| final SvnStatusHandler.ExternalDataCallback callback = createStatusCallback(handler, base, infoBase, svnHandl); |
| |
| return new SvnStatusHandler(callback, base, createInfoGetter(revision)); |
| } |
| |
| private Convertor<File, SVNInfo> createInfoGetter(final SVNRevision revision) { |
| return new Convertor<File, SVNInfo>() { |
| @Override |
| public SVNInfo convert(File o) { |
| try { |
| return myInfoClient.doInfo(o, revision); |
| } |
| catch (SVNException e) { |
| throw new SvnExceptionWrapper(e); |
| } |
| } |
| }; |
| } |
| |
| public static SvnStatusHandler.ExternalDataCallback createStatusCallback(final ISVNStatusHandler handler, |
| final File base, |
| final SVNInfo infoBase, |
| final SvnStatusHandler[] svnHandl) { |
| final Map<File, SVNInfo> externalsMap = new HashMap<File, SVNInfo>(); |
| final String[] changelistName = new String[1]; |
| |
| return new SvnStatusHandler.ExternalDataCallback() { |
| @Override |
| public void switchPath() { |
| final PortableStatus pending = svnHandl[0].getPending(); |
| pending.setChangelistName(changelistName[0]); |
| try { |
| //if (infoBase != null) { |
| SVNInfo baseInfo = infoBase; |
| File baseFile = base; |
| final File pendingFile = new File(pending.getPath()); |
| if (! externalsMap.isEmpty()) { |
| for (File file : externalsMap.keySet()) { |
| if (FileUtil.isAncestor(file, pendingFile, false)) { |
| baseInfo = externalsMap.get(file); |
| baseFile = file; |
| break; |
| } |
| } |
| } |
| if (baseInfo != null) { |
| final String append; |
| final String systemIndependentPath = FileUtil.toSystemIndependentName(pending.getPath()); |
| if (pendingFile.isAbsolute()) { |
| final String relativePath = |
| FileUtil.getRelativePath(FileUtil.toSystemIndependentName(baseFile.getPath()), systemIndependentPath, '/'); |
| append = SVNPathUtil.append(baseInfo.getURL().toString(), FileUtil.toSystemIndependentName(relativePath)); |
| } |
| else { |
| append = SVNPathUtil.append(baseInfo.getURL().toString(), systemIndependentPath); |
| } |
| pending.setURL(SVNURL.parseURIEncoded(append)); |
| } |
| if (SVNStatusType.STATUS_EXTERNAL.equals(pending.getNodeStatus())) { |
| externalsMap.put(pending.getFile(), pending.getInfo()); |
| } |
| handler.handleStatus(pending); |
| } |
| catch (SVNException e) { |
| throw new SvnExceptionWrapper(e); |
| } |
| } |
| |
| @Override |
| public void switchChangeList(String newList) { |
| changelistName[0] = newList; |
| } |
| }; |
| } |
| |
| @Override |
| public SVNStatus doStatus(File path, boolean remote) throws SVNException { |
| return doStatus(path, remote, false); |
| } |
| |
| @Override |
| public SVNStatus doStatus(File path, boolean remote, boolean collectParentExternals) throws SVNException { |
| final SVNStatus[] svnStatus = new SVNStatus[1]; |
| doStatus(path, SVNRevision.UNDEFINED, SVNDepth.EMPTY, remote, false, false, collectParentExternals, new ISVNStatusHandler() { |
| @Override |
| public void handleStatus(SVNStatus status) throws SVNException { |
| svnStatus[0] = status; |
| } |
| }, null); |
| return svnStatus[0]; |
| } |
| } |