org.azyva.dragom.util.Util.java Source code

Java tutorial

Introduction

Here is the source code for org.azyva.dragom.util.Util.java

Source

/*
 * Copyright 2015 AZYVA INC.
 *
 * This file is part of Dragom.
 *
 * Dragom is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Dragom is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Dragom.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.azyva.dragom.util;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.cli.CommandLine;
import org.azyva.dragom.execcontext.ExecContext;
import org.azyva.dragom.execcontext.impl.DefaultExecContextFactory;
import org.azyva.dragom.execcontext.plugins.RuntimePropertiesPlugin;
import org.azyva.dragom.execcontext.plugins.UserInteractionCallbackPlugin;
import org.azyva.dragom.model.Module;
import org.azyva.dragom.model.ModuleClassificationPath;
import org.azyva.dragom.model.ModuleVersion;
import org.azyva.dragom.model.Version;
import org.azyva.dragom.model.VersionType;
import org.azyva.dragom.model.plugins.ScmPlugin;
import org.azyva.dragom.referencegraphpath.ReferenceGraphPathMatcher;
import org.azyva.dragom.referencegraphpath.ReferenceGraphPathMatcherAnd;
import org.azyva.dragom.referencegraphpath.ReferenceGraphPathMatcherByElement;
import org.azyva.dragom.referencegraphpath.ReferenceGraphPathMatcherOr;
import org.azyva.dragom.task.RootManager;
import org.json.JSONObject;

/**
 * Static utility methods.
 *
 * For now this class is a mixed bag of utility methods. With time and maturity
 * some groups of utility methods may be migrated to separate classes.
 *
 * @author David Raymond
 */
public final class Util {
    private static final String EXEC_CONTEXT_FACTORY_CLASS_INIT_PROP = "org.azyva.dragom.ExecContextFactory";

    private static final String PATH_USER_PROPERTIES_FILE_INIT_PROP = "org.azyva.dragom.UserProperties";

    /**
     * Infers a groupId segment from a module classification path.
     *
     * We talk about the groupId segment since a complete groupId will generally be
     * some prefix plus a . to which the value returned is appended.
     *
     * Only the node classification path is used. The module classification path can
     * be partial or not.
     *
     * @param moduleClassificationPath
     */
    public static String inferGroupIdSegmentFromModuleClassificationPath(
            ModuleClassificationPath moduleClassificationPath) {
        StringBuilder stringBuilder;
        int nodeCount;

        stringBuilder = new StringBuilder();
        nodeCount = moduleClassificationPath.getNodeCount();

        if (!moduleClassificationPath.isPartial()) {
            nodeCount--;
        }

        for (int i = 0; i < nodeCount; i++) {
            if (i != 0) {
                stringBuilder.append('.');
            }

            stringBuilder
                    .append(Util.convertPascalCaseToLowercaseWithDashes(moduleClassificationPath.getNodeName(i)));
        }

        return stringBuilder.toString();
    }

    /**
     * Converts a PascalCase (or camelCase) string to lowercase with dashes.
     *
     * @param stringPascalCase See description.
     * @return See description.
     */
    public static String convertPascalCaseToLowercaseWithDashes(String stringPascalCase) {
        StringBuilder stringBuilder;
        int charCount;

        stringBuilder = new StringBuilder();
        charCount = stringPascalCase.length();

        for (int i = 0; i < charCount; i++) {
            char character = stringPascalCase.charAt(i);
            if (Character.isUpperCase(character)) {
                if (i != 0) {
                    stringBuilder.append('-');
                }

                stringBuilder.append(Character.toLowerCase(character));
            } else {
                stringBuilder.append(character);
            }
        }

        return stringBuilder.toString();
    }

    public static boolean isDirectoryEmpty(Path path) {
        DirectoryStream<Path> directoryStream;

        try {
            directoryStream = Files.newDirectoryStream(path);
        } catch (IOException ioe) {
            throw new RuntimeException(ioe);
        }

        return !directoryStream.iterator().hasNext();
    }

    //TODO: The init properties include:
    //- system properties
    //- Properties from file identified by Util.PATH_USER_PROPERTIES_FILE_INIT_PROP
    //- Properties from resource META-INF/dragom.properties
    //- Property Util.EXEC_CONTEXT_FACTORY_CLASS_INIT_PROP
    public static ExecContext setupExecContext(String workspacePath) {
        String stringPathUserPropertiesFile;
        InputStream inputStream;
        Properties propertiesInit;
        String execContextFactoryClassName;
        Class<?> classExecContextFactory;
        Method methodExecContextFactoryGetInstance;
        ExecContext execContext;

        propertiesInit = new Properties(System.getProperties());

        stringPathUserPropertiesFile = propertiesInit.getProperty(Util.PATH_USER_PROPERTIES_FILE_INIT_PROP);

        if (stringPathUserPropertiesFile != null) {
            try {
                inputStream = new FileInputStream(stringPathUserPropertiesFile);
                propertiesInit.load(inputStream);
            } catch (IOException ioe) {
                throw new RuntimeException(ioe);
            }
        }

        inputStream = Util.class.getResourceAsStream("/META-INF/dragom.properties");

        if (inputStream != null) {
            try {
                propertiesInit.load(inputStream);
            } catch (IOException ioe) {
                throw new RuntimeException(ioe);
            }
        }

        if (workspacePath != null) {
            propertiesInit.setProperty(DefaultExecContextFactory.WORKSPACE_PATH_INIT_PROP, workspacePath);
        }

        execContextFactoryClassName = propertiesInit.getProperty(Util.EXEC_CONTEXT_FACTORY_CLASS_INIT_PROP);

        if (execContextFactoryClassName == null) {
            classExecContextFactory = DefaultExecContextFactory.class;
        } else {
            try {
                classExecContextFactory = Class.forName(execContextFactoryClassName);
            } catch (ClassNotFoundException cnfe) {
                throw new RuntimeException(cnfe);
            }
        }

        try {
            methodExecContextFactoryGetInstance = classExecContextFactory.getMethod("getInstance",
                    Properties.class);
            execContext = (ExecContext) methodExecContextFactoryGetInstance.invoke(null, propertiesInit);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }

        return execContext;
    }

    /**
     * Helper method to retrieve commit attributes from a commit message.
     *
     * These attributes are stored in a JSONObject at the beginning of the commit
     * message. The commit message must therefore start with '{' and continue to a
     * matching closing '}'. For now, embedded JSONObject or '}' are not supported.
     *
     * If the commit message does not start with '{', an empty Map is returned.
     *
     * @param commitMessage
     * @return Map of attributes.
     */
    public static Map<String, String> getCommitAttr(String commitMessage) {
        int indexClosingBrace;
        JSONObject jsonObjectAttributes;
        Map<String, String> mapCommitAttr;

        if ((commitMessage.length() == 0) || (commitMessage.charAt(0) != '{')) {
            return Collections.<String, String>emptyMap();
        }

        indexClosingBrace = commitMessage.indexOf('}');
        jsonObjectAttributes = new JSONObject(commitMessage.substring(0, indexClosingBrace + 1));

        mapCommitAttr = new HashMap<String, String>();

        for (String name : JSONObject.getNames(jsonObjectAttributes)) {
            mapCommitAttr.put(name, jsonObjectAttributes.getString(name));
        }

        return mapCommitAttr;
    }

    /**
     * Helper method to remove commit attributes from a commit message.
     *
     * These attributes are stored in a JSONObject at the beginning of the commit
     * message. The commit message must therefore start with '{' and continue to a
     * matching closing '}'. For now, embedded JSONObject or '}' are not supported.
     *
     * If the commit message does not start with '{', the message itself is returned.
     *
     * @param commitMessage Commit message.
     * @return Commit message with attributes removed.
     */
    public static String getCommitMessageWithoutAttr(String commitMessage) {
        int indexClosingBrace;

        if ((commitMessage.length() == 0) || (commitMessage.charAt(0) != '{')) {
            return commitMessage;
        }

        indexClosingBrace = commitMessage.indexOf('}');

        return commitMessage.substring(indexClosingBrace + 1);
    }

    /**
     * Facilitates interpreting Boolean objects.
     *
     * @param aBoolean Boolean.
     * @return true if aBoolean is not null and true (null is assumed to mean false).
     */
    public static boolean isNotNullAndTrue(Boolean aBoolean) {
        return ((aBoolean != null) && aBoolean.booleanValue());
    }

    /**
     * Facilitates interpreting Boolean objects.
     *
     * @param stringBoolean null, "true" or "false".
     * @return true if stringBoolean is not null and "true" (null is assumed to mean
     *   false).
     */
    public static boolean isNotNullAndTrue(String stringBoolean) {
        return ((stringBoolean != null) && Boolean.valueOf(stringBoolean));
    }

    /**
     * Facilitates letting the user input a AlwaysNeverAskUserResponse.
     *
     * If idInfo ends with '*' it is replaced with
     * " (Y(es always), N(ever), A(sk again)) [<default>]? " where <default> is
     * "Y", "N" or "A" depending on alwaysNeverAskUserResponseDefaultValue.
     *
     * As a convenience, "1" and "0" can also be entered by the user to mean "Yes
     * always" and "Never" respectively.
     *
     * The user can also enter the string value of the enum constants ("ALWAYS",
     * "NEVER" and "ASK").
     *
     * @param userInteractionCallbackPlugin UserInteractionCallbackPlugin.
     * @param idInfo See corresponding parameter in
     *   UserInteractionCallbackPlugin.getInfo.
     * @param alwaysNeverAskUserResponseDefaultValue Default user response. It is
     *   translated to the defaultValue parameter in
     *   UserInteractionCallbackPlugin.getInfo.
     * @param arrayParam See corresponding parameter in
     *   UserInteractionCallbackPlugin.getInfo.
     * @return AlwaysNeverAskUserResponse.
     */
    public static AlwaysNeverAskUserResponse getInfoAlwaysNeverAskUserResponse(
            UserInteractionCallbackPlugin userInteractionCallbackPlugin, String idInfo,
            AlwaysNeverAskUserResponse alwaysNeverAskUserResponseDefaultValue, Object... arrayParam) {
        String userResponse;
        AlwaysNeverAskUserResponse alwaysNeverAskUserResponse;

        if (idInfo.endsWith("*")) {
            String defaultValue = null;

            switch (alwaysNeverAskUserResponseDefaultValue) {
            case ALWAYS:
                defaultValue = "Y";
                break;

            case NEVER:
                defaultValue = "N";
                break;

            case ASK:
                defaultValue = "A";
                break;
            }

            idInfo = idInfo.substring(0, idInfo.length() - 1) + " (Y(es always), N(ever), A(sk again)) ["
                    + defaultValue + "]? ";
        }

        do {
            userResponse = userInteractionCallbackPlugin.getInfoWithDefault(idInfo,
                    alwaysNeverAskUserResponseDefaultValue.toString(), arrayParam);

            userResponse = userResponse.toUpperCase().trim();

            alwaysNeverAskUserResponse = null;

            try {
                alwaysNeverAskUserResponse = AlwaysNeverAskUserResponse.valueOf(userResponse);
            } catch (IllegalArgumentException iae) {
                if (userResponse.equals("A")) {
                    alwaysNeverAskUserResponse = AlwaysNeverAskUserResponse.ASK;
                } else if (userResponse.equals("N")) {
                    alwaysNeverAskUserResponse = AlwaysNeverAskUserResponse.NEVER;
                } else if (userResponse.equals("Y")) {
                    alwaysNeverAskUserResponse = AlwaysNeverAskUserResponse.ALWAYS;
                }
            }

            if (alwaysNeverAskUserResponse == null) {
                userInteractionCallbackPlugin.provideInfo("Invalid response. Please try again.");
                continue;
            }
        } while (false);

        return alwaysNeverAskUserResponse;
    }

    /**
     * This is an extension to the method getInfoAlwaysNeverAskUserResponse that
     * handles storing the user response in a runtime property.
     *
     * First it gets the value of the runtime property runtimeProperty. If it is
     * AlwaysNeverAskUserResponse.ASK it delegates to the
     * getInfoAlwaysNeverAskUserResponse method. If the user does not respond
     * AlwaysNeverAskUserResponse.ASK, the response is written back to the runtime
     * property.
     *
     * This is at the limit of being generic. It is very specific to one way of
     * handling AlwaysNeverAskUserResponse user input together with a runtime
     * property. But this idiom occurs in many places in Dragom and it was deemed
     * worth factoring it out.
     *
     * @param runtimePropertiesPlugin RuntimePropertiesPlugin.
     * @param module Module.
     * @param runtimeProperty Runtime property.
     * @param userInteractionCallbackPlugin See corresponding parameter in
     *   getInfoAlwaysNeverAskUserResponse.
     * @param idInfo See corresponding parameter in
     *   getInfoAlwaysNeverAskUserResponse.
     * @param alwaysNeverAskUserResponseDefaultValue See corresponding parameter in
     *   getInfoAlwaysNeverAskUserResponse.
     * @param arrayParam See corresponding parameter in
     *   getInfoAlwaysNeverAskUserResponse.
     * @return AlwaysNeverAskUserResponse.
     */
    public static AlwaysNeverAskUserResponse getInfoAlwaysNeverAskUserResponseAndHandleAsk(
            RuntimePropertiesPlugin runtimePropertiesPlugin, Module module, String runtimeProperty,
            UserInteractionCallbackPlugin userInteractionCallbackPlugin, String idInfo,
            AlwaysNeverAskUserResponse alwaysNeverAskUserResponseDefaultValue, Object... arrayParam) {
        AlwaysNeverAskUserResponse alwaysNeverAskUserResponse;

        alwaysNeverAskUserResponse = AlwaysNeverAskUserResponse
                .valueOfWithAskDefault(runtimePropertiesPlugin.getProperty(module, runtimeProperty));

        if (alwaysNeverAskUserResponse.isAsk()) {
            alwaysNeverAskUserResponse = Util.getInfoAlwaysNeverAskUserResponse(userInteractionCallbackPlugin,
                    idInfo, alwaysNeverAskUserResponseDefaultValue, arrayParam);

            if (!alwaysNeverAskUserResponse.isAsk()) {
                runtimePropertiesPlugin.setProperty(module, runtimeProperty, alwaysNeverAskUserResponse.toString(),
                        true);
            }
        }

        return alwaysNeverAskUserResponse;
    }

    /**
     * Facilitates letting the user input a YesAlwaysNoUserResponse.
     *
     * If idInfo ends with '*' it is replaced with
     * " (Y(es), A(ways), N(o)) [<default>]? " where <default> is
     * "Y", "A" or "N" depending on yesAlwaysNoUserResponseDefaultValue.
     *
     * As a convenience, "1" and "0" can also be entered by the user to mean "Yes"
     * and "No" respectively.
     *
     * The user can also enter the string value of the enum constants ("YES",
     * "YES_ALWAYS" and "NO").
     *
     * @param userInteractionCallbackPlugin UserInteractionCallbackPlugin.
     * @param idInfo See corresponding parameter in
     *   UserInteractionCallbackPlugin.getInfo.
     * @param yesAlwaysNoUserResponseDefaultValue Default user response. It is
     *   translated to the defaultValue parameter in
     *   UserInteractionCallbackPlugin.getInfo.
     * @param arrayParam See corresponding parameter in
     *   UserInteractionCallbackPlugin.getInfo.
     * @return YesAlwaysNoUserResponse.
     */
    public static YesAlwaysNoUserResponse getInfoYesAlwaysNoUserResponse(
            UserInteractionCallbackPlugin userInteractionCallbackPlugin, String idInfo,
            YesAlwaysNoUserResponse yesAlwaysNoUserResponseDefaultValue, Object... arrayParam) {
        String userResponse;
        YesAlwaysNoUserResponse yesAlwaysNoUserResponse;

        if (idInfo.endsWith("*")) {
            String defaultValue = null;

            switch (yesAlwaysNoUserResponseDefaultValue) {
            case YES:
                defaultValue = "Y";
                break;

            case YES_ALWAYS:
                defaultValue = "A";
                break;

            case NO:
                defaultValue = "N";
                break;
            }

            idInfo = idInfo.substring(0, idInfo.length() - 1) + " (Y(es), A(lways), N(o)) [" + defaultValue + "]? ";
        }

        do {
            userResponse = userInteractionCallbackPlugin.getInfoWithDefault(idInfo,
                    yesAlwaysNoUserResponseDefaultValue.toString(), arrayParam);

            userResponse = userResponse.toUpperCase().trim();

            yesAlwaysNoUserResponse = null;

            try {
                yesAlwaysNoUserResponse = YesAlwaysNoUserResponse.valueOf(userResponse);
            } catch (IllegalArgumentException iae) {
                if (userResponse.equals("Y") || userResponse.equals("1")) {
                    yesAlwaysNoUserResponse = YesAlwaysNoUserResponse.YES;
                } else if (userResponse.equals("A")) {
                    yesAlwaysNoUserResponse = YesAlwaysNoUserResponse.YES_ALWAYS;
                } else if (userResponse.equals("N") || userResponse.equals("0")) {
                    yesAlwaysNoUserResponse = YesAlwaysNoUserResponse.NO;
                }
            }

            if (yesAlwaysNoUserResponse == null) {
                userInteractionCallbackPlugin.provideInfo("Invalid response. Please try again.");
                continue;
            }
        } while (false);

        return yesAlwaysNoUserResponse;
    }

    /**
     * This method is very similar to getInfoYesAlwaysNoUserResponse except that it
     * does not support the "Always" response. It still returns a
     * YesAlwaysNoUserResponse but YesAlwaysNoUserResponse.YES_ALWAYS will not be
     * returned.
     *
     * @param userInteractionCallbackPlugin UserInteractionCallbackPlugin.
     * @param idInfo See corresponding parameter in
     *   UserInteractionCallbackPlugin.getInfo.
     * @param yesAlwaysNoUserResponseDefaultValue Default user response. It is
     *   translated to the defaultValue parameter in
     *   UserInteractionCallbackPlugin.getInfo.
     * @param arrayParam See corresponding parameter in
     *   UserInteractionCallbackPlugin.getInfo.
     * @return YesAlwaysNoUserResponse.
     */
    public static YesAlwaysNoUserResponse getInfoYesNoUserResponse(
            UserInteractionCallbackPlugin userInteractionCallbackPlugin, String idInfo,
            YesAlwaysNoUserResponse yesAlwaysNoUserResponseDefaultValue, Object... arrayParam) {
        String userResponse;
        YesAlwaysNoUserResponse yesAlwaysNoUserResponse;

        if (idInfo.endsWith("*")) {
            String defaultValue = null;

            switch (yesAlwaysNoUserResponseDefaultValue) {
            case YES:
                defaultValue = "Y";
                break;

            case YES_ALWAYS:
                throw new RuntimeException("YES_ALWAYS is not supported by this method.");

            case NO:
                defaultValue = "N";
                break;
            }

            idInfo = idInfo.substring(0, idInfo.length() - 1) + " (Y(es), N(o)) [" + defaultValue + "]? ";
        }

        do {
            userResponse = userInteractionCallbackPlugin.getInfoWithDefault(idInfo,
                    yesAlwaysNoUserResponseDefaultValue.toString(), arrayParam);

            userResponse = userResponse.toUpperCase().trim();

            yesAlwaysNoUserResponse = null;

            try {
                yesAlwaysNoUserResponse = YesAlwaysNoUserResponse.valueOf(userResponse);

                if (yesAlwaysNoUserResponse == YesAlwaysNoUserResponse.YES_ALWAYS) {
                    yesAlwaysNoUserResponse = null;
                }
            } catch (IllegalArgumentException iae) {
                if (userResponse.equals("Y") || userResponse.equals("1")) {
                    yesAlwaysNoUserResponse = YesAlwaysNoUserResponse.YES;
                } else if (userResponse.equals("N") || userResponse.equals("0")) {
                    yesAlwaysNoUserResponse = YesAlwaysNoUserResponse.NO;
                }
            }

            if (yesAlwaysNoUserResponse == null) {
                userInteractionCallbackPlugin.provideInfo("Invalid response. Please try again.");
                continue;
            }
        } while (false);

        return yesAlwaysNoUserResponse;
    }

    /**
     * Facilitates letting the user input a Version.
     *
     * If idInfo ends with '*' it is replaced with text that gides the user in
     * inputting the Version.
     *
     * If scmPlugin is not null that text is
     * " (use the format <format>/<version>; version must exist) [<default>]? "
     * where <format> is "S", "D" or "S/D" depending on whether versionType is
     * VersionType.STATIC, VersionType.DYNAMIC or null, and where <default> is
     * versionDefaultValue. If versionDefaultValue is null, the last part
     * " [<default>]" is not included.
     *
     * If scmPlugin is null that text is similar, but excludes that part saying
     * that the Version must exist.
     *
     * If versionType is not null, the type of Version will be validated to be as
     * such.
     *
     * If scmPlugin is not null, the existence of the Version will be validated.
     *
     * @param versionType
     * @param scmPlugin
     * @param userInteractionCallbackPlugin UserInteractionCallbackPlugin.
     * @param idInfo See corresponding parameter in
     *   UserInteractionCallbackPlugin.getInfo.
     * @param versionDefaultValue Default user response. It is translated to the
     *   defaultValue parameter in UserInteractionCallbackPlugin.getInfo.
     * @param arrayParam See corresponding parameter in
     *   UserInteractionCallbackPlugin.getInfo.
     * @return Version.
     */
    public static Version getInfoVersion(VersionType versionType, ScmPlugin scmPlugin,
            UserInteractionCallbackPlugin userInteractionCallbackPlugin, String idInfo, Version versionDefaultValue,
            Object... arrayParam) {
        String userResponse;
        Version version = null;

        if (idInfo.endsWith("*")) {
            String stringVersionType;
            String versionMayOrMayNotExist;
            String defaultValue;

            if (versionType == null) {
                stringVersionType = "S/D";
            } else if (versionType == VersionType.STATIC) {
                stringVersionType = "S/D";
            } else if (versionType == VersionType.DYNAMIC) {
                stringVersionType = "D";
            } else {
                throw new RuntimeException("Should never get here.");
            }

            if (scmPlugin != null) {
                versionMayOrMayNotExist = "; version must exist";
            } else {
                versionMayOrMayNotExist = "";
            }

            if (versionDefaultValue != null) {
                defaultValue = " [" + versionDefaultValue.toString() + "]";
            } else {
                defaultValue = "";
            }

            idInfo = idInfo.substring(0, idInfo.length() - 1) + " (use the format " + stringVersionType
                    + "/<version>" + versionMayOrMayNotExist + ")" + defaultValue + "? ";
        }

        do {
            if (versionDefaultValue != null) {
                userResponse = userInteractionCallbackPlugin.getInfoWithDefault(idInfo,
                        versionDefaultValue.toString(), arrayParam);
            } else {
                userResponse = userInteractionCallbackPlugin.getInfo(idInfo, arrayParam);
            }

            try {
                version = Version.parse(userResponse);
            } catch (Exception e) {
                userInteractionCallbackPlugin.provideInfo(e.getMessage());
                userInteractionCallbackPlugin.provideInfo("Please try again.");
                continue;
            }

            if ((versionType != null) && (version.getVersionType() != versionType)) {
                userInteractionCallbackPlugin.provideInfo("The version is " + version.getVersionType()
                        + " which is not the expected type " + versionType + ".");
                userInteractionCallbackPlugin.provideInfo("Please try again.");
                continue;
            }

            if (scmPlugin != null) {
                if (scmPlugin.isVersionExists(version)) {
                    userInteractionCallbackPlugin.provideInfo("The version " + version + " does not exist.");
                    userInteractionCallbackPlugin.provideInfo("Please try again.");
                }
            }
        } while (false);

        return version;
    }

    /**
     * Helper method to return the List of root ModuleVersion's used by many tools.
     *
     * If the command line specifies the --root-module-version option, no root
     * ModuleVersions's must be specified by RootManager, and the List of root
     * ModuleVerion's contains the single ModuleVersion specified by this option.
     *
     * Otherwise, RootManager must specify at least one root ModuleVersion and this
     * List of root ModuleVersion's specified by RootManager is returned.
     *
     * @param commandLine CommandLine.
     * @return List of root ModuleVersion's.
     */
    public static List<ModuleVersion> getListModuleVersionRoot(CommandLine commandLine) {
        List<ModuleVersion> listModuleVersionRoot;

        if (commandLine.hasOption("root-module-version")) {
            if (!RootManager.getListModuleVersion().isEmpty()) {
                throw new RuntimeExceptionUserError(
                        "No root module version can be specified on the command line with the --root-module-version option when one is specified in the workspace. Use the --help option to display help information.");
            }

            listModuleVersionRoot = new ArrayList<ModuleVersion>();

            listModuleVersionRoot.add(ModuleVersion.parse(commandLine.getOptionValue("root-module-version")));
        } else {
            if (RootManager.getListModuleVersion().isEmpty()) {
                throw new RuntimeExceptionUserError(
                        "A root module version must be specified on the command line with the --root-module-version option when none is specified in the workspace. Use the --help option to display help information.");
            }

            listModuleVersionRoot = RootManager.getListModuleVersion();
        }

        return listModuleVersionRoot;

    }

    /**
     * Helper method to return a ReferenceGraphPathMatcherAnd that is built from the
     * ReferenceGraphPathMatcherOr specified by RootManager and a
     * ReferenceGraphPathMatcherOr built from the command line options
     * --reference-graph-path-matcher that specify ReferenceGraphPathMatcherByElement
     * literals.
     *
     * @param commandLine CommandLine.
     * @return ReferenceGraphPathMatcher.
     */
    public static ReferenceGraphPathMatcher getReferenceGraphPathMatcher(CommandLine commandLine) {
        String[] arrayStringReferenceGraphPathMatcher;
        ReferenceGraphPathMatcherOr referenceGraphPathMatcherOrCommandLine;
        ReferenceGraphPathMatcherAnd referenceGraphPathMatcherAnd;

        arrayStringReferenceGraphPathMatcher = commandLine.getOptionValues("reference-graph-path-matcher");

        if (arrayStringReferenceGraphPathMatcher == null) {
            throw new RuntimeExceptionUserError(
                    "At least one --reference-graph-path-matcher option must be specified on the command line. Use the --help option to display help information.");
        }

        referenceGraphPathMatcherOrCommandLine = new ReferenceGraphPathMatcherOr();

        for (int i = 0; i < arrayStringReferenceGraphPathMatcher.length; i++) {
            referenceGraphPathMatcherOrCommandLine.addReferenceGraphPathMatcher(
                    ReferenceGraphPathMatcherByElement.parse(arrayStringReferenceGraphPathMatcher[i]));
        }

        referenceGraphPathMatcherAnd = new ReferenceGraphPathMatcherAnd();

        referenceGraphPathMatcherAnd.addReferenceGraphPathMatcher(RootManager.getReferenceGraphPathMatcherOr());
        referenceGraphPathMatcherAnd.addReferenceGraphPathMatcher(referenceGraphPathMatcherOrCommandLine);

        return referenceGraphPathMatcherAnd;
    }
}