com.jaspersoft.jasperserver.export.modules.repository.ResourceImporter.java Source code

Java tutorial

Introduction

Here is the source code for com.jaspersoft.jasperserver.export.modules.repository.ResourceImporter.java

Source

/*
 * Copyright (C) 2005 - 2014 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com.
 *
 * Unless you have purchased  a commercial license agreement from Jaspersoft,
 * the following license terms  apply:
 *
 * This program 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.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 */

package com.jaspersoft.jasperserver.export.modules.repository;

import com.jaspersoft.jasperserver.api.JSException;
import com.jaspersoft.jasperserver.api.JSExceptionWrapper;
import com.jaspersoft.jasperserver.api.common.domain.ExecutionContext;
import com.jaspersoft.jasperserver.api.metadata.common.domain.Folder;
import com.jaspersoft.jasperserver.api.metadata.common.domain.InternalURI;
import com.jaspersoft.jasperserver.api.metadata.common.domain.Resource;
import com.jaspersoft.jasperserver.api.metadata.common.domain.ResourceReference;
import com.jaspersoft.jasperserver.api.metadata.common.domain.client.FolderImpl;
import com.jaspersoft.jasperserver.api.metadata.common.domain.util.DataContainerStreamUtil;
import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService;
import com.jaspersoft.jasperserver.api.metadata.user.domain.ObjectPermission;
import com.jaspersoft.jasperserver.api.metadata.user.domain.Role;
import com.jaspersoft.jasperserver.api.metadata.user.domain.Tenant;
import com.jaspersoft.jasperserver.api.metadata.user.domain.User;
import com.jaspersoft.jasperserver.api.metadata.user.service.ObjectPermissionService;
import com.jaspersoft.jasperserver.api.metadata.user.service.TenantService;
import com.jaspersoft.jasperserver.export.modules.BaseImporterModule;
import com.jaspersoft.jasperserver.export.modules.ImporterModuleContext;
import com.jaspersoft.jasperserver.export.modules.common.TenantQualifiedName;
import com.jaspersoft.jasperserver.export.modules.repository.beans.FolderBean;
import com.jaspersoft.jasperserver.export.modules.repository.beans.PermissionRecipient;
import com.jaspersoft.jasperserver.export.modules.repository.beans.RepositoryObjectPermissionBean;
import com.jaspersoft.jasperserver.export.modules.repository.beans.ResourceBean;
import com.jaspersoft.jasperserver.export.modules.repository.beans.ResourceReferenceBean;
import com.jaspersoft.jasperserver.export.util.PathUtils;
import com.jaspersoft.jasperserver.export.util.PathUtils.SplittedPath;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.security.SpringSecurityException;

import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Lucian Chirita (lucianc@users.sourceforge.net)
 * @version $Id: ResourceImporter.java 47331 2014-07-18 09:13:06Z kklein $
 */
public class ResourceImporter extends BaseImporterModule implements ResourceImportHandler, InitializingBean {

    public final static String ATTRIBUTE_UPDATE_RESOURCES = "updateResources";

    private final static Log log = LogFactory.getLog(ResourceImporter.class);

    private ResourceModuleConfiguration configuration;
    private TenantService tenantService;
    private String prependPathArg;
    private String updateArg;
    private String includeSettingsArg;
    private String skipThemesArgument;
    private List<String> rootSubTenantFolderUris;
    private Pattern orgPattern = Pattern.compile("((/" + TenantService.ORGANIZATIONS + "/[^/]+)*)");
    private Pattern themesPattern = Pattern.compile(".*/themes/.*");

    protected RepositoryService repository;
    protected String prependPath;
    private boolean update;

    protected Set importedURIs;
    private LinkedList folderQueue;
    private LinkedList resourceQueue;
    private Map roles;
    private Map users;

    public void afterPropertiesSet() {
        this.repository = configuration.getRepository();
    }

    public void init(ImporterModuleContext moduleContext) {
        super.init(moduleContext);
        configuration.setApplicationContext(
                (ApplicationContext) moduleContext.getAttributes().getAttribute("appContext"));
        prependPath = getPrependPath();
        update = getUpdateFlag();
        //Retrieve all root subtenants
        List<Tenant> allRootSubTenantList = tenantService.getAllSubTenantList(executionContext,
                TenantService.ORGANIZATIONS);
        rootSubTenantFolderUris = new ArrayList<String>();
        for (Tenant tenant : allRootSubTenantList) {
            rootSubTenantFolderUris.add(tenant.getTenantFolderUri());
        }
    }

    protected String getPrependPath() {
        String path = getParameterValue(getPrependPathArg());
        if (path != null) {
            path = PathUtils.normalizePath(path);
            if (path.length() == 0 || path.equals(Folder.SEPARATOR)) {
                path = null;
            } else if (!path.startsWith(Folder.SEPARATOR)) {
                path = Folder.SEPARATOR + path;
            }
        }
        return path;
    }

    protected boolean getUpdateFlag() {
        return hasParameter(getUpdateArg());
    }

    protected boolean getIncludeSettingsFlag() {
        return hasParameter(getIncludeSettingsArg());
    }

    protected boolean skipThemes() {
        return hasParameter(getSkipThemesArgument());
    }

    protected boolean isUpdate() {
        return update;
    }

    public List<String> process() {
        initProcess();

        createPrependFolder();

        queueEntryFolders();
        queueEntryResources();

        while (!folderQueue.isEmpty() || !resourceQueue.isEmpty()) {
            if (folderQueue.isEmpty()) {
                String uri = (String) resourceQueue.removeFirst();
                try {
                    importResource(uri, false);
                } catch (ResourceBeanDataNotFoundException e) {
                    final String msg;
                    if (uri.equals(e.getMessage())) {
                        msg = "Reference resource \"" + e.getMessage() + "\" not found.";
                    } else {
                        msg = "Reference resource \"" + e.getMessage() + "\" not found when importing resource \""
                                + uri + "\".";
                    }
                    commandOut.warn(msg);
                    fileBrokenResource(msg);
                }
            } else {
                String uri = (String) folderQueue.removeFirst();
                importFolder(uri, true);
            }
        }
        return null;
    }

    private void fileBrokenResource(String message) {
        File file = new File("import-errors.log");
        Writer out = null;
        try {
            out = new BufferedWriter(new FileWriter(file, true));
            out.write(message);
            out.write('\n');
        } catch (IOException e) {
            commandOut.error("Error writing to import.log", e);
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e1) {
                }
            }
        }
    }

    protected void queueEntryFolders() {
        List entryFolders = new ArrayList();
        for (Iterator it = indexElement.elementIterator(configuration.getFolderIndexElement()); it.hasNext();) {
            Element folderElement = (Element) it.next();
            entryFolders.add(folderElement.getText());
        }

        if (!entryFolders.isEmpty()) {
            Collections.sort(entryFolders);
            for (Iterator it = entryFolders.iterator(); it.hasNext();) {
                String uri = (String) it.next();
                folderQueue.addLast(uri);
            }
        }
    }

    protected void queueEntryResources() {
        for (Iterator it = indexElement.elementIterator(configuration.getResourceIndexElement()); it.hasNext();) {
            Element resourceElement = (Element) it.next();
            String uri = resourceElement.getText();
            resourceQueue.addLast(uri);
        }
    }

    protected void createPrependFolder() {
        if (prependPath != null) {
            LinkedList toCreateURIs = new LinkedList();
            for (String path = prependPath; repository.getFolder(executionContext,
                    path) == null; path = PathUtils.splitPath(path).parentPath) {
                toCreateURIs.addFirst(path);
            }

            while (!toCreateURIs.isEmpty()) {
                String path = (String) toCreateURIs.removeFirst();
                Folder folder = createFolder(path);

                commandOut.debug("About to save folder " + path);
                try {
                    repository.saveFolder(executionContext, folder);
                } catch (SpringSecurityException er) {
                    this.updateSecuredResource(executionContext, folder);
                }
            }
        }
    }

    protected void initProcess() {
        importedURIs = new HashSet();
        folderQueue = new LinkedList();
        resourceQueue = new LinkedList();

        roles = new HashMap();
        users = new HashMap();
    }

    protected void importFolder(String uri, boolean detailsRequired) {
        if (importedURIs.contains(uri) || skipResource(uri)) {
            return;
        }

        // All organization folder uris have pattern /organizations/org_1../organizations/org2/folder except of template folder
        // which has pattern /organizations/org_template, so we have to skip it.
        String tenantFolderUri = "";
        int orgTeplateIndex = uri.indexOf("/" + TenantService.ORGANIZATIONS + "/" + TenantService.ORG_TEMPLATE);
        if (orgTeplateIndex >= 0) {
            tenantFolderUri = uri.substring(0, orgTeplateIndex);
        } else {
            Matcher m = orgPattern.matcher(uri);
            if (m.find()) {
                tenantFolderUri = m.group(1);
            }
        }

        if (!(rootSubTenantFolderUris.contains(tenantFolderUri) || tenantFolderUri.equals(""))) {
            commandOut
                    .info("Folder with the uri" + uri + " is attached to not existing organization. Not imported");
            return;
        }

        FolderBean folderBean = getFolderDetails(uri, detailsRequired);

        String importUri = prependedPath(uri);
        Folder folder = repository.getFolder(executionContext, importUri);

        if (executionContext.getAttributes() == null) {
            executionContext.setAttributes(new ArrayList());
        }
        if (!executionContext.getAttributes().contains(RepositoryService.IS_IMPORTING)) {
            executionContext.getAttributes().add(RepositoryService.IS_IMPORTING);
        }

        if (folder == null) {
            ensureParent(uri);

            if (folderBean == null) {
                folder = createFolder(importUri);
            } else {
                folder = createFolder(folderBean);
            }

            try {
                commandOut.debug("About to save folder " + importUri);
                repository.saveFolder(executionContext, folder);
            } catch (SpringSecurityException er) {
                this.updateSecuredResource(executionContext, folder);
            }

            if (folderBean != null) {
                setPermissions(folder, folderBean.getPermissions(), false);
            }

            commandOut.info("Created repository folder " + importUri);
        } else if (update && folderBean != null) {
            folder.setLabel(folderBean.getLabel());
            folder.setDescription(folderBean.getDescription());

            try {
                commandOut.debug("About to save folder " + importUri);
                repository.saveFolder(executionContext, folder);
            } catch (SpringSecurityException er) {
                this.updateSecuredResource(executionContext, folder);
            }
            commandOut.info("Updating folder " + importUri);

            if (folderBean.isExportedWithPermissions()) {
                configuration.getPermissionService().deleteObjectPermissionForObject(executionContext, folder);
            }
            setPermissions(folder, folderBean.getPermissions(), false);
        } else {
            if (folderBean != null) {
                commandOut.info("Folder " + importUri + " already exists, importing permissions only");

                setPermissions(folder, folderBean.getPermissions(), true);
            }
        }

        importedURIs.add(uri);

        if (folderBean != null) {
            queueSubFolders(uri, folderBean);

            queueResources(uri, folderBean);
        }
    }

    private boolean skipResource(String uri) {
        return themesPattern.matcher(uri).matches() && skipThemes();
    }

    protected void queueSubFolders(String uri, FolderBean folderBean) {
        String[] subFolders = folderBean.getSubFolders();
        if (subFolders != null) {
            for (int i = 0; i < subFolders.length; i++) {
                String subfolderURI = appendPath(uri, subFolders[i]);
                folderQueue.addLast(subfolderURI);
            }
        }
    }

    protected void queueResources(String uri, FolderBean folderBean) {
        String[] resources = folderBean.getResources();
        if (resources != null) {
            for (int i = 0; i < resources.length; i++) {
                String resourceUri = appendPath(uri, resources[i]);
                resourceQueue.addLast(resourceUri);
            }
        }
    }

    protected String prependedPath(String uri) {
        return PathUtils.concatPaths(prependPath, uri);
    }

    protected String appendPath(String uri, String name) {
        String subUri;
        if (uri.equals(Folder.SEPARATOR)) {
            subUri = Folder.SEPARATOR + name;
        } else {
            subUri = uri + Folder.SEPARATOR + name;
        }
        return subUri;
    }

    protected FolderBean getFolderDetails(String uri, boolean required) {
        FolderBean folderBean = null;
        String folderPath = PathUtils.concatPaths(configuration.getResourcesDirName(), uri);
        if (input.fileExists(folderPath, configuration.getFolderDetailsFileName())) {
            folderBean = (FolderBean) deserialize(folderPath, configuration.getFolderDetailsFileName(),
                    configuration.getSerializer());
        } else {
            if (required) {
                // Adding non localized message cause import-export tool does not support localization.
                StringBuilder message = new StringBuilder("Folder details for folder ");
                message.append(uri);
                message.append(" were not found in the import information.");
                commandOut.info(message.toString());
                throw new JSException(message.toString());
            }
        }
        return folderBean;
    }

    protected void ensureParent(String uri) {
        SplittedPath splitPath = PathUtils.splitPath(uri);
        if (splitPath != null && splitPath.parentPath != null) {
            importFolder(splitPath.parentPath, false);
        }
    }

    protected Folder createFolder(String uri) {
        Folder folder = new FolderImpl();
        SplittedPath splPath = PathUtils.splitPath(uri);
        folder.setParentFolder(splPath.parentPath);
        folder.setName(splPath.name);
        folder.setLabel(splPath.name);
        return folder;
    }

    protected Folder createFolder(FolderBean folderBean) {
        Folder folder = new FolderImpl();
        folderBean.copyTo(folder);
        folder.setCreationDate(folderBean.getCreationDate());
        folder.setUpdateDate(folderBean.getUpdateDate());
        folder.setParentFolder(prependedPath(folder.getParentFolder()));
        return folder;
    }

    protected String importResource(String uri, boolean ignoreMissing) {
        String importUri = prependedPath(uri);
        if (!importedURIs.contains(uri) && !skipResource(uri)) {
            if (ignoreMissing && !hasResourceBeanData(uri)) {
                commandOut.info("Resource " + uri + " data missing from the catalog, skipping from import");
            } else {
                if (!hasResourceBeanData(uri)) {
                    throw new ResourceBeanDataNotFoundException(uri);
                }

                boolean importingSettings = uri.equals(configuration.getUriOfSettingsList());
                if (importingSettings) {
                    update = getIncludeSettingsFlag();
                }

                Resource resource = repository.getResource(executionContext, importUri);

                if (executionContext.getAttributes() == null) {
                    executionContext.setAttributes(new ArrayList());
                }
                if (!executionContext.getAttributes().contains(RepositoryService.IS_IMPORTING)) {
                    executionContext.getAttributes().add(RepositoryService.IS_IMPORTING);
                }

                if (resource == null) {
                    ensureParent(uri);

                    ResourceBean bean = readResourceBean(uri);
                    resource = createResource(bean);

                    commandOut.debug("About to save resource " + importUri);
                    try {
                        repository.saveResource(executionContext, resource);
                    } catch (SpringSecurityException er) {
                        this.updateSecuredResource(executionContext, resource);
                    }

                    setPermissions(resource, bean.getPermissions(), false);

                    commandOut.info("Imported resource " + importUri);
                } else if (update) {
                    registerUpdateResource(importUri);

                    ResourceBean bean = readResourceBean(uri);
                    Resource updated = createResource(bean);

                    if (resource.isSameType(updated)) {
                        // We need to re-read the object to get the latest version
                        // this is a fix to bug # 27803
                        // http://bugzilla.jaspersoft.com/show_bug.cgi?id=27803
                        Resource resource2 = repository.getResource(executionContext, importUri);
                        updated.setVersion(resource2.getVersion());
                        commandOut.debug("About to save resource " + importUri);
                        try {
                            repository.saveResource(executionContext, updated);
                        } catch (SpringSecurityException er) {
                            this.updateSecuredResource(executionContext, resource);
                        }
                        if (bean.isExportedWithPermissions()) {
                            configuration.getPermissionService().deleteObjectPermissionForObject(executionContext,
                                    resource);
                        }
                        setPermissions(resource, bean.getPermissions(), false);

                        commandOut.info("Updated resource " + importUri);
                    } else {
                        commandOut.warn("Resource " + importUri
                                + " already exists in the repository and has a different type than in the catalog, not updating");
                    }
                } else {
                    commandOut.warn("Resource " + importUri + " already exists, not importing");
                }

                if (importingSettings) {
                    update = getUpdateFlag();
                }

                importedURIs.add(uri);
            }
        }
        return importUri;
    }

    protected void updateSecuredResource(ExecutionContext context, Resource resource) {
        commandOut.info("Access denied for " + resource.getURIString());
    }

    protected void registerUpdateResource(String resourceUri) {
        Set updateResources = (Set) getContextAttributes().getAttribute(ATTRIBUTE_UPDATE_RESOURCES);
        if (updateResources == null) {
            updateResources = new HashSet();
            getContextAttributes().setAttribute(ATTRIBUTE_UPDATE_RESOURCES, updateResources);
        }

        updateResources.add(resourceUri);
    }

    protected Resource createResource(ResourceBean bean) {
        Class resourceItf = configuration.getCastorBeanMappings().getInterface(bean.getClass());
        Resource resource = repository.newResource(executionContext, resourceItf);
        bean.copyTo(resource, this);
        resource.setParentFolder(prependedPath(resource.getParentFolder()));
        return resource;
    }

    protected boolean hasResourceBeanData(String uri) {
        String resourceFileName = getResourceFileName(uri);
        return input.fileExists(configuration.getResourcesDirName(), resourceFileName);
    }

    protected ResourceBean readResourceBean(String uri) {
        String resourceFileName = getResourceFileName(uri);
        ResourceBean bean = (ResourceBean) deserialize(configuration.getResourcesDirName(), resourceFileName,
                configuration.getSerializer());
        return bean;
    }

    protected String getResourceFileName(String uri) {
        return uri + ".xml";
    }

    @Override
    public String getSourceJsVersion() {
        return super.getSourceJsVersion();
    }

    @Override
    public String getTargetJsVersion() {
        return super.getTargetJsVersion();
    }

    public ResourceReference handleReference(ResourceReferenceBean beanReference) {
        ResourceReference reference;
        if (beanReference == null) {
            reference = null;
        } else if (beanReference.isLocal()) {
            ResourceBean localResBean = beanReference.getLocalResource();
            Resource localRes = createResource(localResBean);
            reference = new ResourceReference(localRes);
        } else {
            String referenceURI = beanReference.getExternalURI();
            if (referenceURI == null || referenceURI.equals("")) {
                return null;
            }
            importResource(referenceURI, false);
            reference = new ResourceReference(prependedPath(referenceURI));
        }
        return reference;
    }

    public byte[] handleData(ResourceBean resourceBean, String dataFile, String providerId) {
        String filename = PathUtils.concatPaths(resourceBean.getFolder(), dataFile);
        InputStream dataInput = getFileInput(configuration.getResourcesDirName(), filename);
        boolean closeInput = true;
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            DataContainerStreamUtil.pipeData(dataInput, out);

            closeInput = false;
            dataInput.close();

            return out.toByteArray();
        } catch (IOException e) {
            log.error(e);
            throw new JSExceptionWrapper(e);
        } finally {
            if (closeInput) {
                try {
                    dataInput.close();
                } catch (IOException e) {
                    log.error(e);
                }
            }
        }
    }

    public Resource handleResource(ResourceBean resource) {
        Resource res = createResource(resource);
        return res;
    }

    public String handleResource(String uri) {
        return handleResource(uri, false);
    }

    public String handleResource(String uri, boolean ignoreMissing) {
        return importResource(uri, ignoreMissing);
    }

    protected void setPermissions(InternalURI object, RepositoryObjectPermissionBean[] permissions,
            boolean checkExisting) {
        if (permissions != null) {
            for (int i = 0; i < permissions.length; i++) {
                RepositoryObjectPermissionBean permissionBean = permissions[i];
                setPermission(object, permissionBean, checkExisting);
            }
        }
    }

    protected void setPermission(InternalURI object, RepositoryObjectPermissionBean permissionBean,
            boolean checkExisting) {
        ObjectPermissionService permissionsService = configuration.getPermissionService();

        PermissionRecipient permissionRecipient = permissionBean.getRecipient();
        Object recipient;
        String recipientType = permissionRecipient.getRecipientType();
        if (recipientType.equals(configuration.getPermissionRecipientRole())) {
            recipient = getRole(permissionRecipient);
            if (recipient == null) {
                commandOut.warn(
                        "Role " + permissionRecipient + " not found, skipping permission of " + object.getURI());
            }
        } else if (recipientType.equals(configuration.getPermissionRecipientUser())) {
            recipient = getUser(permissionRecipient);
            if (recipient == null) {
                commandOut.warn(
                        "User " + permissionRecipient + " not found, skipping permission of " + object.getURI());
            }
        } else {
            recipient = null;
            commandOut.warn("Unknown object permission recipient type " + recipientType
                    + ", skipping permission of " + object.getURI());
        }

        if (recipient != null) {
            boolean existing;
            if (checkExisting) {
                List permissions = permissionsService.getObjectPermissionsForObjectAndRecipient(executionContext,
                        object, recipient);
                existing = permissions != null && !permissions.isEmpty();
            } else {
                existing = false;
            }
            if (existing) {
                if (log.isInfoEnabled()) {
                    log.info("Permission on " + object.getURI() + " for " + permissionRecipient
                            + " already exists, skipping.");
                }
            } else {
                ObjectPermission permission = permissionsService.newObjectPermission(executionContext);
                permission.setURI(object.getURI());
                permission.setPermissionMask(permissionBean.getPermissionMask());
                permission.setPermissionRecipient(recipient);

                permissionsService.putObjectPermission(executionContext, permission);
            }
        }
    }

    protected Role getRole(TenantQualifiedName roleName) {
        Role role;
        if (roles.containsKey(roleName)) {
            role = (Role) roles.get(roleName);
        } else {
            role = loadRole(roleName);
            roles.put(roleName, role);
        }
        return role;
    }

    protected Role loadRole(TenantQualifiedName roleName) {
        return configuration.getAuthorityService().getRole(executionContext, roleName.getName());
    }

    protected User getUser(TenantQualifiedName username) {
        User user;
        if (users.containsKey(username)) {
            user = (User) users.get(username);
        } else {
            user = loadUser(username);
            users.put(username, user);
        }
        return user;
    }

    protected User loadUser(TenantQualifiedName username) {
        return configuration.getAuthorityService().getUser(executionContext, username.getName());
    }

    public ResourceModuleConfiguration getConfiguration() {
        return configuration;
    }

    public void setConfiguration(ResourceModuleConfiguration configuration) {
        this.configuration = configuration;
    }

    public TenantService getTenantService() {
        return tenantService;
    }

    public void setTenantService(TenantService tenantService) {
        this.tenantService = tenantService;
    }

    public String getPrependPathArg() {
        return prependPathArg;
    }

    public void setPrependPathArg(String prependPathArg) {
        this.prependPathArg = prependPathArg;
    }

    public String getUpdateArg() {
        return updateArg;
    }

    public void setUpdateArg(String updateArg) {
        this.updateArg = updateArg;
    }

    public String getIncludeSettingsArg() {
        return includeSettingsArg;
    }

    public void setIncludeSettingsArg(String includeSettingsArg) {
        this.includeSettingsArg = includeSettingsArg;
    }

    public String getSkipThemesArgument() {
        return skipThemesArgument;
    }

    public void setSkipThemesArgument(String skipThemesArgument) {
        this.skipThemesArgument = skipThemesArgument;
    }
}