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.hg;
020
021import java.io.File;
022
023import org.apache.maven.scm.ScmException;
024import org.apache.maven.scm.ScmFileStatus;
025import org.apache.maven.scm.provider.hg.command.HgCommandConstants;
026import org.apache.maven.scm.provider.hg.command.HgConsumer;
027import org.codehaus.plexus.util.cli.Commandline;
028
029/**
030 * Check hg installation.
031 *
032 * @author <a href="mailto:thurner.rupert@ymono.net">thurner rupert</a>
033 * @author <a href="mailto:ryan@darksleep.com">ryan daum</a>
034 *
035 */
036public class HgConfig {
037    // Minimum version for the Hg SCM
038    private static final String HG_REQ = "0.9.2";
039
040    // The string which indicates the beginning of the Mercurial line
041    private static final String HG_VERSION_TAG = "Mercurial Distributed SCM (version ";
042
043    // URL to download mercurial from
044    private static final String HG_INSTALL_URL = "'http://www.selenic.com/mercurial/wiki/index.cgi/Download'";
045
046    // Configuration to check with default values (not installed)
047    private HgVersionConsumer hgVersion = new HgVersionConsumer(null);
048
049    HgConfig(File workingDir) {
050        try {
051            hgVersion = getHgVersion(workingDir);
052        } catch (ScmException e) {
053            // Ignore -  is not installed.
054            // This is already recorded thus we do not generate more info.
055        }
056    }
057
058    /**
059     * @return True if one can run basic hg commands
060     */
061    private boolean isInstalled() {
062        return hgVersion.isVersionOk(HG_REQ);
063    }
064
065    /**
066     * @return True if all modules for hg are installed.
067     */
068    private boolean isComplete() {
069        return isInstalled();
070    }
071
072    // Consumer to find the Mercurial version
073    public static HgVersionConsumer getHgVersion(File workingDir) throws ScmException {
074        String[] versionCmd = new String[] {HgCommandConstants.VERSION};
075        HgVersionConsumer consumer = new HgVersionConsumer(HG_VERSION_TAG);
076        Commandline cmd = HgUtils.buildCmd(workingDir, versionCmd);
077
078        // Execute command
079        HgUtils.executeCmd(consumer, cmd);
080
081        // Return result
082        return consumer;
083    }
084
085    /**
086     * Iterate through two dot-notation version strings, normalize them to the same length, then
087     * do alphabetic comparison
088     *
089     * @param version1
090     * @param version2
091     * @return true if version2 is greater than version1
092     */
093    private static boolean compareVersion(String version1, String version2) {
094        int l1, l2;
095        String v1, v2;
096
097        v1 = version1;
098        v2 = version2;
099        l1 = version1.length();
100        l2 = version2.length();
101
102        if (l1 > l2) {
103            for (int x = l2; x >= l1; x--) {
104                v2 += ' ';
105            }
106        }
107        if (l2 > l1) {
108            for (int x = l1; x <= l2; x++) {
109                v1 += ' ';
110            }
111        }
112
113        return v2.compareTo(v1) >= 0;
114    }
115
116    /**
117     * Get version of the executable.
118     * Version is resolved by splitting the line starting with the version tag and finding
119     * the second last word.
120     */
121    private static class HgVersionConsumer extends HgConsumer {
122
123        private String versionStr = "NA";
124
125        private String versionTag;
126
127        HgVersionConsumer(String versionTag) {
128            this.versionTag = versionTag;
129        }
130
131        public void doConsume(ScmFileStatus status, String line) {
132            if (line.startsWith(versionTag)) {
133                String[] elements = line.split(" ");
134                versionStr = elements[elements.length - 1].split("\\)")[0];
135            }
136        }
137
138        String getVersion() {
139            return versionStr;
140        }
141
142        boolean isVersionOk(String version) {
143            // build one number out of the whole version #
144
145            return compareVersion(version, versionStr);
146        }
147    }
148
149    private String getInstalledStr() {
150        if (isComplete()) {
151            return "valid and complete.";
152        }
153        return (isInstalled() ? "incomplete. " : "invalid. ") + "Consult " + HG_INSTALL_URL;
154    }
155
156    public String toString(File workingDir) {
157        boolean hgOk = hgVersion.isVersionOk(HG_REQ);
158        return "\n  Your Hg installation seems to be " + getInstalledStr() + "\n    Hg version: "
159                + hgVersion.getVersion() + (hgOk ? " (OK)" : " (May be INVALID)") + "\n";
160    }
161}