<commit_before>/*
 * Copyright 2011-2015 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.datastore.server.snomed.version;

import static com.b2international.snowowl.core.ApplicationContext.getServiceForClass;
import static com.b2international.snowowl.snomed.SnomedConstants.Concepts.REFSET_MODULE_DEPENDENCY_TYPE;

import java.util.Collection;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;

import com.b2international.collections.longs.LongSet;
import com.b2international.snowowl.core.ApplicationContext;
import com.b2international.snowowl.core.api.IBranchPath;
import com.b2international.snowowl.datastore.CDOEditingContext;
import com.b2international.snowowl.datastore.server.snomed.SnomedModuleDependencyCollectorService;
import com.b2international.snowowl.datastore.server.version.PublishManager;
import com.b2international.snowowl.snomed.SnomedPackage;
import com.b2international.snowowl.snomed.common.SnomedTerminologyComponentConstants;
import com.b2international.snowowl.snomed.datastore.SnomedDatastoreActivator;
import com.b2international.snowowl.snomed.datastore.SnomedEditingContext;
import com.b2international.snowowl.snomed.datastore.SnomedRefSetLookupService;
import com.b2international.snowowl.snomed.datastore.id.ISnomedIdentifierService;
import com.b2international.snowowl.snomed.datastore.id.SnomedIdentifiers;
import com.b2international.snowowl.snomed.datastore.services.ISnomedComponentService;
import com.b2international.snowowl.snomed.datastore.services.ISnomedComponentService.IdStorageKeyPair;
import com.b2international.snowowl.snomed.snomedrefset.SnomedModuleDependencyRefSetMember;
import com.b2international.snowowl.snomed.snomedrefset.SnomedRefSetPackage;
import com.b2international.snowowl.snomed.snomedrefset.SnomedRegularRefSet;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

/**
 * Publish manager for SNOMED&nbsp;CT ontology.
 * <p>This class is responsible for the followings:
 * <ul>
 * <li>Adjust the effective time on all unpublished SNOMED&nbsp;CT components and reference set members.</li>
 * <li>Sets the released flags on all un-released SNOMED&nbsp;CT components and reference set members.</li>
 * <li>Updates the module dependency reference set state based on the overall component changes.</li>
 * </ul>
 */
public class SnomedPublishManager extends PublishManager {

	private final Collection<SnomedModuleDependencyRefSetMember> newModuleDependencyRefSetMembers = Sets.newHashSet();
	private final Collection<String> componentIdsToPublish = Sets.newHashSet();
	
	private final SnomedIdentifiers snomedIdentifiers;
	private final ISnomedComponentService componentService;
	
	public SnomedPublishManager() {
		this.snomedIdentifiers = new SnomedIdentifiers(ApplicationContext.getInstance().getServiceChecked(ISnomedIdentifierService.class));
		this.componentService = ApplicationContext.getInstance().getServiceChecked(ISnomedComponentService.class);
	}
	
	@Override
	protected LongSet getUnversionedComponentStorageKeys(final IBranchPath branchPath) {
		final ISnomedComponentService componentService = getServiceForClass(ISnomedComponentService.class);
		return componentService.getAllUnpublishedComponentStorageKeys(branchPath);
	}

	@Override
	protected EStructuralFeature getEffectiveTimeFeature(final EClass eClass) {
		if (isCoreComponent(eClass)) {
			return SnomedPackage.eINSTANCE.getComponent_EffectiveTime();
		} else if (isRefSetMember(eClass)) {
			return SnomedRefSetPackage.eINSTANCE.getSnomedRefSetMember_EffectiveTime();
		}
		throw new IllegalArgumentException("Unsupported or unexpected component type: " + eClass);
	}

	@Override
	protected CDOEditingContext createEditingContext(IBranchPath branchPath) {
		return new SnomedEditingContext(branchPath);
	}
	
	@Override
	protected EStructuralFeature getReleasedFeature(final EClass eClass) {
		if (isCoreComponent(eClass)) {
			return SnomedPackage.eINSTANCE.getComponent_Released();
		} else if (isRefSetMember(eClass)) {
			return SnomedRefSetPackage.eINSTANCE.getSnomedRefSetMember_Released();
		}
		throw new IllegalArgumentException("Unsupported or unexpected component type: " + eClass);
	}

	@Override
	protected String getRepositoryUuid() {
		return SnomedDatastoreActivator.REPOSITORY_UUID;
	}

	/**
	 * Since effective time and publication cannot be interpreted for SNOMED&nbsp;CT reference sets, we ignore them.
	 * <p>{@inheritDoc}}
	 */
	@Override
	protected boolean isIgnoredType(final EClass eClass) {
		return SnomedRefSetPackage.eINSTANCE.getSnomedRefSet().isSuperTypeOf(eClass);
	}
	
	@Override
	protected void preProcess(final LongSet storageKeys) {
		collectModuleDependencyChanges(storageKeys);
		collectComponentIdsToPublish(storageKeys);
	}

	private void collectModuleDependencyChanges(final LongSet storageKeys) {
		LOGGER.info("Collecting module dependency changes...");
		newModuleDependencyRefSetMembers.addAll(collectModuleDependecyRefSetMembers(storageKeys));
		LOGGER.info("Collecting module dependency changes successfully finished.");
	}

	private void collectComponentIdsToPublish(final LongSet storageKeys) {
		LOGGER.info("Collecting component IDs for ID publication...");
		final Collection<IdStorageKeyPair> idStorageKeyPairs = getIdStorageKeyPairs(storageKeys);
		
		for (final IdStorageKeyPair idStorageKeyPair : idStorageKeyPairs) {
			componentIdsToPublish.add(idStorageKeyPair.getId());
		}
		
		LOGGER.info("Collecting component IDs for ID publication successfully finished.");
	}

	private Collection<IdStorageKeyPair> getIdStorageKeyPairs(final LongSet storageKeys) {
		final Collection<IdStorageKeyPair> pairs = Lists.newArrayList();
		pairs.addAll(componentService.getAllComponentIdStorageKeys(getBranchPathForPublication(),
				SnomedTerminologyComponentConstants.CONCEPT_NUMBER));
		pairs.addAll(componentService.getAllComponentIdStorageKeys(getBranchPathForPublication(),
				SnomedTerminologyComponentConstants.DESCRIPTION_NUMBER));
		pairs.addAll(componentService.getAllComponentIdStorageKeys(getBranchPathForPublication(),
				SnomedTerminologyComponentConstants.RELATIONSHIP_NUMBER));
		
		final Collection<IdStorageKeyPair> filteredPairs = Collections2.filter(pairs, new Predicate<IdStorageKeyPair>() {
			@Override
			public boolean apply(IdStorageKeyPair input) {
				return storageKeys.contains(input.getStorageKey());
			}
		});
		
		return filteredPairs;
	}

	@Override
	protected void postProcess() {
		LOGGER.info("Adjusting effective time changes on module dependency...");
		adjustNewModuleDependencyRefSetMembers();
		LOGGER.info("Effective time adjustment successfully finished on module dependency.");
	}
	
	@Override
	public void postCommit() {
		if (!componentIdsToPublish.isEmpty()) {
			snomedIdentifiers.publish(componentIdsToPublish);
		}
		super.postCommit();
	}
	
	/**Returns {@code true} if the given {@link EClass} represents a SNOMED&nbsp;CT reference set member. Otherwise {@code false}.*/
	private boolean isRefSetMember(final EClass eClass) {
		return SnomedRefSetPackage.eINSTANCE.getSnomedRefSetMember().isSuperTypeOf(eClass);
	}

	/**Returns {@code true} if the given {@link EClass} represents a SNOMED&nbsp;CT core component.*/
	private boolean isCoreComponent(final EClass eClass) {
		return SnomedPackage.eINSTANCE.getComponent().isSuperTypeOf(eClass);
	}

	/**Updates all new module dependency reference set members.*/
	private void adjustNewModuleDependencyRefSetMembers() {<commit_msg>Create snomed specific version and add it to the proper snomed release.<commit_after>/*
 * Copyright 2011-2015 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.datastore.server.snomed.version;

import static com.b2international.snowowl.core.ApplicationContext.getServiceForClass;
import static com.b2international.snowowl.snomed.SnomedConstants.Concepts.REFSET_MODULE_DEPENDENCY_TYPE;

import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;

import com.b2international.collections.longs.LongSet;
import com.b2international.commons.functions.UncheckedCastFunction;
import com.b2international.snowowl.core.ApplicationContext;
import com.b2international.snowowl.core.api.IBranchPath;
import com.b2international.snowowl.datastore.CDOEditingContext;
import com.b2international.snowowl.datastore.server.snomed.SnomedModuleDependencyCollectorService;
import com.b2international.snowowl.datastore.server.version.PublishManager;
import com.b2international.snowowl.snomed.SnomedFactory;
import com.b2international.snowowl.snomed.SnomedPackage;
import com.b2international.snowowl.snomed.SnomedRelease;
import com.b2international.snowowl.snomed.SnomedVersion;
import com.b2international.snowowl.snomed.common.SnomedTerminologyComponentConstants;
import com.b2international.snowowl.snomed.datastore.SnomedDatastoreActivator;
import com.b2international.snowowl.snomed.datastore.SnomedEditingContext;
import com.b2international.snowowl.snomed.datastore.SnomedInternationalCodeSystemFactory;
import com.b2international.snowowl.snomed.datastore.SnomedRefSetLookupService;
import com.b2international.snowowl.snomed.datastore.id.ISnomedIdentifierService;
import com.b2international.snowowl.snomed.datastore.id.SnomedIdentifiers;
import com.b2international.snowowl.snomed.datastore.services.ISnomedComponentService;
import com.b2international.snowowl.snomed.datastore.services.ISnomedComponentService.IdStorageKeyPair;
import com.b2international.snowowl.snomed.snomedrefset.SnomedModuleDependencyRefSetMember;
import com.b2international.snowowl.snomed.snomedrefset.SnomedRefSetPackage;
import com.b2international.snowowl.snomed.snomedrefset.SnomedRegularRefSet;
import com.b2international.snowowl.terminologymetadata.CodeSystem;
import com.b2international.snowowl.terminologymetadata.CodeSystemVersion;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

/**
 * Publish manager for SNOMED&nbsp;CT ontology.
 * <p>This class is responsible for the followings:
 * <ul>
 * <li>Adjust the effective time on all unpublished SNOMED&nbsp;CT components and reference set members.</li>
 * <li>Sets the released flags on all un-released SNOMED&nbsp;CT components and reference set members.</li>
 * <li>Updates the module dependency reference set state based on the overall component changes.</li>
 * </ul>
 */
public class SnomedPublishManager extends PublishManager {

	private final Collection<SnomedModuleDependencyRefSetMember> newModuleDependencyRefSetMembers = Sets.newHashSet();
	private final Collection<String> componentIdsToPublish = Sets.newHashSet();
	
	private final SnomedIdentifiers snomedIdentifiers;
	private final ISnomedComponentService componentService;
	
	public SnomedPublishManager() {
		this.snomedIdentifiers = new SnomedIdentifiers(ApplicationContext.getInstance().getServiceChecked(ISnomedIdentifierService.class));
		this.componentService = ApplicationContext.getInstance().getServiceChecked(ISnomedComponentService.class);
	}
	
	@Override
	protected LongSet getUnversionedComponentStorageKeys(final IBranchPath branchPath) {
		final ISnomedComponentService componentService = getServiceForClass(ISnomedComponentService.class);
		return componentService.getAllUnpublishedComponentStorageKeys(branchPath);
	}

	@Override
	protected EStructuralFeature getEffectiveTimeFeature(final EClass eClass) {
		if (isCoreComponent(eClass)) {
			return SnomedPackage.eINSTANCE.getComponent_EffectiveTime();
		} else if (isRefSetMember(eClass)) {
			return SnomedRefSetPackage.eINSTANCE.getSnomedRefSetMember_EffectiveTime();
		}
		throw new IllegalArgumentException("Unsupported or unexpected component type: " + eClass);
	}

	@Override
	protected CDOEditingContext createEditingContext(IBranchPath branchPath) {
		return new SnomedEditingContext(branchPath);
	}
	
	@Override
	protected EStructuralFeature getReleasedFeature(final EClass eClass) {
		if (isCoreComponent(eClass)) {
			return SnomedPackage.eINSTANCE.getComponent_Released();
		} else if (isRefSetMember(eClass)) {
			return SnomedRefSetPackage.eINSTANCE.getSnomedRefSetMember_Released();
		}
		throw new IllegalArgumentException("Unsupported or unexpected component type: " + eClass);
	}

	@Override
	protected String getRepositoryUuid() {
		return SnomedDatastoreActivator.REPOSITORY_UUID;
	}

	/**
	 * Since effective time and publication cannot be interpreted for SNOMED&nbsp;CT reference sets, we ignore them.
	 * <p>{@inheritDoc}}
	 */
	@Override
	protected boolean isIgnoredType(final EClass eClass) {
		return SnomedRefSetPackage.eINSTANCE.getSnomedRefSet().isSuperTypeOf(eClass);
	}
	
	@Override
	protected void preProcess(final LongSet storageKeys) {
		collectModuleDependencyChanges(storageKeys);
		collectComponentIdsToPublish(storageKeys);
	}

	private void collectModuleDependencyChanges(final LongSet storageKeys) {
		LOGGER.info("Collecting module dependency changes...");
		newModuleDependencyRefSetMembers.addAll(collectModuleDependecyRefSetMembers(storageKeys));
		LOGGER.info("Collecting module dependency changes successfully finished.");
	}

	private void collectComponentIdsToPublish(final LongSet storageKeys) {
		LOGGER.info("Collecting component IDs for ID publication...");
		final Collection<IdStorageKeyPair> idStorageKeyPairs = getIdStorageKeyPairs(storageKeys);
		
		for (final IdStorageKeyPair idStorageKeyPair : idStorageKeyPairs) {
			componentIdsToPublish.add(idStorageKeyPair.getId());
		}
		
		LOGGER.info("Collecting component IDs for ID publication successfully finished.");
	}

	private Collection<IdStorageKeyPair> getIdStorageKeyPairs(final LongSet storageKeys) {
		final Collection<IdStorageKeyPair> pairs = Lists.newArrayList();
		pairs.addAll(componentService.getAllComponentIdStorageKeys(getBranchPathForPublication(),
				SnomedTerminologyComponentConstants.CONCEPT_NUMBER));
		pairs.addAll(componentService.getAllComponentIdStorageKeys(getBranchPathForPublication(),
				SnomedTerminologyComponentConstants.DESCRIPTION_NUMBER));
		pairs.addAll(componentService.getAllComponentIdStorageKeys(getBranchPathForPublication(),
				SnomedTerminologyComponentConstants.RELATIONSHIP_NUMBER));
		
		final Collection<IdStorageKeyPair> filteredPairs = Collections2.filter(pairs, new Predicate<IdStorageKeyPair>() {
			@Override
			public boolean apply(IdStorageKeyPair input) {
				return storageKeys.contains(input.getStorageKey());
			}
		});
		
		return filteredPairs;
	}
	
	@Override
	protected CodeSystemVersion createCodeSystemVersion() {
		final SnomedVersion snomedVersion = SnomedFactory.eINSTANCE.createSnomedVersion();
		snomedVersion.setEffectiveDate(getEffectiveTime());
		snomedVersion.setImportDate(new Date());
		snomedVersion.setVersionId(getVersionName());
		snomedVersion.setParentBranchPath(getParentBranchPath());
		snomedVersion.setDescription(getCodeSystemVersionDescription());

		// TODO add snomed version specific settings
		// snomedVersion.getModules().add(null);

		return snomedVersion;
	}

	@Override
	protected void addCodeSystemVersion(final CodeSystemVersion codeSystemVersion) {
		final String shortName = getReleaseShortName();
		final List<CodeSystem> codeSystems = getEditingContext().getCodeSystems();
		final Optional<SnomedRelease> optional = FluentIterable.from(codeSystems)
				.transform(new UncheckedCastFunction<>(SnomedRelease.class))
				.firstMatch(new Predicate<SnomedRelease>() {
					@Override
					public boolean apply(final SnomedRelease input) {
						return input.getShortName().equalsIgnoreCase(shortName);
					}
				});

		if (!optional.isPresent()) {
			throw new IllegalStateException(String.format("Couldn't find SNOMED release for %s.", shortName));
		} else {
			final CodeSystem codeSystem = optional.get();
			codeSystem.getCodeSystemVersions().add(codeSystemVersion);
		}
	}

	private String getReleaseShortName() {
		final String parentBranchPath = getParentBranchPath();
		final String[] paths = parentBranchPath.split("/");
		if (paths.length == 1 && paths[0].equalsIgnoreCase("MAIN")) {
			return SnomedInternationalCodeSystemFactory.SHORT_NAME;
		} else if (paths.length > 1) {
			return paths[paths.length - 1];
		} else {
			throw new IllegalStateException(String.format("Couldn't extract short name from %s.", parentBranchPath));
		}
	}
	
	@Override
	protected void postProcess() {
		LOGGER.info("Adjusting effective time changes on module dependency...");
		adjustNewModuleDependencyRefSetMembers();
		LOGGER.info("Effective time adjustment successfully finished on module dependency.");
	}
	
	@Override
	public void postCommit() {
		if (!componentIdsToPublish.isEmpty()) {
			snomedIdentifiers.publish(componentIdsToPublish);
		}
		super.postCommit();
	}
	
	/**Returns {@code true} if the given {@link EClass} represents a SNOMED&nbsp;CT reference set member. Otherwise {@code false}.*/
	private boolean isRefSetMember(final EClass eClass) {
		return SnomedRefSetPackage.eINSTANCE.getSnomedRefSetMember().isSuperTypeOf(eClass);
	}

	/**Returns {@code true} if the given {@link EClass} represents a SNOMED&nbsp;CT core component.*/
	private boolean isCoreComponent(final EClass eClass) {
		return SnomedPackage.eINSTANCE.getComponent().isSuperTypeOf(eClass);
	}

	/**Updates all new module dependency reference set members.*/
	private void adjustNewModuleDependencyRefSetMembers() {
{'fraction_non_alphanumeric': 0.0868107302533532, 'fraction_numerical': 0.004524164360229934, 'mean_word_length': 6.533680834001604, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 42, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '686627', 'n_tokens_mistral': 5703, 'n_tokens_neox': 5339, 'n_words': 1117}