All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openl.rules.ui.ProjectModel Maven / Gradle / Ivy

There is a newer version: 5.27.9
Show newest version
package org.openl.rules.ui;

import java.io.File;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.openl.CompiledOpenClass;
import org.openl.OpenClassUtil;
import org.openl.base.INamedThing;
import org.openl.dependency.CompiledDependency;
import org.openl.dependency.ResolvedDependency;
import org.openl.message.OpenLMessage;
import org.openl.message.OpenLMessagesUtils;
import org.openl.message.Severity;
import org.openl.meta.IMetaInfo;
import org.openl.rules.common.ProjectException;
import org.openl.rules.dependency.graph.DependencyRulesGraph;
import org.openl.rules.lang.xls.OverloadedMethodsDictionary;
import org.openl.rules.lang.xls.XlsNodeTypes;
import org.openl.rules.lang.xls.XlsWorkbookListener;
import org.openl.rules.lang.xls.XlsWorkbookSourceCodeModule;
import org.openl.rules.lang.xls.binding.XlsMetaInfo;
import org.openl.rules.lang.xls.binding.XlsModuleOpenClass;
import org.openl.rules.lang.xls.binding.wrapper.WrapperLogic;
import org.openl.rules.lang.xls.syntax.TableSyntaxNode;
import org.openl.rules.lang.xls.syntax.TableSyntaxNodeAdapter;
import org.openl.rules.lang.xls.syntax.WorkbookSyntaxNode;
import org.openl.rules.lang.xls.syntax.XlsModuleSyntaxNode;
import org.openl.rules.method.ExecutableRulesMethod;
import org.openl.rules.project.abstraction.AProjectArtefact;
import org.openl.rules.project.abstraction.AProjectFolder;
import org.openl.rules.project.abstraction.RulesProject;
import org.openl.rules.project.dependencies.ProjectExternalDependenciesHelper;
import org.openl.rules.project.impl.local.LocalRepository;
import org.openl.rules.project.instantiation.AbstractDependencyManager;
import org.openl.rules.project.instantiation.IDependencyLoader;
import org.openl.rules.project.instantiation.ReloadType;
import org.openl.rules.project.instantiation.RulesInstantiationException;
import org.openl.rules.project.instantiation.RulesInstantiationStrategy;
import org.openl.rules.project.instantiation.SimpleMultiModuleInstantiationStrategy;
import org.openl.rules.project.model.Module;
import org.openl.rules.project.model.PathEntry;
import org.openl.rules.project.model.ProjectDescriptor;
import org.openl.rules.project.resolving.ProjectDescriptorBasedResolvingStrategy;
import org.openl.rules.project.resolving.ProjectResolver;
import org.openl.rules.project.validation.openapi.OpenApiProjectValidator;
import org.openl.rules.repository.api.BranchRepository;
import org.openl.rules.repository.api.Repository;
import org.openl.rules.rest.ProjectHistoryService;
import org.openl.rules.source.impl.VirtualSourceCodeModule;
import org.openl.rules.table.CompositeGrid;
import org.openl.rules.table.IGridTable;
import org.openl.rules.table.IOpenLTable;
import org.openl.rules.table.properties.ITableProperties;
import org.openl.rules.table.xls.XlsUrlParser;
import org.openl.rules.tableeditor.model.TableEditorModel;
import org.openl.rules.testmethod.ProjectHelper;
import org.openl.rules.testmethod.TestSuite;
import org.openl.rules.testmethod.TestSuiteExecutor;
import org.openl.rules.testmethod.TestSuiteMethod;
import org.openl.rules.testmethod.TestUnitsResults;
import org.openl.rules.types.OpenMethodDispatcher;
import org.openl.rules.ui.tree.OpenMethodsGroupTreeNodeBuilder;
import org.openl.rules.ui.tree.ProjectTreeNode;
import org.openl.rules.ui.tree.TreeNodeBuilder;
import org.openl.rules.ui.tree.WorksheetTreeNodeBuilder;
import org.openl.rules.ui.tree.richfaces.TreeNode;
import org.openl.rules.validation.properties.dimentional.DispatcherTablesBuilder;
import org.openl.rules.webstudio.dependencies.WebStudioWorkspaceDependencyManagerFactory;
import org.openl.rules.webstudio.dependencies.WebStudioWorkspaceRelatedDependencyManager;
import org.openl.rules.webstudio.web.Props;
import org.openl.rules.webstudio.web.SearchScope;
import org.openl.rules.webstudio.web.admin.AdministrationSettings;
import org.openl.rules.webstudio.web.trace.node.CachingArgumentsCloner;
import org.openl.rules.webstudio.web.util.Constants;
import org.openl.rules.webstudio.web.util.WebStudioUtils;
import org.openl.rules.workspace.lw.impl.FolderHelper;
import org.openl.rules.workspace.uw.UserWorkspace;
import org.openl.security.acl.permission.AclPermission;
import org.openl.types.IMemberMetaInfo;
import org.openl.types.IOpenClass;
import org.openl.types.IOpenMethod;
import org.openl.types.NullOpenClass;
import org.openl.util.FileUtils;

public class ProjectModel {

    private final Logger log = LoggerFactory.getLogger(ProjectModel.class);

    private static final Comparator DEFAULT_NODE_CMP = Comparator.comparing(
            node -> Optional.ofNullable(node.getMember()).map(INamedThing::getName).orElse(null),
            Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER));

    /**
     * Compiled rules with errors. Representation of wrapper.
     */
    private volatile CompiledOpenClass compiledOpenClass;
    private volatile CompiledOpenClass openedModuleCompiledOpenClass;
    private volatile boolean compilationInProgress;
    private volatile ResolvedDependency projectCompilationCompleted;

    private XlsModuleSyntaxNode xlsModuleSyntaxNode;
    private final Map> xlsModuleSyntaxNodesPerProject = new ConcurrentHashMap<>();
    private final Collection xlsModuleSyntaxNodes = ConcurrentHashMap.newKeySet();

    private Module moduleInfo;
    private long moduleLastModified;

    private final WebStudioWorkspaceDependencyManagerFactory webStudioWorkspaceDependencyManagerFactory;
    private WebStudioWorkspaceRelatedDependencyManager webStudioWorkspaceDependencyManager;

    private final WebStudio studio;

    private final ColorFilterHolder filterHolder = new ColorFilterHolder();

    private TreeNode projectRoot = null;

    private DependencyRulesGraph dependencyGraph;
    private String historyStoragePath;

    private final RecentlyVisitedTables recentlyVisitedTables = new RecentlyVisitedTables();
    private final TestSuiteExecutor testSuiteExecutor;

    /**
     * For tests only
     */
    ProjectModel(WebStudio studio) {
        this(studio, null);
    }

    public ProjectModel(WebStudio studio, TestSuiteExecutor testSuiteExecutor) {
        this.studio = studio;
        this.webStudioWorkspaceDependencyManagerFactory = new WebStudioWorkspaceDependencyManagerFactory(studio);
        this.testSuiteExecutor = testSuiteExecutor;
    }

    public synchronized RulesProject getProject() {
        return studio.getCurrentProject();
    }

    public synchronized TableSyntaxNode findNode(String url) {
        XlsUrlParser parsedUrl = new XlsUrlParser(url);

        return findNode(parsedUrl);
    }

    private boolean findInCompositeGrid(CompositeGrid compositeGrid, XlsUrlParser p1) {
        for (IGridTable gridTable : compositeGrid.getGridTables()) {
            if (gridTable.getGrid() instanceof CompositeGrid) {
                if (findInCompositeGrid((CompositeGrid) gridTable.getGrid(), p1)) {
                    return true;
                }
            } else {
                if (p1.intersects(gridTable.getUriParser())) {
                    return true;
                }
            }
        }
        return false;
    }

    private TableSyntaxNode findNode(XlsUrlParser p1) {
        Collection nodes = getAllTableSyntaxNodes();
        for (TableSyntaxNode node : nodes) {
            if (p1.intersects(node.getGridTable().getUriParser())) {
                if (XlsNodeTypes.XLS_TABLEPART.equals(node.getNodeType())) {
                    for (TableSyntaxNode tableSyntaxNode : nodes) {
                        IGridTable table = tableSyntaxNode.getGridTable();
                        if (table.getGrid() instanceof CompositeGrid) {
                            CompositeGrid compositeGrid = (CompositeGrid) table.getGrid();
                            if (findInCompositeGrid(compositeGrid, p1)) {
                                return tableSyntaxNode;
                            }
                        }
                    }
                }
                return node;
            }
        }

        return null;
    }

    public synchronized int getErrorNodesNumber() {
        int count = 0;
        Collection> messages = getModuleMessages().stream()
                .map(e -> Pair.of(e, e.getSourceLocation() != null ? new XlsUrlParser(e.getSourceLocation()) : null))
                .collect(Collectors.toList());
        for (TableSyntaxNode tsn : getTableSyntaxNodes()) {
            for (Pair pair : messages) {
                if (pair.getRight() != null && pair.getLeft().getSeverity() == Severity.ERROR) {
                    if (pair.getRight().intersects(tsn.getUriParser())) {
                        count++;
                        break;
                    }
                }
            }
        }
        return count;
    }

    public synchronized TableSyntaxNode getTableByUri(String uri) {
        for (TableSyntaxNode tableSyntaxNode : getTableSyntaxNodes()) {
            if (Objects.equals(uri, tableSyntaxNode.getUri())) {
                return tableSyntaxNode;
            }
        }
        for (TableSyntaxNode tableSyntaxNode : getAllTableSyntaxNodes()) {
            if (Objects.equals(uri, tableSyntaxNode.getUri())) {
                return tableSyntaxNode;
            }
        }
        return null;
    }

    public synchronized TableSyntaxNode getNodeById(String id) {
        for (TableSyntaxNode tableSyntaxNode : getTableSyntaxNodes()) {
            if (Objects.equals(id, tableSyntaxNode.getId())) {
                return tableSyntaxNode;
            }
        }
        for (TableSyntaxNode tableSyntaxNode : getAllTableSyntaxNodes()) {
            if (Objects.equals(id, tableSyntaxNode.getId())) {
                return tableSyntaxNode;
            }
        }
        return null;
    }

    public synchronized List getWarnsByUri(String uri) {
        return getMessagesByTsn(uri, Severity.WARN);
    }

    private List getMessagesByTsn(TableSyntaxNode tableSyntaxNode,
                                                Severity severity,
                                                Collection openLMessages) {
        List messages = new ArrayList<>();
        for (OpenLMessage openLMessage : openLMessages) {
            if (openLMessage.getSourceLocation() != null && openLMessage.getSeverity() == severity) {
                XlsUrlParser xlsUrlParser = new XlsUrlParser(openLMessage.getSourceLocation());
                if (tableSyntaxNode.getUriParser().intersects(xlsUrlParser)) {
                    messages.add(openLMessage);
                }
            }
        }
        return messages;
    }

    public List getMessagesByTsn(String uri, Severity severity) {
        TableSyntaxNode tableSyntaxNode = getTableByUri(uri);
        return getMessagesByTsn(tableSyntaxNode, severity, getModuleMessages());
    }

    public List getOpenedModuleMessagesByTsn(String uri, Severity severity) {
        TableSyntaxNode tableSyntaxNode = getTableByUri(uri);
        return getMessagesByTsn(tableSyntaxNode, severity, getOpenedModuleMessages());
    }

    public synchronized List getErrorsByUri(String uri) {
        return getMessagesByTsn(uri, Severity.ERROR);
    }

    public synchronized ColorFilterHolder getFilterHolder() {
        return filterHolder;
    }

    public synchronized IOpenMethod getMethod(String tableUri) {
        if (!isCompiledSuccessfully()) {
            return null;
        }
        IOpenClass openClass = compiledOpenClass.getOpenClassWithErrors();
        return getOpenClassMethod(tableUri, openClass);
    }

    public synchronized IOpenMethod getOpenedModuleMethod(String tableUri) {
        if (!isOpenedModuleCompiledSuccessfully()) {
            return null;
        }
        IOpenClass openClass = openedModuleCompiledOpenClass.getOpenClassWithErrors();
        return getOpenClassMethod(tableUri, openClass);
    }

    public synchronized IOpenMethod getOpenClassMethod(String tableUri, IOpenClass openClass) {
        for (IOpenMethod method : openClass.getMethods()) {
            IOpenMethod resolvedMethod;

            if (method instanceof OpenMethodDispatcher) {
                resolvedMethod = resolveMethodDispatcher((OpenMethodDispatcher) method, tableUri);
                if (resolvedMethod != null) {
                    return method;
                }
            } else {
                resolvedMethod = resolveMethod(method, tableUri);
            }

            if (resolvedMethod != null) {
                return resolvedMethod;
            }
        }

        // for methods that exist in module but not included in
        // CompiledOpenClass
        // e.g. elder inactive versions of methods
        TableSyntaxNode tsn = getNode(tableUri);
        if (tsn != null && tsn.getMember() instanceof IOpenMethod) {
            return WrapperLogic.wrapOpenMethod((IOpenMethod) tsn.getMember(), (XlsModuleOpenClass) openClass, false);
        }

        return null;
    }

    private IOpenMethod resolveMethodDispatcher(OpenMethodDispatcher method, String uri) {
        List candidates = method.getCandidates();

        for (IOpenMethod candidate : candidates) {
            IOpenMethod resolvedMethod = resolveMethod(candidate, uri);

            if (resolvedMethod != null) {
                return resolvedMethod;
            }
        }

        return null;
    }

    private IOpenMethod getMethodFromDispatcher(OpenMethodDispatcher method, String uri) {
        List candidates = method.getCandidates();

        for (IOpenMethod candidate : candidates) {
            IOpenMethod resolvedMethod = resolveMethod(candidate, uri);
            if (resolvedMethod != null) {
                return WrapperLogic.wrapOpenMethod(resolvedMethod, method.getDeclaringClass(), false);
            }
        }

        return null;
    }

    private IOpenMethod resolveMethod(IOpenMethod method, String uri) {

        if (isInstanceOfTable(method, uri)) {
            if (method instanceof ExecutableRulesMethod) {
                ExecutableRulesMethod executableRulesMethod = (ExecutableRulesMethod) method;
                // Skip Alias tables to find original one
                if (executableRulesMethod.isAlias()) {
                    return null;
                }
            }
            return method;
        }

        return null;
    }

    /**
     * Checks that {@link IOpenMethod} object is instance that represents the given {@link TableSyntaxNode} object.
     * Actually, {@link IOpenMethod} object must have the same syntax node as given one. If given method is instance of
     * {@link OpenMethodDispatcher} false value will be returned.
     *
     * @param method method to check
     * @return true if {@link IOpenMethod} object represents the given table syntax node;
     * false - otherwise
     */
    private boolean isInstanceOfTable(IOpenMethod method, String uri) {

        IMemberMetaInfo metaInfo = method.getInfo();

        return metaInfo != null && uri.equals(metaInfo.getSourceUrl());
    }

    public synchronized TableSyntaxNode getNode(String tableUri) {
        TableSyntaxNode tsn = null;
        if (tableUri != null) {
            tsn = getTableByUri(tableUri);
            if (tsn == null) {
                tsn = findNode(tableUri);
            }
        }
        return tsn;
    }

    public synchronized TreeNode getProjectTree() {
        if (projectRoot == null) {
            buildProjectTree();
        }
        return projectRoot;
    }

    public synchronized IOpenLTable getTable(String tableUri) {
        TableSyntaxNode tsn = getNode(tableUri);
        if (tsn != null) {
            return new TableSyntaxNodeAdapter(tsn);
        }
        tsn = getNode(tableUri);
        if (tsn != null) {
            return new TableSyntaxNodeAdapter(tsn);
        }
        return null;

    }

    public synchronized IOpenLTable getTableById(String id) {
        if (projectRoot == null) {
            buildProjectTree();
        }
        TableSyntaxNode tsn = getNodeById(id);
        if (tsn != null) {
            return new TableSyntaxNodeAdapter(tsn);
        }
        tsn = getNodeById(id);
        if (tsn != null) {
            return new TableSyntaxNodeAdapter(tsn);
        }
        return null;
    }

    public IGridTable getGridTable(String tableUri) {
        TableSyntaxNode tsn = getNode(tableUri);
        return tsn == null ? null : tsn.getGridTable();
    }

    /**
     * Gets test methods for method by uri.
     *
     * @param forTable uri for method table
     * @return test methods
     */
    public IOpenMethod[] getTestMethods(String forTable, boolean currentOpenedModule) {
        IOpenMethod method = currentOpenedModule ? getOpenedModuleMethod(forTable) : getMethod(forTable);
        if (method != null) {
            return ProjectHelper.testers(method,
                    currentOpenedModule ? openedModuleCompiledOpenClass : compiledOpenClass);
        }
        return null;
    }

    /**
     * Gets all test methods for method by uri.
     *
     * @param tableUri uri for method table
     * @return all test methods, including tests with test cases, runs with filled runs, tests without cases(empty),
     * runs without any parameters and tests without cases and runs.
     */
    public IOpenMethod[] getTestAndRunMethods(String tableUri, boolean currentOpenedModule) {
        IOpenMethod method = getMethod(tableUri);
        if (method != null) {
            List res = new ArrayList<>();
            Collection methods;
            if (currentOpenedModule) {
                methods = openedModuleCompiledOpenClass.getOpenClassWithErrors().getMethods();
            } else {
                methods = compiledOpenClass.getOpenClassWithErrors().getMethods();
            }
            for (IOpenMethod tester : methods) {
                if (ProjectHelper.isTestForMethod(tester, method)) {
                    res.add(tester);
                }
            }
            return res.toArray(IOpenMethod.EMPTY_ARRAY);
        }
        return null;
    }

    public TestSuiteMethod[] getAllTestMethods() {
        if (isCompiledSuccessfully()) {
            return ProjectHelper.allTesters(compiledOpenClass.getOpenClassWithErrors());
        }
        return null;
    }

    public TestSuiteMethod[] getOpenedModuleTestMethods() {
        if (isOpenedModuleCompiledSuccessfully()) {
            return ProjectHelper.allTesters(openedModuleCompiledOpenClass.getOpenClassWithErrors());
        }
        return null;
    }

    public WorkbookSyntaxNode[] getWorkbookNodes() {
        if (!isCompiledSuccessfully()) {
            return null;
        }

        return getXlsModuleNode().getWorkbookSyntaxNodes();
    }

    /**
     * Get all workbooks of all modules
     *
     * @return all workbooks
     */
    public Collection getAllEditableWorkbookNodes() {
        if (!isCompiledSuccessfully()) {
            return null;
        }
        RulesProject rulesProject = getProject();
        List ret = new ArrayList<>();
        for (XlsModuleSyntaxNode xlsModuleSyntaxNode : xlsModuleSyntaxNodes) {
            String s = URLDecoder.decode(xlsModuleSyntaxNode.getModule().getUri(), StandardCharsets.UTF_8);
            s = s.substring(s.indexOf("/") + 1);
            try {
                if (studio.getDesignRepositoryAclService()
                        .isGranted(rulesProject.getArtefact(s), List.of(AclPermission.EDIT))) {
                    ret.addAll(List.of(xlsModuleSyntaxNode.getWorkbookSyntaxNodes()));
                }
            } catch (ProjectException ignored) {
            }
        }
        return ret;
    }

    public boolean isSourceModified() {
        RulesProject project = getProject();
        if (project == null || !project.isOpened()) {
            return false;
        }
        if (studio.isProjectFrozen(project.getName())) {
            log.debug("Project is saving currently. Ignore it's intermediate state.");
            return false;
        }

        if (isModified()) {
            getLocalRepository().getProjectState(moduleInfo.getRulesPath().toString()).notifyModified();
            return true;
        }
        return false;
    }

    public void resetSourceModified() {
        isModified();
    }

    public CompiledOpenClass getCompiledOpenClass() {
        return compiledOpenClass;
    }

    public CompiledOpenClass getOpenedModuleCompiledOpenClass() {
        return openedModuleCompiledOpenClass;
    }

    public synchronized ProjectCompilationStatus getCompilationStatus() {
        ProjectCompilationStatus.Builder compilationStatus = ProjectCompilationStatus.newBuilder();
        if (moduleInfo != null && moduleInfo.getWebstudioConfiguration() != null && moduleInfo
                .getWebstudioConfiguration()
                .isCompileThisModuleOnly()) {
            compilationStatus.addMessages(compiledOpenClass.getAllMessages());
            compilationStatus.setModulesCompiled(1);
            compilationStatus.addModulesCount(1);
        } else {
            if (webStudioWorkspaceDependencyManager == null) {
                return compilationStatus.build();
            }
            Collection dependencyLoaders = webStudioWorkspaceDependencyManager
                    .findAllProjectDependencyLoaders(moduleInfo.getProject());
            if (isProjectCompilationCompleted()) {
                compilationStatus.addMessages(compiledOpenClass.getAllMessages());
                dependencyLoaders.stream().filter(IDependencyLoader::isProjectLoader).forEach(e -> {
                    compilationStatus.addModulesCount(e.getProject().getModules().size());
                    compilationStatus.addModulesCompiled(e.getProject().getModules().size());
                });
            } else {
                for (IDependencyLoader dependencyLoader : dependencyLoaders) {
                    if (dependencyLoader.isProjectLoader()) {
                        if (!Objects.equals(dependencyLoader.getProject(), moduleInfo.getProject())) {
                            if (dependencyLoader.getRefToCompiledDependency() != null) {
                                compilationStatus.addMessages(
                                        dependencyLoader.getRefToCompiledDependency().getCompiledOpenClass().getMessages());
                            }
                        }
                    } else {
                        compilationStatus.addModulesCount(1);
                        if (Objects.equals(dependencyLoader.getModule().getName(), moduleInfo.getName()) && Objects
                                .equals(dependencyLoader.getProject(), moduleInfo.getProject())) {
                            // TODO possible duplicates messages here, use getMessages() instead of getAllMessages() and
                            // rewrite the algorithm to handle with it is required here
                            compilationStatus.addMessages(openedModuleCompiledOpenClass.getAllMessages())
                                    .addModulesCompiled(1);
                        } else {
                            if (dependencyLoader.getRefToCompiledDependency() != null) {
                                compilationStatus.addMessages(
                                                dependencyLoader.getRefToCompiledDependency().getCompiledOpenClass().getMessages())
                                        .addModulesCompiled(1);
                            }
                        }
                    }
                }
            }
        }
        return compilationStatus.build();
    }

    public Collection getModuleMessages() {
        CompiledOpenClass compiledOpenClass = getCompiledOpenClass();
        if (compiledOpenClass != null) {
            return compiledOpenClass.getAllMessages();
        }
        return Collections.emptyList();
    }

    public Collection getOpenedModuleMessages() {
        CompiledOpenClass openedCompiledOpenClass = getOpenedModuleCompiledOpenClass();
        if (openedCompiledOpenClass != null) {
            return openedCompiledOpenClass.getAllMessages();
        }
        return Collections.emptyList();
    }

    /**
     * @return Returns the wrapperInfo.
     */
    public Module getModuleInfo() {
        return moduleInfo;
    }

    public XlsModuleSyntaxNode getXlsModuleNode() {

        if (!isCompiledSuccessfully()) {
            return null;
        }

        return xlsModuleSyntaxNode;
    }

    private XlsModuleSyntaxNode findXlsModuleSyntaxNode(CompiledOpenClass compiledOpenClass) {
        XlsMetaInfo xmi = (XlsMetaInfo) compiledOpenClass.getOpenClassWithErrors().getMetaInfo();
        return xmi == null ? null : xmi.getXlsModuleNode();
    }

    /**
     * Returns if current project or module is read only.
     *
     * @return true if project is read only.
     */
    public boolean isEditable() {
        RulesProject currentProject = getProject();
        if (currentProject != null) {
            AProjectArtefact currentModule = null;
            try {
                if (studio.getCurrentModule() != null) {
                    currentModule = currentProject.getArtefact(studio.getCurrentModule().getRulesRootPath().getPath());
                }
            } catch (ProjectException e) {
                return false;
            }
            if (currentModule != null) {
                if (!studio.getDesignRepositoryAclService().isGranted(currentModule, List.of(AclPermission.EDIT))) {
                    return false;
                }
            }
            return isEditableProject(currentProject);
        }
        return false;
    }

    public boolean isEditableProjectDescriptor() {
        RulesProject currentProject = studio.getCurrentProject();
        if (currentProject.hasArtefact(ProjectDescriptorBasedResolvingStrategy.PROJECT_DESCRIPTOR_FILE_NAME)) {
            try {
                AProjectArtefact rulesDescriptorArtifact = currentProject
                        .getArtefact(ProjectDescriptorBasedResolvingStrategy.PROJECT_DESCRIPTOR_FILE_NAME);
                return studio.getDesignRepositoryAclService()
                        .isGranted(rulesDescriptorArtifact, List.of(AclPermission.EDIT));
            } catch (ProjectException ignored) {
                return false;
            }
        } else {
            return studio.getDesignRepositoryAclService().isGranted(currentProject, List.of(AclPermission.ADD));
        }
    }

    private boolean isEditableProject(RulesProject rulesProject) {
        return !isCurrentBranchProtected() && (rulesProject.isLocalOnly() || !rulesProject.isLocked() || rulesProject
                .isOpenedForEditing());
    }

    public boolean getCanUpdate() {
        if (isEditable()) {
            if (studio.getCurrentModule() == null) {
                RulesProject currentProject = getProject();
                return studio.getDesignRepositoryAclService()
                        .isGranted(currentProject, List.of(AclPermission.EDIT)) || studio.getDesignRepositoryAclService()
                        .isGranted(currentProject, List.of(AclPermission.ADD)) || studio.getDesignRepositoryAclService()
                        .isGranted(currentProject, List.of(AclPermission.DELETE));
            }
            return true;
        }
        return false;
    }

    /*
     * Return is editable current project
     */
    public boolean isEditableProject() {
        RulesProject currentProject = getProject();
        if (currentProject != null) {
            return isEditableProject(currentProject);
        }
        return false;
    }

    public boolean isEditableTable(String uri) {
        return !isTablePart(uri) && isEditable();
    }

    /**
     * Check is the table is partial
     */
    public boolean isTablePart(String uri) {
        IGridTable grid = this.getGridTable(uri);
        return grid != null && grid.getGrid() instanceof CompositeGrid;
    }

    public boolean isCanEditTable(String uri) {
        if (!isEditableTable(uri)) {
            return false;
        }
        return !isCurrentBranchProtected();
    }

    private boolean isCurrentBranchProtected() {
        RulesProject project = getProject();
        if (project != null && !project.isLocalOnly()) {
            Repository repo = project.getDesignRepository();
            return repo.supports().branches() && ((BranchRepository) repo).isBranchProtected(project.getBranch());
        }
        return false;
    }

    public boolean isReady() {
        return compiledOpenClass != null;
    }

    public boolean isTestable(String uri) {
        IOpenMethod m = getMethod(uri);
        if (m == null) {
            return false;
        }

        return ProjectHelper.testers(m, compiledOpenClass).length > 0;
    }

    public synchronized void buildProjectTree() {
        if (compiledOpenClass == null || studio.getCurrentModule() == null) {
            return;
        }

        ProjectTreeNode root = makeProjectTreeRoot();

        TableSyntaxNode[] tableSyntaxNodes = getTableSyntaxNodes();

        OverloadedMethodsDictionary methodNodesDictionary = makeMethodNodesDictionary(tableSyntaxNodes);

        TreeNodeBuilder[] treeSorters = studio.getTreeView().getBuilders();

        // Find all group sorters defined for current subtree.
        // Group sorter should have additional information for grouping
        // nodes by method signature.
        // author: Alexey Gamanovich
        //
        for (TreeNodeBuilder treeSorter : treeSorters) {

            if (treeSorter instanceof OpenMethodsGroupTreeNodeBuilder) {
                // Set to sorter information about open methods.
                // author: Alexey Gamanovich
                //
                OpenMethodsGroupTreeNodeBuilder tableTreeNodeBuilder = (OpenMethodsGroupTreeNodeBuilder) treeSorter;
                tableTreeNodeBuilder.setOpenMethodGroupsDictionary(methodNodesDictionary);
            }
            if (treeSorter instanceof WorksheetTreeNodeBuilder)
                root.setElements(new LinkedHashMap<>(root.getElements()));
        }

        for (TableSyntaxNode tableSyntaxNode : tableSyntaxNodes) {
            ProjectTreeNode element = root;
            for (TreeNodeBuilder treeSorter : treeSorters) {
                element = addToNode(element, tableSyntaxNode, treeSorter);
            }
        }
        dependencyGraph = null;

        projectRoot = build(root);

        initProjectHistory();
    }

    /**
     * Adds new object to target tree node.
     * 

* The algorithm of adding new object to tree is following: the new object is passed to each tree node builder using * order in which they are appear in builders array. Tree node builder makes appropriate tree node or nothing if it * is not necessary (e.g. builder that makes folder nodes). The new node is added to tree. * * @param targetNode target node to which will be added new object * @param object object to add */ private ProjectTreeNode addToNode(ProjectTreeNode targetNode, Object object, TreeNodeBuilder treeNodeBuilder) { // Create key for adding object. It used to check that the same node // exists. // Comparable key = treeNodeBuilder.makeKey(object); ProjectTreeNode element = null; // If key is null the rest of building node process should be skipped. // if (treeNodeBuilder.isBuilderApplicableForObject(object) && key != null) { // Try to find child node with the same object. // element = targetNode.getChild(key); // If element is null the node with same object is absent. // if (element == null) { // Build new node for the object. // element = treeNodeBuilder.makeNode(object, 0); // If element is null then builder has not created the new // element // and this builder should be skipped. // author: Alexey Gamanovich // if (element != null) { targetNode.addChild(key, element); } else { element = targetNode; } } // /////// // ??????????????????? // ////// else if (treeNodeBuilder.isUnique(object)) { for (int i = 2; i < 100; ++i) { Comparable key2 = treeNodeBuilder.makeKey(object, i); element = targetNode.getChild(key2); if (element == null) { element = treeNodeBuilder.makeNode(object, i); // If element is null then sorter has not created the // new // element and this sorter should be skipped. // author: Alexey Gamanovich // if (element != null) { targetNode.addChild(key2, element); } else { element = targetNode; } break; } } } } // If node is null skip the current builder: set the targetNode to // current element. // if (element == null) { element = targetNode; } return element; } private boolean isGapOverlap(ProjectTreeNode tableNode) { if (tableNode.getTableSyntaxNode() != null) { return isGapOverlap(tableNode.getTableSyntaxNode()); } return false; } public boolean isGapOverlap(TableSyntaxNode tsn) { String tableType = tsn.getType(); if (XlsNodeTypes.XLS_DT.toString().equals(tableType)) { return DispatcherTablesBuilder.isDispatcherTable(tsn); } return false; } public synchronized List search(Predicate selectors, SearchScope searchScope) { return getSearchScopeData(searchScope).stream() .filter(tableSyntaxNode -> !XlsNodeTypes.XLS_TABLEPART.toString().equals(tableSyntaxNode.getType())) .filter(tsn -> !isGapOverlap(tsn)) .filter(selectors) .map(TableSyntaxNodeAdapter::new) .collect(Collectors.toList()); } private TreeNode build(ProjectTreeNode root) { TreeNode node = createNode(root); Iterable children = root.getChildren(); int errors = 0; for (ProjectTreeNode child : children) { // Always hide dispatcher tables if (isGapOverlap(child)) { continue; } TreeNode rfChild = build(child); if (IProjectTypes.PT_WORKSHEET.equals(rfChild.getType()) || IProjectTypes.PT_WORKBOOK .equals(rfChild.getType())) { // skip workbook or worksheet node if it has no children nodes if (!rfChild.getChildrenKeysIterator().hasNext()) { continue; } } errors += rfChild.getNumErrors(); node.addChild(rfChild, rfChild); } node.setNumErrors(node.getNumErrors() + errors); return node; } private TreeNode createNode(ProjectTreeNode element) { boolean leaf = element.getChildren().isEmpty(); String name = element.getDisplayName(INamedThing.SHORT); String title = element.getDisplayName(INamedThing.REGULAR); String type = element.getType(); String url = null; int state = 0; int numErrors = 0; boolean active = true; if (type.startsWith(IProjectTypes.PT_TABLE + ".")) { TableSyntaxNode tsn = element.getTableSyntaxNode(); url = studio.url("table?" + Constants.REQUEST_PARAM_ID + "=" + tsn.getId()); if (studio.getModel().isTestable(element.getTableSyntaxNode().getUri())) { state = 2; // has tests } if (leaf) { numErrors = getErrorsByUri(tsn.getUri()).size(); } ITableProperties tableProperties = tsn.getTableProperties(); if (tableProperties != null) { Boolean act = tableProperties.getActive(); if (act != null) { active = act; } } } TreeNode node = new TreeNode(leaf); node.setName(name); node.setTitle(title); node.setType(type); node.setUrl(url); node.setState(state); node.setNumErrors(numErrors); node.setActive(active); return node; } private void initProjectHistory() { WorkbookSyntaxNode[] workbookNodes = getWorkbookNodes(); if (workbookNodes != null) { LocalRepository repository = getLocalRepository(); for (WorkbookSyntaxNode workbookSyntaxNode : workbookNodes) { XlsWorkbookSourceCodeModule sourceCodeModule = workbookSyntaxNode.getWorkbookSourceCodeModule(); Collection listeners = sourceCodeModule.getListeners(); for (XlsWorkbookListener listener : listeners) { if (listener instanceof XlsModificationListener) { return; } } sourceCodeModule.addListener(new XlsModificationListener(repository, getHistoryStoragePath())); } } } private LocalRepository getLocalRepository() { UserWorkspace userWorkspace = WebStudioUtils.getUserWorkspace(WebStudioUtils.getSession()); return userWorkspace.getLocalWorkspace().getRepository(studio.getCurrentRepositoryId()); } public synchronized TableSyntaxNode[] getTableSyntaxNodes() { if (isCompiledSuccessfully()) { XlsModuleSyntaxNode moduleSyntaxNode = getXlsModuleNode(); return moduleSyntaxNode.getXlsTableSyntaxNodes(); } return TableSyntaxNode.EMPTY_ARRAY; } public synchronized Set getAllTableSyntaxNodes() { Set result = ConcurrentHashMap.newKeySet(); if (webStudioWorkspaceDependencyManager != null) { webStudioWorkspaceDependencyManager.findAllProjectDependencyLoaders(getProjectDescriptor()) .stream() .filter(IDependencyLoader::isProjectLoader) .map(e -> getModuleSyntaxNodesByProject(e.getProject().getName())) .flatMap(Collection::stream) .map(XlsModuleSyntaxNode::getXlsTableSyntaxNodes) .filter(Objects::nonNull) .map(Arrays::asList) .forEach(result::addAll); } return result; } private synchronized Set getCurrentProjectTableSyntaxNodes() { return Optional.ofNullable(studio.getCurrentProject()) .map(AProjectFolder::getName) .map(this::getModuleSyntaxNodesByProject) .map(nodes -> nodes.stream() .filter(Objects::nonNull) .map(XlsModuleSyntaxNode::getXlsTableSyntaxNodes) .map(Arrays::asList) .flatMap(Collection::stream) .collect(Collectors.toSet())) .orElse(Collections.emptySet()); } public synchronized int getNumberOfTables() { int count = 0; TableSyntaxNode[] tables = getTableSyntaxNodes(); for (TableSyntaxNode table : tables) { if (!XlsNodeTypes.XLS_OTHER.toString().equals(table.getType())) { count++; } } return count; } private OverloadedMethodsDictionary makeMethodNodesDictionary(TableSyntaxNode[] tableSyntaxNodes) { // Create open methods dictionary that organizes // open methods in groups using their meta info. // Dictionary contains required information what will be used to create // groups of methods in tree. // author: Alexey Gamanovich // List executableNodes = getAllExecutableTables(tableSyntaxNodes); OverloadedMethodsDictionary methodNodesDictionary = new OverloadedMethodsDictionary(); methodNodesDictionary.addAll(executableNodes); return methodNodesDictionary; } private ProjectTreeNode makeProjectTreeRoot() { return new ProjectTreeNode(new String[]{null, null, null}, "root", null); } private List getAllExecutableTables(TableSyntaxNode[] nodes) { List executableNodes = new ArrayList<>(); for (TableSyntaxNode node : nodes) { if (node.getMember() instanceof IOpenMethod) { executableNodes.add(node); } } return executableNodes; } public synchronized void redraw() { projectRoot = null; } public void reset(ReloadType reloadType) throws Exception { reset(reloadType, moduleInfo); } public synchronized void reset(ReloadType reloadType, Module moduleToOpen) throws Exception { switch (reloadType) { case FORCED: moduleToOpen = studio.getCurrentModule(); // falls through case RELOAD: if (webStudioWorkspaceDependencyManager != null) { webStudioWorkspaceDependencyManager.shutdown(); xlsModuleSyntaxNodesPerProject.clear(); xlsModuleSyntaxNodes.clear(); } webStudioWorkspaceDependencyManager = null; recentlyVisitedTables.clear(); break; case SINGLE: webStudioWorkspaceDependencyManager .reset(AbstractDependencyManager.buildResolvedDependency(moduleToOpen)); break; } setModuleInfo(moduleToOpen, reloadType); projectRoot = null; } public synchronized TestUnitsResults runTest(TestSuite test, boolean currentOpenedModule) { Integer threads = Props.integer(AdministrationSettings.TEST_RUN_THREAD_COUNT_PROPERTY); boolean isParallel = threads != null && threads > 1; return runTest(test, isParallel, currentOpenedModule ? openedModuleCompiledOpenClass.getOpenClassWithErrors() : compiledOpenClass.getOpenClassWithErrors()); } private TestUnitsResults runTest(TestSuite test, boolean isParallel, IOpenClass openClass) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(compiledOpenClass.getClassLoader()); if (!isParallel) { return test.invokeSequentially(openClass, 1); } else { return test.invokeParallel(testSuiteExecutor, openClass, 1); } } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } // Logic in this block of code implemented with a recursion to achieve sorting of each dataset by comparator // and place this sets in the proper order. public synchronized Set getSearchScopeData(SearchScope searchScope) { if (searchScope == SearchScope.ALL) { Set nodes = getSearchScopeData(SearchScope.CURRENT_PROJECT); getAllTableSyntaxNodes().stream().sorted(DEFAULT_NODE_CMP).forEach(nodes::add); return nodes; } else if (searchScope == SearchScope.CURRENT_PROJECT) { Set nodes = WebStudioUtils.getWebStudio().getCurrentModule() != null ? getSearchScopeData( SearchScope.CURRENT_MODULE) : new LinkedHashSet<>(); getCurrentProjectTableSyntaxNodes().stream().sorted(DEFAULT_NODE_CMP).forEach(nodes::add); return nodes; } else if (searchScope == SearchScope.CURRENT_MODULE) { return Arrays.stream(getXlsModuleNode().getXlsTableSyntaxNodes()) .sorted(DEFAULT_NODE_CMP) .collect(Collectors.toCollection(LinkedHashSet::new)); } else { throw new IllegalStateException(); } } public synchronized void clearModuleInfo() { this.moduleInfo = null; historyStoragePath = null; clearModuleResources(); // prevent memory leak OpenClassUtil.release(compiledOpenClass); compiledOpenClass = null; if (webStudioWorkspaceDependencyManager != null) { webStudioWorkspaceDependencyManager.shutdown(); xlsModuleSyntaxNodesPerProject.clear(); xlsModuleSyntaxNodes.clear(); } webStudioWorkspaceDependencyManager = null; xlsModuleSyntaxNode = null; projectRoot = null; } public void setModuleInfo(Module moduleInfo) throws Exception { setModuleInfo(moduleInfo, ReloadType.NO); } private void addCompiledDependency(IDependencyLoader dependencyLoader, CompiledDependency compiledDependency) { IMetaInfo metaInfo = compiledDependency.getCompiledOpenClass().getOpenClassWithErrors().getMetaInfo(); if (metaInfo instanceof XlsMetaInfo) { XlsMetaInfo xlsMetaInfo = (XlsMetaInfo) metaInfo; XlsModuleSyntaxNode xlsModuleSyntaxNode = xlsMetaInfo.getXlsModuleNode(); if (xlsModuleSyntaxNode != null) { getModuleSyntaxNodesByProject(dependencyLoader.getProject().getName()).add(xlsModuleSyntaxNode); if (!(xlsModuleSyntaxNode.getModule() instanceof VirtualSourceCodeModule)) { xlsModuleSyntaxNodes.add(xlsModuleSyntaxNode); } } } } private void removeCompiledDependency(IDependencyLoader dependencyLoader, CompiledDependency compiledDependency) { IMetaInfo metaInfo = compiledDependency.getCompiledOpenClass().getOpenClassWithErrors().getMetaInfo(); if (metaInfo instanceof XlsMetaInfo) { XlsMetaInfo xlsMetaInfo = (XlsMetaInfo) metaInfo; XlsModuleSyntaxNode xlsModuleSyntaxNode = xlsMetaInfo.getXlsModuleNode(); if (xlsModuleSyntaxNode != null) { getModuleSyntaxNodesByProject(dependencyLoader.getProject().getName()).remove(xlsModuleSyntaxNode); if (!(xlsModuleSyntaxNode.getModule() instanceof VirtualSourceCodeModule)) { xlsModuleSyntaxNodes.remove(xlsModuleSyntaxNode); } } } } private Set getModuleSyntaxNodesByProject(String projectName) { return xlsModuleSyntaxNodesPerProject.computeIfAbsent(projectName, e -> ConcurrentHashMap.newKeySet()); } public synchronized void setModuleInfo(Module moduleInfo, ReloadType reloadType) throws Exception { if (moduleInfo == null || this.moduleInfo == moduleInfo && reloadType == ReloadType.NO) { return; } File projectFolder = moduleInfo.getProject().getProjectFolder().toFile(); if (reloadType == ReloadType.FORCED) { ProjectResolver projectResolver = studio.getProjectResolver(); ProjectDescriptor projectDescriptor = projectResolver.resolve(projectFolder); Module reloadedModule = null; for (Module module : projectDescriptor.getModules()) { if (moduleInfo.getName().equals(module.getName())) { reloadedModule = module; break; } } this.moduleInfo = reloadedModule; } else { this.moduleInfo = moduleInfo; } initHistoryStoragePath(); isModified(); clearModuleResources(); // prevent memory leak projectRoot = null; xlsModuleSyntaxNode = null; prepareWorkspaceDependencyManager(moduleInfo.getProject()); try { CompiledOpenClass thisModuleCompiledOpenClass = webStudioWorkspaceDependencyManager .loadDependency(AbstractDependencyManager.buildResolvedDependency(moduleInfo)) .getCompiledOpenClass(); xlsModuleSyntaxNode = findXlsModuleSyntaxNode(thisModuleCompiledOpenClass); openedModuleCompiledOpenClass = thisModuleCompiledOpenClass; if (compiledOpenClass == null || !isProjectCompilationCompleted() || !ReloadType.NO.equals(reloadType)) { compiledOpenClass = thisModuleCompiledOpenClass; } if (!moduleInfo.getWebstudioConfiguration().isCompileThisModuleOnly()) { ResolvedDependency projectDependency = AbstractDependencyManager .buildResolvedDependency(moduleInfo.getProject()); if (!ReloadType.NO.equals(reloadType) || !Objects.equals(projectDependency, projectCompilationCompleted)) { compileProject(false, false); } } else { this.compilationInProgress = false; } } catch (Exception | LinkageError e) { onCompilationFailed(e); } } public void compileProject(boolean sync, boolean prepareWorkspaceDependencyManager) { final CountDownLatch countDownLatch = new CountDownLatch(1); synchronized (this) { ProjectDescriptor projectDescriptor = getProjectDescriptor(); if (this.webStudioWorkspaceDependencyManager == null || prepareWorkspaceDependencyManager) { prepareWorkspaceDependencyManager(projectDescriptor); } this.compilationInProgress = true; this.projectCompilationCompleted = null; ResolvedDependency projectDependency = AbstractDependencyManager.buildResolvedDependency(projectDescriptor); this.webStudioWorkspaceDependencyManager.loadDependencyAsync(projectDependency, (compiledDependency) -> { synchronized (ProjectModel.this) { try { this.compiledOpenClass = this.validate(projectDescriptor); XlsMetaInfo metaInfo1 = (XlsMetaInfo) this.compiledOpenClass.getOpenClassWithErrors() .getMetaInfo(); getModuleSyntaxNodesByProject(projectDescriptor.getName()).add(metaInfo1.getXlsModuleNode()); redraw(); } catch (Exception | LinkageError e) { onCompilationFailed(e); } this.projectCompilationCompleted = compiledDependency.getDependency(); this.compilationInProgress = false; countDownLatch.countDown(); } }); } if (sync) { try { countDownLatch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } private ProjectDescriptor getProjectDescriptor() { try { ProjectResolver projectResolver = studio.getProjectResolver(); File localFile = new File(getProject().getLocalRepository().getRoot(), getProject().getName()); return projectResolver.resolve(localFile); } catch (Exception e) { // Fail-safe behavior for mock tests where studio.getProjectResolver() is not set. return moduleInfo.getProject(); } } private void onCompilationFailed(Throwable t) { compilationInProgress = false; log.error("Failed to load.", t); Collection messages = new LinkedHashSet<>(); for (OpenLMessage openLMessage : OpenLMessagesUtils.newErrorMessages(t)) { String message = String.format("Cannot load the module: %s", openLMessage.getSummary()); messages.add(new OpenLMessage(message, Severity.ERROR)); } compiledOpenClass = new CompiledOpenClass(NullOpenClass.the, messages); openedModuleCompiledOpenClass = new CompiledOpenClass(NullOpenClass.the, messages); } public boolean isModified() { if (moduleInfo == null) { return false; } long modificationTime = moduleLastModified; moduleLastModified = moduleInfo.getRulesPath().toFile().lastModified(); return modificationTime != moduleLastModified; } private CompiledOpenClass validate(ProjectDescriptor projectDescriptor) throws RulesInstantiationException { OpenApiProjectValidator openApiProjectValidator = new OpenApiProjectValidator(); return openApiProjectValidator.validate(projectDescriptor, getRulesInstantiationStrategy(projectDescriptor)); } public RulesInstantiationStrategy getRulesInstantiationStrategy(ProjectDescriptor projectDescriptor) { List modules = projectDescriptor.getModules(); RulesInstantiationStrategy instantiationStrategy = new SimpleMultiModuleInstantiationStrategy(modules, webStudioWorkspaceDependencyManager, false); Map externalParameters = ProjectExternalDependenciesHelper .buildExternalParamsWithProjectDependencies(studio.getExternalProperties(), modules); instantiationStrategy.setExternalParameters(externalParameters); return instantiationStrategy; } private void prepareWorkspaceDependencyManager(ProjectDescriptor projectDescriptor) { if (webStudioWorkspaceDependencyManager == null) { webStudioWorkspaceDependencyManager = webStudioWorkspaceDependencyManagerFactory .buildDependencyManager(projectDescriptor); webStudioWorkspaceDependencyManager.registerOnCompilationCompleteListener(this::addCompiledDependency); webStudioWorkspaceDependencyManager.registerOnResetCompleteListener(this::removeCompiledDependency); projectCompilationCompleted = null; } else { Set projectsInWorkspace = webStudioWorkspaceDependencyManagerFactory .resolveWorkspace(projectDescriptor); Set projectNamesInWorkspace = projectsInWorkspace.stream() .map(ProjectDescriptor::getName) .collect(Collectors.toSet()); boolean foundOpenedProject = false; boolean allProjectCanBeReused = true; Collection projectDependencyLoaders = webStudioWorkspaceDependencyManager .getDependencyLoaders() .stream() .filter(IDependencyLoader::isProjectLoader) .collect(Collectors.toList()); for (IDependencyLoader projectDependencyLoader : projectDependencyLoaders) { if (projectDescriptor.getName().equals(projectDependencyLoader.getProject().getName())) { foundOpenedProject = true; } if (!projectNamesInWorkspace.contains(projectDependencyLoader.getProject().getName())) { allProjectCanBeReused = false; } } if (!foundOpenedProject) { if (!allProjectCanBeReused) { webStudioWorkspaceDependencyManager.shutdown(); xlsModuleSyntaxNodesPerProject.clear(); xlsModuleSyntaxNodes.clear(); webStudioWorkspaceDependencyManager = webStudioWorkspaceDependencyManagerFactory .buildDependencyManager(projectDescriptor); webStudioWorkspaceDependencyManager .registerOnCompilationCompleteListener(this::addCompiledDependency); webStudioWorkspaceDependencyManager.registerOnResetCompleteListener(this::removeCompiledDependency); projectCompilationCompleted = null; } else { // If loaded projects are a part of the new opened project, then we can reuse dependency manager webStudioWorkspaceDependencyManager .expand(webStudioWorkspaceDependencyManagerFactory.resolveWorkspace(projectDescriptor)); } } } } public synchronized void traceElement(TestSuite testSuite) { ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader(); boolean currentOpenedModule = Boolean .parseBoolean(WebStudioUtils.getRequestParameter(Constants.REQUEST_PARAM_CURRENT_OPENED_MODULE)); try { if (currentOpenedModule) { Thread.currentThread().setContextClassLoader(openedModuleCompiledOpenClass.getClassLoader()); CachingArgumentsCloner.initInstance(); runTest(testSuite, false, openedModuleCompiledOpenClass.getOpenClassWithErrors()); } else { Thread.currentThread().setContextClassLoader(compiledOpenClass.getClassLoader()); CachingArgumentsCloner.initInstance(); runTest(testSuite, false, compiledOpenClass.getOpenClassWithErrors()); } } finally { Thread.currentThread().setContextClassLoader(currentContextClassLoader); CachingArgumentsCloner.removeInstance(); } } public synchronized TableEditorModel getTableEditorModel(String tableUri) { IOpenLTable table = getTable(tableUri); return getTableEditorModel(table); } public WebStudioWorkspaceRelatedDependencyManager getWebStudioWorkspaceDependencyManager() { return webStudioWorkspaceDependencyManager; } public synchronized TableEditorModel getTableEditorModel(IOpenLTable table) { String tableView = studio.getTableView(); return new TableEditorModel(table, tableView, false); } public synchronized boolean isCompiledSuccessfully() { return compiledOpenClass != null && compiledOpenClass.getOpenClassWithErrors() != null && !(compiledOpenClass .getOpenClassWithErrors() instanceof NullOpenClass) && xlsModuleSyntaxNode != null; } public synchronized boolean isOpenedModuleCompiledSuccessfully() { return openedModuleCompiledOpenClass != null && openedModuleCompiledOpenClass .getOpenClassWithErrors() != null && !(openedModuleCompiledOpenClass .getOpenClassWithErrors() instanceof NullOpenClass) && xlsModuleSyntaxNode != null; } public synchronized DependencyRulesGraph getDependencyGraph() { if (dependencyGraph == null) { Collection rulesMethods = compiledOpenClass.getOpenClassWithErrors().getMethods(); dependencyGraph = DependencyRulesGraph.filterAndCreateGraph(rulesMethods); } return dependencyGraph; } public synchronized List getSources() { List sourceFiles = new ArrayList<>(); WorkbookSyntaxNode[] workbookNodes = getWorkbookNodes(); if (workbookNodes != null) { for (WorkbookSyntaxNode workbookSyntaxNode : workbookNodes) { File sourceFile = workbookSyntaxNode.getWorkbookSourceCodeModule().getSourceFile(); sourceFiles.add(sourceFile); } } // TODO: Consider the case when there is compilation error. In this case sourceFiles will be empty, it can break // history manager. return sourceFiles; } public synchronized String[] getModuleSourceNames() { List moduleSources = getSources(); String[] moduleSourceNames = new String[moduleSources.size()]; int i = 0; for (File source : moduleSources) { moduleSourceNames[i] = source.getName(); i++; } return moduleSourceNames; } public synchronized File getSourceByName(String fileName) { List sourceFiles = getSources(); for (File file : sourceFiles) { if (file.getName().equals(fileName)) { return file; } } return null; } public String getHistoryStoragePath() { return historyStoragePath; } private void initHistoryStoragePath() { if (WebStudioUtils.getSession() != null) { File location = WebStudioUtils.getUserWorkspace(WebStudioUtils.getSession()) .getLocalWorkspace() .getLocation(); this.historyStoragePath = Paths .get(location.getPath(), FolderHelper.resolveHistoryFolder(getProject(), moduleInfo)) .toString(); } } public synchronized RecentlyVisitedTables getRecentlyVisitedTables() { return recentlyVisitedTables; } public synchronized XlsWorkbookSourceCodeModule getCurrentModuleWorkbook() { Module currentModule = studio.getCurrentModule(); if (currentModule == null) { return null; } PathEntry rulesRootPath = currentModule.getRulesRootPath(); WorkbookSyntaxNode[] workbookNodes = getWorkbookNodes(); if (workbookNodes == null) { return null; } for (WorkbookSyntaxNode workbookSyntaxNode : workbookNodes) { XlsWorkbookSourceCodeModule module = workbookSyntaxNode.getWorkbookSourceCodeModule(); if (rulesRootPath != null && module.getSourceFile() .getName() .equals(FileUtils.getName(rulesRootPath.getPath()))) { return module; } } return null; } /** * Returns true if both are true: 1) Old project version is opened and 2) project is not modified yet. *

* Otherwise return false */ public synchronized boolean isConfirmOverwriteNewerRevision() { RulesProject project = getProject(); return project != null && project.isOpenedOtherVersion() && !project.isModified(); } public void destroy() { clearModuleInfo(); } private void clearModuleResources() { removeListeners(); } /** * Remove listeners added in {@link #initProjectHistory()} */ private void removeListeners() { WorkbookSyntaxNode[] workbookNodes = getWorkbookNodes(); if (workbookNodes != null) { for (WorkbookSyntaxNode workbookSyntaxNode : workbookNodes) { XlsWorkbookSourceCodeModule sourceCodeModule = workbookSyntaxNode.getWorkbookSourceCodeModule(); Iterator iterator = sourceCodeModule.getListeners().iterator(); while (iterator.hasNext()) { XlsWorkbookListener listener = iterator.next(); if (listener instanceof XlsModificationListener) { iterator.remove(); break; } } } } } public boolean isCompilationInProgress() { return compilationInProgress; } public boolean isProjectCompilationCompleted() { if (moduleInfo != null) { ResolvedDependency projectDependency = AbstractDependencyManager .buildResolvedDependency(moduleInfo.getProject()); return Objects.equals(projectCompilationCompleted, projectDependency); } return false; } public synchronized IOpenMethod getCurrentDispatcherMethod(IOpenMethod method, String uri) { return getMethodFromDispatcher((OpenMethodDispatcher) method, uri); } public synchronized String getMessageNodeId(String sourceLocation) { XlsUrlParser xlsUrlParser = sourceLocation != null ? new XlsUrlParser(sourceLocation) : null; for (TableSyntaxNode tsn : getAllTableSyntaxNodes()) { // for all modules if (xlsUrlParser != null && xlsUrlParser.intersects(tsn.getUriParser())) { return tsn.getId(); } } return null; } private class XlsModificationListener implements XlsWorkbookListener { private final LocalRepository repository; private final String historyStoragePath; private XlsModificationListener(LocalRepository repository, String historyStoragePath) { this.repository = repository; this.historyStoragePath = historyStoragePath; } @Override public void beforeSave(XlsWorkbookSourceCodeModule workbookSourceCodeModule) { File sourceFile = workbookSourceCodeModule.getSourceFile(); ProjectHistoryService.init(historyStoragePath, sourceFile); } @Override public void afterSave(XlsWorkbookSourceCodeModule workbookSourceCodeModule) { isModified(); File sourceFile = workbookSourceCodeModule.getSourceFile(); repository.getProjectState(sourceFile.getPath()).notifyModified(); ProjectHistoryService.save(historyStoragePath, sourceFile); } } }