public abstract class AbstractSavingStrategy {
    private static final Logger logger = LogManager.getLogger(AbstractSavingStrategy.class.getName());
    protected static final char SLASH = File.separatorChar;
    private final boolean saveInProject;

    /**
     * Basic constructor. Takes the name of the project.
     * @param saveInProject determines whether the folder where the file is saved should be refreshed in the Eclipse
     * IDE. Set this true if the file is saved in a project in the IDE.
     */
    public AbstractSavingStrategy(boolean saveInProject) {
        this.saveInProject = saveInProject;
    }

    /**
     * Returns the name of the saving strategy. For example "FooBar" for a strategy called "FooBarSaving".
     * @return the name.
     */
    public String getName() {
        return getClass().getSimpleName().replace("Saving", "");
    }

    /**
     * Saves an {@link EPackage} as an Ecore file. The method calls the methods filePath() and fileName() to get the
     * information it needs to save the metamodel. If the default saving behavior is not wanted, this method has to be
     * overridden in the strategy class that overrides this class.
     * @param ePackage is the EPackage to save.
     * @param projectName is the name of the project the EPAckage was generated from.
     * @return the saving information.
     */
    public SavingInformation save(EPackage ePackage, String projectName) {
        beforeSaving(projectName);
        ePackage.eClass(); // Initialize the EPackage:
        Resource.Factory.Registry registry = Resource.Factory.Registry.INSTANCE;
        Map<String, Object> map = registry.getExtensionToFactoryMap();
        map.put(EcorePackage.eNAME, new XMIResourceFactoryImpl());  // add default extension
        ResourceSet resourceSet = new ResourceSetImpl(); // get new resource set
        Resource resource = null; // create a resource:
        String fileName = getFileName(); // get name
        String filePath = getFilePath(); // get path
        try {
            resource = resourceSet.createResource(URI.createFileURI(filePath + fileName + ".ecore"));
        } catch (IllegalArgumentException exception) {
            logger.error("Error while saving the metamodel.", exception);
        }
        resource.getContents().add(ePackage); // add the EPackage as root.
        try { // save the content:
            resource.save(Collections.EMPTY_MAP);
        } catch (IOException exception) {
            logger.error("Error while saving the metamodel.", exception);
        }
        if (saveInProject) {
            refreshFolder(getFilePath());
        }
        logger.info("The extracted metamodel was saved under: " + getFilePath());
        return new SavingInformation(filePath, fileName);
    }

    /**
     * Refreshes a specific folder in the Eclipse IDE
     * @param folderPath is the path of the folder.
     */
    private void refreshFolder(String folderPath) {
        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
        IContainer folder = root.getContainerForLocation(new Path(folderPath));
        try {
            folder.refreshLocal(IResource.DEPTH_INFINITE, null);
        } catch (CoreException exception) {
            logger.warn("Could not refresh output folder. Try that manually.", exception);
        }
    }

    /**
     * Can be used to prepare the saving itself.
     * @param projectName is the name of the project where the metamodel was extracted.
     */
    protected abstract void beforeSaving(String projectName);

    /**
     * Returns the project suffix, which basically is a separator character and a suffix base, depending on the naming
     * type of the projects name (e.g. "My-Project" and "model" will return "-Model").
     * @param projectName is the projects name. Determines which separator is used and decides whether there is
     * capitalization.
     * @param suffixBase is the base string of the suffix.
     * @return the project suffix.
     */
    protected String createSuffix(String projectName, String suffixBase) {
        char[] candidates = { ' ', '.', '-', '_', ':' }; // possible separators
        char separator = Character.MIN_VALUE; // 0000
        int mostFrequent = 0;
        String base = suffixBase;
        for (char candidate : candidates) { // for every candidate
            int occurrences = 0; // count occurrences in project name:
            for (int i = 0; i < projectName.length(); i++) {
                occurrences = (candidate == projectName.charAt(i)) ? (occurrences + 1) : occurrences;
            }
            if (occurrences > mostFrequent) { // if candidate is new most used candidate
                mostFrequent = occurrences; // set as new preferred separator
                separator = candidate;
            }
        }
        if (!projectName.matches(".*[A-Z].*")) { // if has no upper case
            base = base.toLowerCase(); // use lower case suffix
        }
        if (separator == Character.MIN_VALUE) { // no separator was chosen
            return base; // return suffix without separator
        }
        return separator + base; // identifier = separator + suffix
    }

    /**
     * Determines the name of the ecore file.
     * @return the file name.
     */
    protected abstract String getFileName();

    /**
     * Determines the path where the ecore file is saved.
     * @return the file path.
     */
    protected abstract String getFilePath();

    /**
     * Check whether the output project exists.
     * @param name is the name of the project.
     * @return true if it exists.
     */
    protected boolean projectExists(String name) {
        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
        for (IProject iProject : root.getProjects()) {
            if (iProject.getName().equals(name)) {
                return true;
            }
        }
        return false;
    }
} public class CopyProjectSaving extends OriginalProjectSaving {
    private static final Logger logger = LogManager.getLogger(CopyProjectSaving.class.getName());
    private IProject projectCopy;
    private final String projectSuffix;

    /**
     * Basic constructor.
     * @param projectSuffix is the suffix of the name of the copied project.
     */
    public CopyProjectSaving(String projectSuffix) {
        super(); // refresh folder.
        this.projectSuffix = projectSuffix;
    }

    /**
     * Copies an specific {@link IProject}.
     * @param project is the specific {@link IProject} to copy to.
     * @return the copy of the original {@link IProject}.
     */
    private IProject copy(IProject project) {
        IProject copy = null;
        try {
            IPath newPath = createPath(project);
            project.copy(newPath, false, new NullProgressMonitor());
            IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
            copy = workspaceRoot.getProject(newPath.toString());
        } catch (CoreException exception) {
            logger.fatal(exception);
        }
        return copy;
    }

    /**
     * Creates the {@link IPath} of the copy.
     */
    private IPath createPath(IProject project) {
        String suffix = createSuffix(project.getName(), projectSuffix);
        int version = 2;
        String versionString = "";
        while (projectExists(project.getName() + suffix + versionString)) { // to avoid duplicates:
            versionString = Integer.toString(version);
            version++;
        }
        return new Path(project.getFullPath() + suffix + versionString);
    }

    /**
     * Gets {@link IProject} from name.
     */
    private IProject getProject(String projectName) {
        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
        for (IProject project : root.getProjects()) {
            if (project.getName().equals(projectName)) {
                return project;
            }
        }
        return null;
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#beforeSaving(java.lang.String)
     */
    @Override
    protected void beforeSaving(String projectName) {
        super.projectName = projectName;
        IProject project = getProject(projectName);
        projectCopy = copy(project);
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#filePath()
     */
    @Override
    protected String getFilePath() {
        IWorkspace workspace = ResourcesPlugin.getWorkspace();
        return workspace.getRoot().getLocation().toFile().getPath() + SLASH + projectCopy.getName() + SLASH + "model" + SLASH;
    }
} public class CustomPathSaving extends AbstractSavingStrategy {
    private String name;
    private String path;

    /**
     * Basic constructor.
     */
    public CustomPathSaving() {
        super(false); // don't refresh folder
    }

    /**
     * Opens a saving dialog and sets the path and the name.
     */
    @Override
    protected void beforeSaving(String projectName) {
        Display display = Display.getCurrent();
        Shell shell = new Shell(display);
        shell.open();
        String result = null;
        while (result == null) {
            FileDialog dialog = new FileDialog(shell, SWT.SAVE);
            dialog.setFilterNames(new String[] { "Ecore File" });
            dialog.setFilterExtensions(new String[] { "*.ecore" });
            dialog.setFileName(projectName + ".ecore");
            result = dialog.open(); // calculate result:
        }
        int index = result.lastIndexOf(SLASH) + 1;
        path = result.substring(0, index);
        name = result.substring(index, result.lastIndexOf('.'));
        shell.close();
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#fileName()
     */
    @Override
    protected String getFileName() {
        return name;
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#filePath()
     */
    @Override
    protected String getFilePath() {
        return path;
    }
} public class ExistingProjectSaving extends AbstractSavingStrategy {
    private final DateTimeFormatter formatter;
    private final String outputProjectName;
    private String projectName;

    /**
     * Basic constructor.
     * @param outputProjectName is the name of the Eclipse project where models will be saved in.
     */
    public ExistingProjectSaving(String outputProjectName) {
        super(true);
        this.outputProjectName = outputProjectName;
        formatter = DateTimeFormatter.ofPattern("-yyyy-MM-dd-HH:mm.ss"); // date time format
        if (!projectExists(outputProjectName)) {
            throw new IllegalArgumentException("The specified output project could not be found: " + outputProjectName);
        }
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#beforeSaving(java.lang.String)
     */
    @Override
    protected void beforeSaving(String projectName) {
        this.projectName = projectName;
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#fileName()
     */
    @Override
    protected String getFileName() {
        return projectName + LocalDateTime.now().format(formatter);
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#filePath()
     */
    @Override
    protected String getFilePath() {
        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
        return root.getLocation().toFile().getPath() + SLASH + outputProjectName + SLASH + "model" + SLASH;
    }
} public class NewProjectSaving extends AbstractSavingStrategy {
    private String fileName;
    private String generatedProjectName;

    /**
     * Basic constructor.
     */
    public NewProjectSaving() {
        super(true);
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#beforeSaving()
     */
    @Override
    protected void beforeSaving(String projectName) {
        fileName = projectName;
        IProject newProject = EMFProjectGenerator.createProject(projectName + createSuffix(projectName, "Model"));
        this.generatedProjectName = newProject.getName();
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#fileName()
     */
    @Override
    protected String getFileName() {
        return fileName;
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#filePath()
     */
    @Override
    protected String getFilePath() {
        IWorkspace workspace = ResourcesPlugin.getWorkspace();
        return workspace.getRoot().getLocation().toFile().getPath() + SLASH + generatedProjectName + SLASH + "model" + SLASH;
    }
} public class OriginalProjectSaving extends AbstractSavingStrategy {
    protected String projectName;

    /**
     * Basic constructor.
     */
    public OriginalProjectSaving() {
        super(true); // refresh folder.
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#beforeSaving()
     */
    @Override
    protected void beforeSaving(String projectName) {
        this.projectName = projectName;
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#fileName()
     */
    @Override
    protected String getFileName() {
        return projectName;
    }

    /*
     * @see eme.generator.saving.AbstractSavingStrategy#filePath()
     */
    @Override
    protected String getFilePath() {
        IWorkspace workspace = ResourcesPlugin.getWorkspace();
        return workspace.getRoot().getLocation().toFile().getPath() + SLASH + projectName + SLASH + "model" + SLASH;
    }
} public class SavingInformation {
    private final String fileName;
    private final String filePath;
    private final String projectName;

    /**
     * Basic constructor that sets the information.
     * @param filePath is the path where the Ecore file was saved.
     * @param fileName is the name of file where the Ecore metamodel was saved in.
     */
    public SavingInformation(String filePath, String fileName) {
        this.filePath = filePath;
        this.fileName = fileName;
        String[] segments = filePath.split(Pattern.quote(File.separator));
        projectName = segments[segments.length - 2];
    }

    /**
     * Accessor for the file name.
     * @return the name of file where the Ecore metamodel was saved in.
     */
    public String getFileName() {
        return fileName;
    }

    /**
     * Accessor for the file path.
     * @return the file path where the Ecore file was saved.
     */
    public String getFilePath() {
        return filePath;
    }

    /**
     * Accessor for the project name.
     * @return the the name of the saved project.
     */
    public String getProjectName() {
        return projectName;
    }
} public class ExtractedPackage extends ExtractedElement {
    private final List<ExtractedClass> classes;
    private final List<ExtractedEnum> enumerations;
    private final List<ExtractedInterface> interfaces;
    private final List<ExtractedPackage> subpackages;
    protected boolean root;

    /**
     * Creates an extracted package.
     * @param fullName is the full name of the package.
     */
    public ExtractedPackage(String fullName) {
        super(fullName);
        subpackages = new LinkedList<ExtractedPackage>();
        classes = new LinkedList<ExtractedClass>();
        interfaces = new LinkedList<ExtractedInterface>();
        enumerations = new LinkedList<ExtractedEnum>();
        root = false;
    }

    /**
     * Adds a new subpackage to the package.
     * @param subpackage is the new subpackage of the package.
     */
    public void add(ExtractedPackage subpackage) {
        subpackages.add(subpackage);
    }

    /**
     * Adds a new {@link ExtractedType} to the package.
     * @param type is the new {@link ExtractedType} of the package.
     */
    public void add(ExtractedType type) {
        if (type.getClass() == ExtractedClass.class) {
            classes.add((ExtractedClass) type);
        } else if (type.getClass() == ExtractedInterface.class) {
            interfaces.add((ExtractedInterface) type);
        } else if (type.getClass() == ExtractedEnum.class) {
            enumerations.add((ExtractedEnum) type);
        }
    }

    /**
     * accessor for the {@link ExtractedClass}es.
     * @return the classes.
     */
    public List<ExtractedClass> getClasses() {
        return classes;
    }

    /**
     * accessor for the {@link ExtractedEnum}s.
     * @return the enumerations.
     */
    public List<ExtractedEnum> getEnumerations() {
        return enumerations;
    }

    /**
     * accessor for the {@link ExtractedInterface}s.
     * @return the interfaces.
     */
    public List<ExtractedInterface> getInterfaces() {
        return interfaces;
    }

    /**
     * accessor for the subpackages.
     * @return the subpackages.
     */
    public List<ExtractedPackage> getSubpackages() {
        return subpackages;
    }

    /**
     * accessor for the {@link ExtractedType}s (interfaces, classes and enumerations).
     * @return the types.
     */
    public List<ExtractedType> getTypes() {
        List<ExtractedType> types = new LinkedList<ExtractedType>(enumerations);
        types.addAll(classes);
        types.addAll(interfaces);
        return types;
    }

    /**
     * Checks whether package is empty.
     * @return true if the package is empty.
     */
    public boolean isEmpty() {
        for (ExtractedPackage subpackage : subpackages) {
            if (!subpackage.isEmpty()) {
                return false;
            }
        }
        return getTypes().isEmpty();
    }

    /**
     * Checks whether package is the root package.
     * @return true if the package is the root package.
     */
    public boolean isRoot() {
        return root;
    }

    /**
     * Sets the package as root package, marking it as default package by changing its name.
     */
    public void setAsRoot() {
        root = true;
    }

    /**
     * Sorts the content of the package. Sorts its types, its subpackages and all the content of every subpackage.
     */
    public void sort() {
        Collections.sort(interfaces);
        Collections.sort(classes);
        Collections.sort(enumerations);
        Collections.sort(subpackages);
        for (ExtractedPackage subpackage : subpackages) {
            subpackage.sort(); // sort the content of all subpackages.
        }
    }

    @Override
    public void setSelected(boolean selected) {
        if (!root) { // root cannot be deselected.
            super.setSelected(selected);
        }
        getTypes().forEach(type -> type.setSelected(selected));
        getSubpackages().forEach(subpackage -> subpackage.setSelected(selected));
    }

    @Override
    public String toString() {
        if ("".equals(name)) {
            return getClass().getSimpleName() + "(default package)";
        }
        return super.toString();
    }
} public class IntermediateModel {
    private static final Logger logger = LogManager.getLogger(IntermediateModel.class.getName());
    private final Set<ExtractedType> externalTypes;
    private final Set<ExtractedPackage> packages;
    private final String projectName;
    private ExtractedPackage rootPackage;
    private final Set<ExtractedType> types;

    /**
     * Basic constructor.
     * @param projectName is the name of the project the model was extracted from.
     */
    public IntermediateModel(String projectName) {
        packages = new LinkedHashSet<ExtractedPackage>();
        types = new LinkedHashSet<ExtractedType>();
        externalTypes = new LinkedHashSet<ExtractedType>();
        this.projectName = projectName;
    }

    /**
     * Adds a new {@link ExtractedPackage} to the intermediate model if it is not
     * already added.
     * @param newPackage is the new {@link ExtractedPackage} to add.
     */
    public void add(ExtractedPackage newPackage) {
        if (packages.add(newPackage)) {
            if (rootPackage == null) { // if it is the first package
                rootPackage = newPackage; // add as root
                newPackage.setAsRoot(); // mark as root
            } else {
                getPackage(newPackage.getParentName()).add(newPackage);
            }
        }
    }

    /**
     * Adds a new {@link ExtractedType} to the intermediate model if it is not
     * already added. Finds parent {@link ExtractedPackage} automatically.
     * @param type is the new {@link ExtractedType} to add.
     */
    public void add(ExtractedType type) {
        addTo(type, getPackage(type.getParentName()));
    }

    /**
     * Adds a new external {@link ExtractedType} to the intermediate model.
     * @param type is the new external type to add.
     */
    public void addExternal(ExtractedType type) {
        externalTypes.add(type);
    }

    /**
     * Adds a new {@link ExtractedType} to the intermediate model and to a specific
     * parent {@link ExtractedPackage} if it is not already added.
     * @param type is the new {@link ExtractedType} to add.
     * @param parent is the parent {@link ExtractedPackage}.
     */
    public void addTo(ExtractedType type, ExtractedPackage parent) {
        if (!type.getParentName().contains(parent.getFullName())) {
            throw new IllegalArgumentException("Invalid parent declaration in " + type + " to " + parent);
        } else if (types.add(type)) { // add class to list of classes.
            parent.add(type);
        }
    }

    /**
     * Checks whether the model contains an {@link ExtractedType} whose full name
     * matches a given full name.
     * @param fullName is the given full name.
     * @return true if it contains the {@link ExtractedType}, false if not.
     */
    public boolean contains(String fullName) {
        return getType(fullName) != null;
    }

    /**
     * Checks whether the model contains an external {@link ExtractedType} whose
     * full name matches a given full name.
     * @param fullName is the given full name.
     * @return true if it contains the external {@link ExtractedType}, false if not.
     */
    public boolean containsExternal(String fullName) {
        return getExternalType(fullName) != null;
    }

    /**
     * Returns the external {@link ExtractedType} of the intermediate model whose
     * full name matches the given full name.
     * @param fullName is the given full name.
     * @return the external {@link ExtractedType} with the matching name or null if
     * the name is not found.
     */
    public ExtractedType getExternalType(String fullName) {
        return getTypeFrom(fullName, externalTypes);
    }

    /**
     * Returns all the external {@link ExtractedType}s of the intermediate model.
     * @return the set of external {@link ExtractedType}s.
     */
    public Set<ExtractedType> getExternalTypes() {
        return externalTypes;
    }

    /**
     * Returns the {@link ExtractedPackage} of the intermediate model whose full
     * name matches the given full name.
     * @param fullName is the given full name.
     * @return the {@link ExtractedPackage} with the matching name.
     * @throws RuntimeException if the {@link ExtractedPackage} is not found. This
     * means this method cannot be used to check whether there is a certain package
     * in the model. It is explicitly used to find an existing package.
     */
    public ExtractedPackage getPackage(String fullName) {
        for (ExtractedPackage aPackage : packages) { // for all packages
            if (aPackage.getFullName().equals(fullName)) { // if parent
                return aPackage; // can only have on parent
            }
        }
        throw new IllegalArgumentException("Could not find package " + fullName);
    }

    /**
     * accessor for the name of the project.
     * @return the name.
     */
    public String getProjectName() {
        return projectName;
    }

    /**
     * accessor for the root {@link ExtractedPackage} of the model.
     * @return the root package.
     */
    public ExtractedPackage getRoot() {
        return rootPackage;
    }

    /**
     * Returns the {@link ExtractedType} of the intermediate model whose full name
     * matches the given full name.
     * @param fullName is the given full name.
     * @return the {@link ExtractedType} with the matching name or null if the name
     * is not found.
     */
    public ExtractedType getType(String fullName) {
        return getTypeFrom(fullName, types);
    }

    /**
     * Checks whether the model contains a selected {@link ExtractedType} whose full
     * name matches a given full name.
     * @param fullName is the given full name.
     * @return true if it contains the {@link ExtractedType} and it is selected,
     * false if it is not selected or the model does not contain such type.
     */
    public boolean isTypeSelected(String fullName) {
        return contains(fullName) && getType(fullName).isSelected();
    }

    /**
     * Prints the model.
     */
    public void print() {
        logger.info(toString());
        logger.info("   with packages " + packages.toString());
        logger.info("   with types " + types.toString());
        logger.info("   with external types " + externalTypes.toString());
    }

    /**
     * Sorts the content of the root {@link ExtractedPackage}. Sorts its types, its
     * subpackages and all the content of every subpackage.
     */
    public void sort() {
        rootPackage.sort();
    }

    @Override
    public String toString() {
        return "IntermediateModel of " + projectName + ": [Packages=" + packages.size() + ", Types=" + types.size() + ", ExternalTypes="
                + externalTypes.size() + "]";
    }

    /**
     * Finds {@link ExtractedType} from set of {@link ExtractedType}s by its full
     * name.
     * @param fullName is full name.
     * @param typeSet is the set of {@link ExtractedType}s.
     * @return the {@link ExtractedType} with the matching name or null if the name
     * is not found.
     */
    private ExtractedType getTypeFrom(String fullName, Set<ExtractedType> typeSet) {
        for (ExtractedType type : typeSet) { // for all packages
            if (type.getFullName().equals(fullName)) { // if parent
                return type; // can only have on parent
            }
        }
        return null;
    }
} public class ExtractionProperties extends AbstractProperties<TextProperty, BinaryProperty> {
    /**
     * Basic constructor, sets the file name, file description and symbolic bundle name.
     */
    public ExtractionProperties() {
        super("user.properties", "Use this file to configure the Ecore metamodel extraction.", "EcoreMetamodelExtraction");
    }
} public enum TextProperty implements ITextProperty {
    DATATYPE_PACKAGE("DataTypePackageName", "DATATYPES"),
    DEFAULT_PACKAGE("DefaultPackageName", "DEFAULT"),
    DUMMY_NAME("DummyClassName", "DUMMY"),
    ROOT_NAME("RootContainerName", "ROOT"),
    NESTED_TYPE_PACKAGE("NestedTypePackageSuffix", "InnerTypes"),
    PROJECT_SUFFIX("ProjectSuffix", "Model"),
    SAVING_STRATEGY("SavingStrategy", "NewProject");

    private final String defaultValue;
    private final String key;

    /**
     * Private constructor for enum values with key and default value of an extraction property.
     * @param key is the key of the property.
     * @param defaultValue is the default value of the property.
     */
    TextProperty(String key, String defaultValue) {
        this.key = key;
        this.defaultValue = defaultValue;
    }

    @Override
    public String getDefaultValue() {
        return defaultValue;
    }

    @Override
    public String getKey() {
        return key;
    }
}
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.eclipse.emf.ecore.EPackage;
import eme.generator.saving.AbstractSavingStrategy;
import eme.generator.saving.CopyProjectSaving;
import eme.generator.saving.CustomPathSaving;
import eme.generator.saving.ExistingProjectSaving;
import eme.generator.saving.NewProjectSaving;
import eme.generator.saving.OriginalProjectSaving;
import eme.generator.saving.SavingInformation;
import eme.model.ExtractedPackage;
import eme.model.IntermediateModel;
import eme.properties.ExtractionProperties;
import eme.properties.TextProperty;
package eme.generator;



/**
 * This class generates an Ecore Metamodel from an {@link IntermediateModel}. It also allows to save a generated
 * metamodel as an Ecore file using a specific saving strategy.
 * @author Timur Saglam
 */
public class EcoreMetamodelGenerator {
    private static final Logger logger = LogManager.getLogger(EcoreMetamodelGenerator.class.getName());
    private static final String OUTPUT_PROJECT = "EME-Generator-Output";
    private final EPackageGenerator ePackageGenerator;
    private GeneratedEcoreMetamodel metamodel;
    private String projectName;
    private final ExtractionProperties properties;
    private AbstractSavingStrategy savingStrategy;

    /**
     * Basic constructor.
     * @param properties is the ExtractionProperties class for the exraction.
     */
    public EcoreMetamodelGenerator(ExtractionProperties properties) {
        this.properties = properties;
        ePackageGenerator = new EPackageGenerator(properties); // build generators
    }

    /**
     * Changes the {@link AbstractSavingStrategy} to a new one.
     * @param strategyName is the name of the new saving strategy.
     */
    public final void changeSavingStrategy(String strategyName) { // Add custom strategies here
        if (isStrategy(ExistingProjectSaving.class, strategyName)) {
            savingStrategy = new ExistingProjectSaving(OUTPUT_PROJECT);
        } else if (isStrategy(OriginalProjectSaving.class, strategyName)) {
            savingStrategy = new OriginalProjectSaving();
        } else if (isStrategy(CustomPathSaving.class, strategyName)) {
            savingStrategy = new CustomPathSaving();
        } else if (isStrategy(CopyProjectSaving.class, strategyName)) {
            savingStrategy = new CopyProjectSaving(properties.get(TextProperty.PROJECT_SUFFIX));
        } else if (isStrategy(NewProjectSaving.class, strategyName)) {