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

org.eclipse.esmf.aspectmodel.versionupdate.MetaModelVersionMigrator Maven / Gradle / Ivy

There is a newer version: 2.9.5
Show newest version
/*
 * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH
 *
 * See the AUTHORS file(s) distributed with this work for additional
 * information regarding authorship.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 *
 * SPDX-License-Identifier: MPL-2.0
 */
package org.eclipse.esmf.aspectmodel.versionupdate;

import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;

import org.eclipse.esmf.aspectmodel.AspectModelFile;
import org.eclipse.esmf.aspectmodel.VersionNumber;
import org.eclipse.esmf.aspectmodel.resolver.ModelResolutionException;
import org.eclipse.esmf.aspectmodel.resolver.exceptions.InvalidVersionException;
import org.eclipse.esmf.aspectmodel.resolver.modelfile.RawAspectModelFile;
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
import org.eclipse.esmf.aspectmodel.urn.ElementType;
import org.eclipse.esmf.samm.KnownVersion;

import com.google.common.collect.ImmutableList;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The service that migrates all migrators in the correct order.
 */
public class MetaModelVersionMigrator implements UnaryOperator {
   public static final MetaModelVersionMigrator INSTANCE = new MetaModelVersionMigrator();
   private static final Logger LOG = LoggerFactory.getLogger( MetaModelVersionMigrator.class );

   private MetaModelVersionMigrator() {
   }

   private final List migrators = ImmutableList. builder()
         .add( new SammMetaModelVersionUriRewriter( KnownVersion.SAMM_2_0_0, KnownVersion.SAMM_2_1_0 ) )
         .add( new SammMetaModelVersionUriRewriter( KnownVersion.SAMM_1_0_0, KnownVersion.SAMM_2_0_0 ) )
         .add( new SammRemoveSammNameMigrator( KnownVersion.SAMM_1_0_0, KnownVersion.SAMM_2_0_0 ) )
         .add( new UnitInSammNamespaceMigrator() )
         .build();

   private Model execute( final Migrator migrator, final Model sourceModel ) {
      LOG.info( "Start Migration for {} to {}", migrator.sourceVersion(), migrator.targetVersion() );
      final String description = migrator.getDescription().orElse( "" );
      LOG.info( "Migration step {} {}", migrator.getClass().getSimpleName(), description );
      final Model targetModel = migrator.migrate( sourceModel );
      LOG.info( "End Migration" );
      return targetModel;
   }

   private Model convertBammToSamm( final Model model ) {
      final BammUriRewriter bamm100UriRewriter = new BammUriRewriter( BammUriRewriter.BammVersion.BAMM_1_0_0 );
      final BammUriRewriter bamm200UriRewriter = new BammUriRewriter( BammUriRewriter.BammVersion.BAMM_2_0_0 );
      return bamm200UriRewriter.migrate( bamm100UriRewriter.migrate( model ) );
   }

   /**
    * Returns the meta model version used in the model
    *
    * @param model an Aspect model file
    * @return the meta model versions
    */
   private VersionNumber getUsedMetaModelVersion( final Model model ) {
      final String sammUrnStart = String.format( "%s:%s", AspectModelUrn.VALID_PROTOCOL, AspectModelUrn.VALID_NAMESPACE_IDENTIFIER );
      final Set result = model.listObjects()
            .toList()
            .stream()
            .filter( RDFNode::isURIResource )
            .map( RDFNode::asResource )
            .map( Resource::getURI )
            .filter( uri -> uri.startsWith( sammUrnStart ) )
            .flatMap( uri -> AspectModelUrn.from( uri ).toJavaStream() )
            .filter( urn -> (urn.getElementType().equals( ElementType.META_MODEL ) || urn.getElementType()
                  .equals( ElementType.CHARACTERISTIC )) )
            .map( AspectModelUrn::getVersion )
            .map( VersionNumber::parse )
            .collect( Collectors.toSet() );
      if ( result.size() == 1 ) {
         return result.iterator().next();
      } else if ( result.size() > 1 ) {
         throw new ModelResolutionException( "Aspect Model refers more than one SAMM version" );
      } else {
         // Model does not contain any elements. Default to the latest meta model version.
         return VersionNumber.parse( KnownVersion.getLatest().toVersionString() );
      }
   }

   /**
    * Semantically migrates an Aspect model file from its current meta model version to a given target meta model version.
    * This is done by composing the {@link Migrator}s that update from one version to the next into one function
    * which is then applied to the given source model.
    *
    * @param modelFile the source model file
    * @return the resulting {@link AspectModelFile} that corresponds to the input Aspect model file, but with the new meta model version
    */
   //   public AspectModelFile updateMetaModelVersion( final AspectModelFile modelFile ) {
   @Override
   public AspectModelFile apply( final AspectModelFile modelFile ) {
      // Before any semantic migration, perform the mechanical translation of legacy BAMM models
      final Model input = convertBammToSamm( modelFile.sourceModel() );

      final VersionNumber latestKnownVersion = VersionNumber.parse( KnownVersion.getLatest().toVersionString() );
      final VersionNumber sourceVersion = getUsedMetaModelVersion( input );
      Model migrationModel = modelFile.sourceModel();

      if ( sourceVersion.greaterThan( latestKnownVersion ) ) {
         // looks like unreachable
         throw new InvalidVersionException(
               String.format( "Model version %s can not be updated to version %s", sourceVersion, latestKnownVersion ) );
      }

      if ( !sourceVersion.equals( latestKnownVersion ) ) {
         migrationModel = migrate( migrators, sourceVersion, latestKnownVersion, migrationModel );
      }

      return new RawAspectModelFile( migrationModel, modelFile.headerComment(), modelFile.sourceLocation() );
   }

   private Model migrate( final List migrators, final VersionNumber sourceVersion, final VersionNumber targetVersion,
         final Model targetModel ) {
      if ( migrators.isEmpty() ) {
         return targetModel;
      }

      final Comparator comparator = Comparator.comparing( Migrator::sourceVersion );
      final List migratorSet = migrators.stream()
            .sorted( comparator.thenComparing( Migrator::order ) )
            .dropWhile( migrator -> !migrator.sourceVersion().equals( sourceVersion ) )
            .takeWhile( migrator -> !migrator.targetVersion().greaterThan( targetVersion ) )
            .toList();

      Model migratorTargetModel = targetModel;
      for ( final Migrator migrator : migratorSet ) {
         migratorTargetModel = execute( migrator, migratorTargetModel );
      }
      return migratorTargetModel;
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy