com.b2international.snowowl.snomed.exporter.server.net4j.SnomedExportServerIndication.java Source code

Java tutorial

Introduction

Here is the source code for com.b2international.snowowl.snomed.exporter.server.net4j.SnomedExportServerIndication.java

Source

/*
 * Copyright 2011-2018 B2i Healthcare Pte Ltd, http://b2i.sg
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.b2international.snowowl.snomed.exporter.server.net4j;

import static com.google.common.collect.Sets.newHashSet;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.eclipse.net4j.signal.IndicationWithMonitoring;
import org.eclipse.net4j.signal.SignalProtocol;
import org.eclipse.net4j.util.io.ExtendedDataInputStream;
import org.eclipse.net4j.util.io.ExtendedDataOutputStream;
import org.eclipse.net4j.util.om.monitor.OMMonitor;

import com.b2international.commons.FileUtils;
import com.b2international.commons.time.TimeUtil;
import com.b2international.index.query.Query;
import com.b2international.index.revision.RevisionIndex;
import com.b2international.index.revision.RevisionIndexRead;
import com.b2international.index.revision.RevisionSearcher;
import com.b2international.snowowl.core.ApplicationContext;
import com.b2international.snowowl.core.LogUtils;
import com.b2international.snowowl.core.RepositoryManager;
import com.b2international.snowowl.core.api.Net4jProtocolConstants;
import com.b2international.snowowl.core.api.SnowowlRuntimeException;
import com.b2international.snowowl.core.branch.Branch;
import com.b2international.snowowl.core.date.Dates;
import com.b2international.snowowl.core.date.EffectiveTimes;
import com.b2international.snowowl.core.exceptions.BadRequestException;
import com.b2international.snowowl.datastore.BranchPathUtils;
import com.b2international.snowowl.datastore.CodeSystemEntry;
import com.b2international.snowowl.datastore.CodeSystemVersionEntry;
import com.b2international.snowowl.datastore.request.RepositoryRequests;
import com.b2international.snowowl.eventbus.IEventBus;
import com.b2international.snowowl.snomed.SnomedConstants;
import com.b2international.snowowl.snomed.common.ContentSubType;
import com.b2international.snowowl.snomed.common.SnomedTerminologyComponentConstants;
import com.b2international.snowowl.snomed.core.domain.refset.SnomedReferenceSet;
import com.b2international.snowowl.snomed.datastore.SnomedDatastoreActivator;
import com.b2international.snowowl.snomed.datastore.SnomedMapSetSetting;
import com.b2international.snowowl.snomed.datastore.index.entry.SnomedDescriptionIndexEntry;
import com.b2international.snowowl.snomed.datastore.internal.rf2.SnomedExportResult;
import com.b2international.snowowl.snomed.datastore.internal.rf2.SnomedExportResult.Result;
import com.b2international.snowowl.snomed.datastore.request.SnomedRequests;
import com.b2international.snowowl.snomed.exporter.server.SnomedExportContext;
import com.b2international.snowowl.snomed.exporter.server.SnomedExportContextImpl;
import com.b2international.snowowl.snomed.exporter.server.SnomedRefSetExporterFactory;
import com.b2international.snowowl.snomed.exporter.server.rf1.Id2Rf1PropertyMapper;
import com.b2international.snowowl.snomed.exporter.server.rf1.SnomedRf1ConceptExporter;
import com.b2international.snowowl.snomed.exporter.server.rf1.SnomedRf1RelationshipExporter;
import com.b2international.snowowl.snomed.exporter.server.rf2.SimpleSnomedLanguageRefsetExporter;
import com.b2international.snowowl.snomed.exporter.server.rf2.SnomedExporter;
import com.b2international.snowowl.snomed.exporter.server.rf2.SnomedInferredRelationshipExporter;
import com.b2international.snowowl.snomed.exporter.server.rf2.SnomedLanguageRefSetExporter;
import com.b2international.snowowl.snomed.exporter.server.rf2.SnomedRf2ConceptExporter;
import com.b2international.snowowl.snomed.exporter.server.rf2.SnomedRf2DescriptionExporter;
import com.b2international.snowowl.snomed.exporter.server.rf2.SnomedStatedRelationshipExporter;
import com.b2international.snowowl.snomed.exporter.server.rf2.SnomedTextDefinitionExporter;
import com.b2international.snowowl.snomed.snomedrefset.SnomedRefSetType;
import com.b2international.snowowl.terminologyregistry.core.request.CodeSystemRequests;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.primitives.Longs;

/**
 * This class receives requests from client side and depending the user request executes exports correspondingly.
 * 
 * The response is a zipped archive containing the exported files following the RF2 directory standard. The zipped archive and the working directory
 * can be found during the export in your system dependent temporary folder. After finishing the export and uploading the zipped file to the client
 * the working directory and the zipped archive will be deleted.
 * 
 */
public class SnomedExportServerIndication extends IndicationWithMonitoring {

    private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory
            .getLogger(SnomedExportServerIndication.class);

    private static final String RELEASE_ROOT_DIRECTORY_NAME = "SnomedCT_Release";

    private boolean coreComponentExport;
    private ContentSubType releaseType;
    private String unsetEffectiveTimeLabel;
    private boolean includeRf1;
    private boolean includeExtendedDescriptionTypes;
    private Set<String> modulesToExport;
    private Date startEffectiveTime;
    private Date endEffectiveTime;
    private String namespace;

    private List<SnomedReferenceSet> referenceSetsToExport;
    private Set<SnomedMapSetSetting> settings;

    // Used for logging
    private String userId;
    private String branchPath;

    private SnomedExportResult result;

    private SnomedExportContext exportContext;

    // indicates whether the unpublished artifacts should be part of the export process
    private boolean includeUnpublished;

    // this is the directory where the exported files with the RF2 directory "standard" are put
    private Path tempDir;

    private String codeSystemShortName;
    private boolean extensionOnly;
    private boolean conceptsAndRelationshipsOnly;
    private boolean languageAware;

    public SnomedExportServerIndication(SignalProtocol<?> protocol) {
        super(protocol, Net4jProtocolConstants.SNOMED_EXPORT_SIGNAL);
    }

    @Override
    protected int getIndicatingWorkPercent() {
        return 0;
    }

    @Override
    protected void indicating(ExtendedDataInputStream in, OMMonitor monitor) throws Exception {

        userId = in.readUTF();
        branchPath = in.readUTF();

        String startEffectiveTimeString = in.readUTF();
        String endEffectiveTimeString = in.readUTF();
        startEffectiveTime = startEffectiveTimeString.equals("") ? null
                : convertRF2StringToDate(startEffectiveTimeString);
        endEffectiveTime = endEffectiveTimeString.equals("") ? null
                : convertRF2StringToDate(endEffectiveTimeString);

        releaseType = ContentSubType.getByValue(in.readInt());
        unsetEffectiveTimeLabel = in.readUTF();
        includeUnpublished = in.readBoolean();

        includeRf1 = in.readBoolean();
        includeExtendedDescriptionTypes = in.readBoolean();

        coreComponentExport = in.readBoolean();

        final int numberOfRefSetsToExport = in.readInt();
        Set<String> refsetIdentifierConcepts = numberOfRefSetsToExport > 0
                ? Sets.<String>newHashSetWithExpectedSize(numberOfRefSetsToExport)
                : Collections.<String>emptySet();
        for (int i = 0; i < numberOfRefSetsToExport; i++) {
            refsetIdentifierConcepts.add(in.readUTF());
        }

        referenceSetsToExport = refsetIdentifierConcepts.isEmpty() ? Collections.<SnomedReferenceSet>emptyList()
                : SnomedRequests.prepareSearchRefSet().all().filterByIds(refsetIdentifierConcepts)
                        .build(SnomedDatastoreActivator.REPOSITORY_UUID, branchPath).execute(getEventBus())
                        .getSync().getItems();

        final int settingSize = in.readInt();
        settings = settingSize > 0 ? Sets.<SnomedMapSetSetting>newHashSetWithExpectedSize(settingSize)
                : Collections.<SnomedMapSetSetting>emptySet();
        for (int i = 0; i < settingSize; i++) {
            settings.add(SnomedMapSetSetting.read(in));
        }

        final int modulesToExportSize = in.readInt();
        modulesToExport = modulesToExportSize > 0 ? Sets.<String>newHashSetWithExpectedSize(modulesToExportSize)
                : Collections.<String>emptySet();
        for (int i = 0; i < modulesToExportSize; i++) {
            modulesToExport.add(in.readUTF());
        }

        namespace = in.readUTF();
        codeSystemShortName = in.readUTF();
        extensionOnly = in.readBoolean();
        conceptsAndRelationshipsOnly = in.readBoolean();
        languageAware = in.readBoolean();

        tempDir = Files.createTempDirectory("export");

        exportContext = new SnomedExportContextImpl(BranchPathUtils.createPath(branchPath), // TODO remove IBranchPath
                releaseType, unsetEffectiveTimeLabel, startEffectiveTime, endEffectiveTime, namespace,
                modulesToExport, new Id2Rf1PropertyMapper(), getReleaseRootPath(tempDir, namespace));

        logActivity(String.format("SNOMED CT %s export has been requested", releaseType.getDisplayName()));
    }

    @Override
    protected void responding(ExtendedDataOutputStream out, final OMMonitor monitor) throws Exception {

        File file = null;

        result = new SnomedExportResult();

        try {

            Stopwatch stopwatch = Stopwatch.createStarted();

            monitor.begin(calculateProgressMonitorStep());

            // obtain the index service here to ensure a consistent view of the data during the export process
            RepositoryManager repositoryManager = ApplicationContext.getInstance()
                    .getService(RepositoryManager.class);
            RevisionIndex revisionIndex = repositoryManager.get(SnomedDatastoreActivator.REPOSITORY_UUID)
                    .service(RevisionIndex.class);
            file = doExport(revisionIndex, monitor);

            logActivity("Transferring export result...");

            sendResult(out, file, monitor);

            logActivity(String.format("SNOMED CT export finished in %s", TimeUtil.toString(stopwatch)));

            monitor.worked(1);

        } catch (Exception e) {
            LOGGER.error("Failed to perform export", e);

            final String reason = null != e.getMessage() ? " Reason: '" + e.getMessage() + "'" : "";
            logActivity("Caught exception while exporting SNOMED CT terminology." + reason);

            if (e.getClass().isAssignableFrom(RuntimeException.class)) {
                result.setResultAndMessage(Result.EXCEPTION,
                        "An error occurred while exporting SNOMED CT components: could not retrieve data from database.");
            } else if (e.getClass().isAssignableFrom(IOException.class)) {
                result.setResultAndMessage(Result.EXCEPTION,
                        "An error occurred while exporting SNOMED CT components: could not create release files.");
            }

        } finally {

            monitor.done();

            if (null != file) {
                file.delete();
            }

            FileUtils.deleteDirectory(tempDir.toFile());
        }
    }

    private void sendResult(ExtendedDataOutputStream out, File file, OMMonitor monitor) throws IOException {

        out.writeObject(result);

        if (Result.SUCCESSFUL == result.getResult()) {
            long size = file.length();
            BufferedInputStream in = null;

            monitor.fork(size);

            out.writeLong(size);

            try {
                in = new BufferedInputStream(new FileInputStream(file));
                while (size != 0L) {
                    int chunk = Net4jProtocolConstants.BUFFER_SIZE;
                    if (size < Net4jProtocolConstants.BUFFER_SIZE) {
                        chunk = (int) size;
                    }

                    monitor.worked(chunk / 1.0);

                    byte[] buffer = new byte[chunk];
                    in.read(buffer);
                    out.writeByteArray(buffer);

                    size -= chunk;
                }
            } finally {
                in.close();
            }
        }

    }

    private File doExport(final RevisionIndex revisionIndex, final OMMonitor monitor) throws Exception {

        switch (exportContext.getContentSubType()) {
        case DELTA:
            executeDeltaExport(revisionIndex, monitor);
            break;
        case SNAPSHOT:
            executeSnapshotExport(revisionIndex, monitor);
            break;
        case FULL:
            executeFullExport(revisionIndex, monitor);
            break;
        }

        logActivity("Creating SNOMED CT export archive...");
        File zipFile = FileUtils.createZipArchive(tempDir.toFile(),
                Files.createTempFile("export", ".zip").toFile());

        if (monitor.isCanceled()) {
            processCancel();
            return null;
        } else {
            monitor.worked(1);
        }

        return zipFile;
    }

    private void executeDeltaExport(final RevisionIndex revisionIndex, final OMMonitor monitor) {

        if (startEffectiveTime == null && endEffectiveTime == null) {

            executeExport(revisionIndex, branchPath, true, monitor);

        } else {

            List<CodeSystemVersionEntry> sortedVersions = FluentIterable.from(getCodeSystemVersions())
                    .filter(new Predicate<CodeSystemVersionEntry>() {
                        @Override
                        public boolean apply(CodeSystemVersionEntry input) {

                            Date versionEffectiveDate = new Date(input.getEffectiveDate());

                            if (startEffectiveTime != null && endEffectiveTime != null) {
                                return (versionEffectiveDate.after(startEffectiveTime)
                                        || versionEffectiveDate.equals(startEffectiveTime))
                                        && (versionEffectiveDate.before(endEffectiveTime)
                                                || versionEffectiveDate.equals(endEffectiveTime));
                            } else if (startEffectiveTime == null) {
                                return versionEffectiveDate.before(endEffectiveTime)
                                        || versionEffectiveDate.equals(endEffectiveTime);
                            } else if (endEffectiveTime == null) {
                                return versionEffectiveDate.after(startEffectiveTime)
                                        || versionEffectiveDate.equals(startEffectiveTime);
                            }

                            return false;
                        }

                    }).toSortedList((o1, o2) -> Longs.compare(o1.getEffectiveDate(), o2.getEffectiveDate()));

            if (sortedVersions.isEmpty()) {
                String message = null;
                if (startEffectiveTime != null && endEffectiveTime != null) {
                    message = String.format("No version branch found to export between the effective dates %s - %s",
                            Dates.formatByHostTimeZone(startEffectiveTime),
                            Dates.formatByHostTimeZone(endEffectiveTime));
                } else if (startEffectiveTime == null) {
                    message = String.format("No version branch found to export before the effective date %s",
                            Dates.formatByHostTimeZone(endEffectiveTime));
                } else if (endEffectiveTime == null) {
                    message = String.format("No version branch found to export after the effective date %s",
                            Dates.formatByHostTimeZone(startEffectiveTime));
                }
                throw new BadRequestException(message);
            }

            List<String> versionBranchPaths = convertToBranchPaths(sortedVersions);

            for (String versionBranchPath : versionBranchPaths) {
                executeExport(revisionIndex, versionBranchPath, false, monitor);
            }

            if (includeUnpublished) {
                executeExport(revisionIndex, branchPath, true, monitor);
            }
        }

    }

    private void executeSnapshotExport(RevisionIndex revisionIndex, OMMonitor monitor) {

        executeExport(revisionIndex, branchPath, false, monitor);

        if (includeUnpublished) {
            executeExport(revisionIndex, branchPath, true, monitor);
        }

    }

    private void executeFullExport(RevisionIndex revisionIndex, OMMonitor monitor) {

        List<CodeSystemVersionEntry> sortedVersions = FluentIterable.from(getCodeSystemVersions())
                .toSortedList(new Comparator<CodeSystemVersionEntry>() {
                    @Override
                    public int compare(CodeSystemVersionEntry o1, CodeSystemVersionEntry o2) {
                        return Longs.compare(o1.getEffectiveDate(), o2.getEffectiveDate());
                    }
                });

        long startTime = 0L;

        for (CodeSystemVersionEntry version : sortedVersions) {

            exportContext.setStartEffectiveTime(new Date(startTime));
            exportContext.setEndEffectiveTime(new Date(version.getEffectiveDate()));

            String versionBranchPath = convertToBranchPath(version);
            executeExport(revisionIndex, versionBranchPath, false, monitor);

            startTime = version.getEffectiveDate();
        }

        if (includeUnpublished) {
            executeExport(revisionIndex, branchPath, true, monitor);
        }

    }

    private void executeExport(final RevisionIndex revisionIndex, final String versionBranchPath,
            final boolean unpublishedExport, final OMMonitor monitor) {

        exportContext.setUnpublishedExport(unpublishedExport);

        revisionIndex.read(versionBranchPath, new RevisionIndexRead<Void>() {

            @Override
            public Void execute(RevisionSearcher revisionSearcher) throws IOException {

                if (monitor.isCanceled()) {
                    processCancel();
                    return null;
                }

                if (coreComponentExport) {
                    logActivity(String.format("Starting export of %score components from branch path '%s'",
                            unpublishedExport ? "unpublished " : "", versionBranchPath));
                    executeCoreExport(revisionSearcher, monitor);
                }

                if (!referenceSetsToExport.isEmpty()) {

                    logActivity(String.format("Starting export of %sreference sets from branch path '%s'",
                            unpublishedExport ? "unpublished " : "", versionBranchPath));

                    for (SnomedReferenceSet referenceSet : referenceSetsToExport) {

                        if (monitor.isCanceled()) {
                            processCancel();
                            return null;
                        }

                        executeRefSetExport(referenceSet, revisionSearcher, monitor);
                    }
                }

                return null;
            }

        });

    }

    private void executeCoreExport(final RevisionSearcher revisionSearcher, final OMMonitor monitor)
            throws IOException {

        logActivity(String.format("Exporting %sSNOMED CT concepts into RF2 format",
                exportContext.isUnpublishedExport() ? "unpublished " : ""));
        new SnomedRf2ConceptExporter(exportContext, revisionSearcher).execute();

        if (monitor.isCanceled()) {
            return;
        } else {
            monitor.worked(2);
        }

        if (!conceptsAndRelationshipsOnly) {

            if (languageAware) {

                Set<String> languageCodesInUse = getLanguageCodesInUse(revisionSearcher);

                for (String languageCode : languageCodesInUse) {
                    logActivity(String.format(
                            "Exporting %sSNOMED CT descriptions with language code '%s' into RF2 format",
                            exportContext.isUnpublishedExport() ? "unpublished " : "", languageCode));
                    new SnomedRf2DescriptionExporter(exportContext, revisionSearcher, languageCode).execute();
                }

                if (monitor.isCanceled()) {
                    return;
                } else {
                    monitor.worked(2);
                }

                for (String languageCode : languageCodesInUse) {
                    logActivity(String.format(
                            "Exporting %sSNOMED CT text definitions with language code '%s' into RF2 format",
                            exportContext.isUnpublishedExport() ? "unpublished " : "", languageCode));
                    new SnomedTextDefinitionExporter(exportContext, revisionSearcher, languageCode).execute();
                }

                if (monitor.isCanceled()) {
                    return;
                } else {
                    monitor.worked(2);
                }

                if (languageCodesInUse.size() == 1) {
                    String languageCode = Iterables.getOnlyElement(languageCodesInUse);
                    logActivity(String.format(
                            "Exporting %sSNOMED CT language reference set members with language code '%s' into RF2 format",
                            exportContext.isUnpublishedExport() ? "unpublished " : "", languageCode));
                    new SimpleSnomedLanguageRefsetExporter(exportContext, revisionSearcher, languageCode).execute();
                } else {
                    for (String languageCode : languageCodesInUse) {
                        logActivity(String.format(
                                "Exporting %sSNOMED CT language reference set members with language code '%s' into RF2 format",
                                exportContext.isUnpublishedExport() ? "unpublished " : "", languageCode));
                        new SnomedLanguageRefSetExporter(exportContext, revisionSearcher, languageCode).execute();
                    }
                }

                if (monitor.isCanceled()) {
                    return;
                } else {
                    monitor.worked(2);
                }

            } else {

                logActivity(String.format("Exporting %sSNOMED CT descriptions into RF2 format",
                        exportContext.isUnpublishedExport() ? "unpublished " : ""));

                new SnomedRf2DescriptionExporter(exportContext, revisionSearcher).execute();

                if (monitor.isCanceled()) {
                    return;
                } else {
                    monitor.worked(2);
                }

                logActivity(String.format("Exporting %sSNOMED CT text definitions into RF2 format",
                        exportContext.isUnpublishedExport() ? "unpublished " : ""));
                new SnomedTextDefinitionExporter(exportContext, revisionSearcher).execute();

                if (monitor.isCanceled()) {
                    return;
                } else {
                    monitor.worked(2);
                }

                logActivity(String.format("Exporting %sSNOMED CT language reference set members into RF2 format",
                        exportContext.isUnpublishedExport() ? "unpublished " : ""));
                new SnomedLanguageRefSetExporter(exportContext, revisionSearcher, "en").execute(); // TODO

                if (monitor.isCanceled()) {
                    return;
                } else {
                    monitor.worked(2);
                }

            }

        }

        logActivity(String.format("Exporting non-stated %sSNOMED CT relationships into RF2 format",
                exportContext.isUnpublishedExport() ? "unpublished " : ""));
        new SnomedInferredRelationshipExporter(exportContext, revisionSearcher, conceptsAndRelationshipsOnly)
                .execute();

        if (monitor.isCanceled()) {
            return;
        } else {
            monitor.worked(2);
        }

        logActivity(String.format("Exporting stated %sSNOMED CT relationships into RF2 format",
                exportContext.isUnpublishedExport() ? "unpublished " : ""));
        new SnomedStatedRelationshipExporter(exportContext, revisionSearcher).execute();

        if (monitor.isCanceled()) {
            return;
        } else {
            monitor.worked(2);
        }

        if (!exportContext.isUnpublishedExport() && includeRf1) {

            logActivity("Exporting SNOMED CT concepts into RF1 format");
            new SnomedRf1ConceptExporter(exportContext, revisionSearcher).execute();

            if (monitor.isCanceled()) {
                return;
            } else {
                monitor.worked(2);
            }

            logActivity("Exporting SNOMED CT relationships into RF1 format");
            new SnomedRf1RelationshipExporter(exportContext, revisionSearcher).execute();

            if (monitor.isCanceled()) {
                return;
            } else {
                monitor.worked(2);
            }
        }
    }

    private Set<String> getLanguageCodesInUse(final RevisionSearcher revisionSearcher) throws IOException {

        Set<String> languageCodesInUse = newHashSet();

        for (String code : Locale.getISOLanguages()) {
            int size = revisionSearcher
                    .search(Query.select(SnomedDescriptionIndexEntry.class)
                            .where(SnomedDescriptionIndexEntry.Expressions.languageCode(code)).limit(1).build())
                    .getHits().size();

            if (size > 0) {
                languageCodesInUse.add(code);
            }
        }

        return languageCodesInUse;
    }

    private void executeRefSetExport(final SnomedReferenceSet refset, final RevisionSearcher revisionSearcher,
            final OMMonitor monitor) throws IOException {

        if (refset.getType() != SnomedRefSetType.LANGUAGE) {

            final SnomedExporter refSetExporter = SnomedRefSetExporterFactory.getRefSetExporter(refset,
                    exportContext, revisionSearcher);

            logActivity(String.format(
                    "Exporting SNOMED CT reference set into RF2 format. Reference set identifier concept ID: %s",
                    refset.getId()));
            refSetExporter.execute();

        }

        if (!exportContext.isUnpublishedExport() && includeRf1) {

            logActivity("Exporting SNOMED CT reference set into RF1 format. Reference set identifier concept ID: "
                    + refset.getId());

            // RF1 subset exporter.
            Iterable<SnomedExporter> subsetExporters = SnomedRefSetExporterFactory.getSubsetExporter(refset,
                    exportContext, revisionSearcher);

            for (final SnomedExporter exporter : subsetExporters) {
                exporter.execute();
            }

            // RF1 map set exporter.
            final SnomedMapSetSetting mapsetSetting = getSettingForRefSet(refset.getId());
            if (null != mapsetSetting) {
                Iterable<SnomedExporter> crossMapExporters = SnomedRefSetExporterFactory.getCrossMapExporter(refset,
                        exportContext, mapsetSetting, revisionSearcher);
                for (final SnomedExporter exporter : crossMapExporters) {
                    exporter.execute();
                }
            }
        }

        monitor.worked(1);
    }

    private List<String> convertToBranchPaths(List<CodeSystemVersionEntry> sortedVersions) {
        return FluentIterable.from(sortedVersions).transform(new Function<CodeSystemVersionEntry, String>() {
            @Override
            public String apply(CodeSystemVersionEntry input) {
                return convertToBranchPath(input);
            }
        }).toList();
    }

    private String convertToBranchPath(CodeSystemVersionEntry version) {
        return version.getPath();
    }

    private void processCancel() {
        logActivity("SNOMED CT export canceled.");
        result.setResult(Result.CANCELED);
    }

    private int calculateProgressMonitorStep() {
        int counter = 0;

        if (coreComponentExport) {
            if (conceptsAndRelationshipsOnly) {
                counter += 6;
                if (includeRf1) {
                    counter += 4;
                }

            } else {
                counter += 12;
                if (includeRf1) {
                    counter += 6;
                }
            }
        }

        if (!conceptsAndRelationshipsOnly) {
            counter += FluentIterable.from(referenceSetsToExport).filter(new Predicate<SnomedReferenceSet>() {
                @Override
                public boolean apply(SnomedReferenceSet input) {
                    return input.getType() != SnomedRefSetType.LANGUAGE;
                }
            }).size();
        }

        counter++; // compressing zip
        counter++; // sending file to the client;

        return counter;
    }

    private Date convertRF2StringToDate(String dateInRF2Format) {
        try {
            return EffectiveTimes.parse(dateInRF2Format, SnomedConstants.RF2_EFFECTIVE_TIME_FORMAT);
        } catch (SnowowlRuntimeException e) {
            LOGGER.error(String.format("Couldn't parse RF2 date %s.", dateInRF2Format), e);
            throw new BadRequestException("Couldn't parse RF2 date %s.", dateInRF2Format);
        }
    }

    /*returns with the map set setting for the specified reference set identifier concept ID*/
    private SnomedMapSetSetting getSettingForRefSet(final String refSetId) {
        return Iterables.getOnlyElement(Iterables.filter(settings, new Predicate<SnomedMapSetSetting>() {
            @Override
            public boolean apply(final SnomedMapSetSetting setting) {
                return setting.getRefSetId().equals(refSetId);
            }
        }), null);
    }

    private void logActivity(final String message) {
        LogUtils.logExportActivity(LOGGER, userId, branchPath, message);
    }

    private Collection<CodeSystemVersionEntry> getCodeSystemVersions() {

        if (extensionOnly) {

            return CodeSystemRequests.prepareSearchCodeSystemVersion().all()
                    .filterByCodeSystemShortName(codeSystemShortName)
                    .build(SnomedDatastoreActivator.REPOSITORY_UUID).execute(getEventBus()).getSync().getItems();

        }

        return collectAllCodeSystemVersions(getCodeSystem(codeSystemShortName));
    }

    private Collection<CodeSystemVersionEntry> collectAllCodeSystemVersions(CodeSystemEntry codeSystem) {

        Set<CodeSystemVersionEntry> results = newHashSet();

        List<CodeSystemVersionEntry> codeSystemVersions = CodeSystemRequests.prepareSearchCodeSystemVersion().all()
                .filterByCodeSystemShortName(codeSystem.getShortName())
                .build(SnomedDatastoreActivator.REPOSITORY_UUID).execute(getEventBus()).getSync().getItems();

        results.addAll(codeSystemVersions);

        if (!codeSystem.getShortName().equals(SnomedTerminologyComponentConstants.SNOMED_SHORT_NAME)) {

            if (!Strings.isNullOrEmpty(codeSystem.getExtensionOf())) {

                Branch codeSystemBranch = RepositoryRequests.branching().prepareGet(codeSystem.getBranchPath())
                        .build(SnomedDatastoreActivator.REPOSITORY_UUID).execute(getEventBus()).getSync();

                final CodeSystemEntry parentCodeSystem = getCodeSystem(codeSystem.getExtensionOf());

                final CodeSystemVersionEntry parentVersion = Iterables
                        .getOnlyElement(CodeSystemRequests.prepareSearchCodeSystemVersion().one()
                                .filterByCodeSystemShortName(parentCodeSystem.getShortName())
                                .filterByVersionId(codeSystemBranch.parent().name()) // must be a version branch of the parent extension 
                                .build(SnomedDatastoreActivator.REPOSITORY_UUID).execute(getEventBus()).getSync());

                Collection<CodeSystemVersionEntry> parentVersions = collectAllCodeSystemVersions(parentCodeSystem);

                Set<CodeSystemVersionEntry> versionsToRemove = FluentIterable.from(parentVersions)
                        .filter(new Predicate<CodeSystemVersionEntry>() {
                            @Override
                            public boolean apply(CodeSystemVersionEntry input) {
                                return input.getCodeSystemShortName().equals(parentCodeSystem.getShortName())
                                        && input.getEffectiveDate() > parentVersion.getEffectiveDate();
                            }
                        }).toSet();

                parentVersions.removeAll(versionsToRemove);

                results.addAll(parentVersions);
            }

        }

        return results;
    }

    private CodeSystemEntry getCodeSystem(String shortName) {
        return Iterables.getOnlyElement(CodeSystemRequests.prepareSearchCodeSystem().one().filterById(shortName)
                .build(SnomedDatastoreActivator.REPOSITORY_UUID).execute(getEventBus()).getSync());
    }

    private IEventBus getEventBus() {
        return ApplicationContext.getServiceForClass(IEventBus.class);
    }

    private Path getReleaseRootPath(Path tempDir, String namespace) {
        String dirName = Strings.isNullOrEmpty(namespace) ? RELEASE_ROOT_DIRECTORY_NAME
                : String.format("%s_%s", RELEASE_ROOT_DIRECTORY_NAME, namespace);
        return Paths.get(tempDir.toString(), dirName);
    }
}