001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.scm.provider.svn.svnexe.command.changelog;
020
021import java.io.File;
022import java.text.SimpleDateFormat;
023import java.util.Date;
024import java.util.Objects;
025import java.util.TimeZone;
026
027import org.apache.commons.lang3.StringUtils;
028import org.apache.maven.scm.CommandParameter;
029import org.apache.maven.scm.CommandParameters;
030import org.apache.maven.scm.ScmBranch;
031import org.apache.maven.scm.ScmException;
032import org.apache.maven.scm.ScmFileSet;
033import org.apache.maven.scm.ScmResult;
034import org.apache.maven.scm.ScmTag;
035import org.apache.maven.scm.ScmVersion;
036import org.apache.maven.scm.command.changelog.AbstractChangeLogCommand;
037import org.apache.maven.scm.command.changelog.ChangeLogScmRequest;
038import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
039import org.apache.maven.scm.command.changelog.ChangeLogSet;
040import org.apache.maven.scm.provider.ScmProviderRepository;
041import org.apache.maven.scm.provider.svn.SvnTagBranchUtils;
042import org.apache.maven.scm.provider.svn.command.SvnCommand;
043import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
044import org.apache.maven.scm.provider.svn.svnexe.command.SvnCommandLineUtils;
045import org.codehaus.plexus.util.Os;
046import org.codehaus.plexus.util.cli.CommandLineException;
047import org.codehaus.plexus.util.cli.CommandLineUtils;
048import org.codehaus.plexus.util.cli.Commandline;
049
050/**
051 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
052 * @author Olivier Lamy
053 *
054 */
055public class SvnChangeLogCommand extends AbstractChangeLogCommand implements SvnCommand {
056    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss Z";
057
058    @Override
059    public ScmResult executeCommand(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
060            throws ScmException {
061        return executeChangeLogCommand(
062                repository,
063                fileSet,
064                parameters.getDate(CommandParameter.START_DATE, null),
065                parameters.getDate(CommandParameter.END_DATE, null),
066                (ScmBranch) parameters.getScmVersion(CommandParameter.BRANCH, null),
067                parameters.getString(CommandParameter.CHANGELOG_DATE_PATTERN, null),
068                parameters.getScmVersion(CommandParameter.START_SCM_VERSION, null),
069                parameters.getScmVersion(CommandParameter.END_SCM_VERSION, null),
070                parameters.getInt(CommandParameter.LIMIT, -1));
071    }
072
073    /** {@inheritDoc} */
074    @Deprecated
075    @Override
076    protected ChangeLogScmResult executeChangeLogCommand(
077            ScmProviderRepository repo,
078            ScmFileSet fileSet,
079            ScmVersion startVersion,
080            ScmVersion endVersion,
081            String datePattern)
082            throws ScmException {
083        return executeChangeLogCommand(repo, fileSet, null, null, null, datePattern, startVersion, endVersion, null);
084    }
085
086    /** {@inheritDoc} */
087    @Deprecated
088    @Override
089    protected ChangeLogScmResult executeChangeLogCommand(
090            ScmProviderRepository repo,
091            ScmFileSet fileSet,
092            Date startDate,
093            Date endDate,
094            ScmBranch branch,
095            String datePattern)
096            throws ScmException {
097        return executeChangeLogCommand(repo, fileSet, startDate, endDate, branch, datePattern, null, null, null);
098    }
099
100    @Override
101    protected ChangeLogScmResult executeChangeLogCommand(ChangeLogScmRequest request) throws ScmException {
102        final ScmVersion startVersion = request.getStartRevision();
103        final ScmVersion endVersion = request.getEndRevision();
104        final ScmFileSet fileSet = request.getScmFileSet();
105        final String datePattern = request.getDatePattern();
106        return executeChangeLogCommand(
107                request.getScmRepository().getProviderRepository(),
108                fileSet,
109                request.getStartDate(),
110                request.getEndDate(),
111                request.getScmBranch(),
112                datePattern,
113                startVersion,
114                endVersion,
115                request.getLimit());
116    }
117
118    private ChangeLogScmResult executeChangeLogCommand(
119            ScmProviderRepository repo,
120            ScmFileSet fileSet,
121            Date startDate,
122            Date endDate,
123            ScmBranch branch,
124            String datePattern,
125            ScmVersion startVersion,
126            ScmVersion endVersion,
127            Integer limit)
128            throws ScmException {
129        Commandline cl = createCommandLine(
130                (SvnScmProviderRepository) repo,
131                fileSet.getBasedir(),
132                branch,
133                startDate,
134                endDate,
135                startVersion,
136                endVersion,
137                limit);
138
139        SvnChangeLogConsumer consumer = new SvnChangeLogConsumer(datePattern);
140
141        CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
142
143        if (logger.isInfoEnabled()) {
144            logger.info("Executing: " + SvnCommandLineUtils.cryptPassword(cl));
145
146            if (Os.isFamily(Os.FAMILY_WINDOWS)) {
147                logger.info("Working directory: " + cl.getWorkingDirectory().getAbsolutePath());
148            }
149        }
150
151        int exitCode;
152
153        try {
154            exitCode = SvnCommandLineUtils.execute(cl, consumer, stderr);
155        } catch (CommandLineException ex) {
156            throw new ScmException("Error while executing svn command.", ex);
157        }
158
159        if (exitCode != 0) {
160            return new ChangeLogScmResult(cl.toString(), "The svn command failed.", stderr.getOutput(), false);
161        }
162        ChangeLogSet changeLogSet = new ChangeLogSet(consumer.getModifications(), startDate, endDate);
163        changeLogSet.setStartVersion(startVersion);
164        changeLogSet.setEndVersion(endVersion);
165
166        return new ChangeLogScmResult(cl.toString(), changeLogSet);
167    }
168
169    // ----------------------------------------------------------------------
170    //
171    // ----------------------------------------------------------------------
172
173    public static Commandline createCommandLine(
174            SvnScmProviderRepository repository,
175            File workingDirectory,
176            ScmBranch branch,
177            Date startDate,
178            Date endDate,
179            ScmVersion startVersion,
180            ScmVersion endVersion) {
181        return createCommandLine(
182                repository, workingDirectory, branch, startDate, endDate, startVersion, endVersion, null);
183    }
184
185    public static Commandline createCommandLine(
186            SvnScmProviderRepository repository,
187            File workingDirectory,
188            ScmBranch branch,
189            Date startDate,
190            Date endDate,
191            ScmVersion startVersion,
192            ScmVersion endVersion,
193            Integer limit) {
194        SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
195
196        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
197
198        Commandline cl = SvnCommandLineUtils.getBaseSvnCommandLine(workingDirectory, repository);
199
200        cl.createArg().setValue("log");
201
202        cl.createArg().setValue("-v");
203
204        // TODO: May want to add some kind of support for --stop-on-copy and --limit NUM
205
206        if (limit != null && limit > 0) {
207            cl.createArg().setValue("--limit");
208            cl.createArg().setValue(Integer.toString(limit));
209        }
210
211        if (startDate != null) {
212            cl.createArg().setValue("-r");
213
214            if (endDate != null) {
215                cl.createArg()
216                        .setValue("{" + dateFormat.format(startDate) + "}" + ":" + "{" + dateFormat.format(endDate)
217                                + "}");
218            } else {
219                cl.createArg().setValue("{" + dateFormat.format(startDate) + "}:HEAD");
220            }
221        }
222
223        if (startVersion != null) {
224            cl.createArg().setValue("-r");
225
226            if (endVersion != null) {
227                if (startVersion.getName().equals(endVersion.getName())) {
228                    cl.createArg().setValue(startVersion.getName());
229                } else {
230                    cl.createArg().setValue(startVersion.getName() + ":" + endVersion.getName());
231                }
232            } else {
233                cl.createArg().setValue(startVersion.getName() + ":HEAD");
234            }
235        }
236
237        if (branch != null && StringUtils.isNotEmpty(branch.getName())) {
238            // By specifying a branch and this repository url below, subversion should show
239            // the changelog of that branch, but limit it to paths that also occur in this repository.
240            if (branch instanceof ScmTag) {
241                String tagUrl = SvnTagBranchUtils.resolveTagUrl(repository, (ScmTag) branch);
242                cl.createArg().setValue(tagUrl + "@");
243            } else {
244                String branchUrl = SvnTagBranchUtils.resolveBranchUrl(repository, branch);
245                cl.createArg().setValue(branchUrl + "@");
246            }
247        }
248
249        if (endVersion == null || !Objects.equals("BASE", endVersion.getName())) {
250            cl.createArg().setValue(repository.getUrl() + "@");
251        }
252
253        return cl;
254    }
255}