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 */
054public class SvnChangeLogCommand extends AbstractChangeLogCommand implements SvnCommand {
055    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss Z";
056    private final boolean interactive;
057
058    public SvnChangeLogCommand(boolean interactive) {
059        this.interactive = interactive;
060    }
061
062    @Override
063    public ScmResult executeCommand(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
064            throws ScmException {
065        return executeChangeLogCommand(
066                repository,
067                fileSet,
068                parameters.getDate(CommandParameter.START_DATE, null),
069                parameters.getDate(CommandParameter.END_DATE, null),
070                (ScmBranch) parameters.getScmVersion(CommandParameter.BRANCH, null),
071                parameters.getString(CommandParameter.CHANGELOG_DATE_PATTERN, null),
072                parameters.getScmVersion(CommandParameter.START_SCM_VERSION, null),
073                parameters.getScmVersion(CommandParameter.END_SCM_VERSION, null),
074                parameters.getInt(CommandParameter.LIMIT, -1));
075    }
076
077    /**
078     * {@inheritDoc}
079     */
080    @Deprecated
081    @Override
082    protected ChangeLogScmResult executeChangeLogCommand(
083            ScmProviderRepository repo,
084            ScmFileSet fileSet,
085            ScmVersion startVersion,
086            ScmVersion endVersion,
087            String datePattern)
088            throws ScmException {
089        return executeChangeLogCommand(repo, fileSet, null, null, null, datePattern, startVersion, endVersion, null);
090    }
091
092    /**
093     * {@inheritDoc}
094     */
095    @Deprecated
096    @Override
097    protected ChangeLogScmResult executeChangeLogCommand(
098            ScmProviderRepository repo,
099            ScmFileSet fileSet,
100            Date startDate,
101            Date endDate,
102            ScmBranch branch,
103            String datePattern)
104            throws ScmException {
105        return executeChangeLogCommand(repo, fileSet, startDate, endDate, branch, datePattern, null, null, null);
106    }
107
108    @Override
109    protected ChangeLogScmResult executeChangeLogCommand(ChangeLogScmRequest request) throws ScmException {
110        final ScmVersion startVersion = request.getStartRevision();
111        final ScmVersion endVersion = request.getEndRevision();
112        final ScmFileSet fileSet = request.getScmFileSet();
113        final String datePattern = request.getDatePattern();
114        return executeChangeLogCommand(
115                request.getScmRepository().getProviderRepository(),
116                fileSet,
117                request.getStartDate(),
118                request.getEndDate(),
119                request.getScmBranch(),
120                datePattern,
121                startVersion,
122                endVersion,
123                request.getLimit());
124    }
125
126    private ChangeLogScmResult executeChangeLogCommand(
127            ScmProviderRepository repo,
128            ScmFileSet fileSet,
129            Date startDate,
130            Date endDate,
131            ScmBranch branch,
132            String datePattern,
133            ScmVersion startVersion,
134            ScmVersion endVersion,
135            Integer limit)
136            throws ScmException {
137        Commandline cl = createCommandLine(
138                (SvnScmProviderRepository) repo,
139                fileSet.getBasedir(),
140                branch,
141                startDate,
142                endDate,
143                startVersion,
144                endVersion,
145                limit);
146
147        SvnChangeLogConsumer consumer = new SvnChangeLogConsumer(datePattern);
148
149        CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
150
151        if (logger.isInfoEnabled()) {
152            logger.info("Executing: " + SvnCommandLineUtils.cryptPassword(cl));
153
154            if (Os.isFamily(Os.FAMILY_WINDOWS)) {
155                logger.info("Working directory: " + cl.getWorkingDirectory().getAbsolutePath());
156            }
157        }
158
159        int exitCode;
160
161        try {
162            exitCode = SvnCommandLineUtils.execute(cl, consumer, stderr);
163        } catch (CommandLineException ex) {
164            throw new ScmException("Error while executing svn command.", ex);
165        }
166
167        if (exitCode != 0) {
168            return new ChangeLogScmResult(cl.toString(), "The svn command failed.", stderr.getOutput(), false);
169        }
170        ChangeLogSet changeLogSet = new ChangeLogSet(consumer.getModifications(), startDate, endDate);
171        changeLogSet.setStartVersion(startVersion);
172        changeLogSet.setEndVersion(endVersion);
173
174        return new ChangeLogScmResult(cl.toString(), changeLogSet);
175    }
176
177    // ----------------------------------------------------------------------
178    //
179    // ----------------------------------------------------------------------
180
181    public Commandline createCommandLine(
182            SvnScmProviderRepository repository,
183            File workingDirectory,
184            ScmBranch branch,
185            Date startDate,
186            Date endDate,
187            ScmVersion startVersion,
188            ScmVersion endVersion) {
189        return createCommandLine(
190                repository, workingDirectory, branch, startDate, endDate, startVersion, endVersion, null);
191    }
192
193    public Commandline createCommandLine(
194            SvnScmProviderRepository repository,
195            File workingDirectory,
196            ScmBranch branch,
197            Date startDate,
198            Date endDate,
199            ScmVersion startVersion,
200            ScmVersion endVersion,
201            Integer limit) {
202        SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
203
204        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
205
206        Commandline cl = SvnCommandLineUtils.getBaseSvnCommandLine(workingDirectory, repository, interactive);
207
208        cl.createArg().setValue("log");
209
210        cl.createArg().setValue("-v");
211
212        // TODO: May want to add some kind of support for --stop-on-copy and --limit NUM
213
214        if (limit != null && limit > 0) {
215            cl.createArg().setValue("--limit");
216            cl.createArg().setValue(Integer.toString(limit));
217        }
218
219        if (startDate != null) {
220            cl.createArg().setValue("-r");
221
222            if (endDate != null) {
223                cl.createArg()
224                        .setValue("{" + dateFormat.format(startDate) + "}" + ":" + "{" + dateFormat.format(endDate)
225                                + "}");
226            } else {
227                cl.createArg().setValue("{" + dateFormat.format(startDate) + "}:HEAD");
228            }
229        }
230
231        if (startVersion != null) {
232            cl.createArg().setValue("-r");
233
234            if (endVersion != null) {
235                if (startVersion.getName().equals(endVersion.getName())) {
236                    cl.createArg().setValue(startVersion.getName());
237                } else {
238                    cl.createArg().setValue(startVersion.getName() + ":" + endVersion.getName());
239                }
240            } else {
241                cl.createArg().setValue(startVersion.getName() + ":HEAD");
242            }
243        }
244
245        if (branch != null && StringUtils.isNotEmpty(branch.getName())) {
246            // By specifying a branch and this repository url below, subversion should show
247            // the changelog of that branch, but limit it to paths that also occur in this repository.
248            if (branch instanceof ScmTag) {
249                String tagUrl = SvnTagBranchUtils.resolveTagUrl(repository, (ScmTag) branch);
250                cl.createArg().setValue(tagUrl + "@");
251            } else {
252                String branchUrl = SvnTagBranchUtils.resolveBranchUrl(repository, branch);
253                cl.createArg().setValue(branchUrl + "@");
254            }
255        }
256
257        if (endVersion == null || !Objects.equals("BASE", endVersion.getName())) {
258            cl.createArg().setValue(repository.getUrl() + "@");
259        }
260
261        return cl;
262    }
263}