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