/******************************************************************************* | |
* Copyright (c) 2008 The University of York. | |
* This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License 2.0 | |
* which is available at https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* Contributors: | |
* Dimitrios Kolovos - initial API and implementation | |
******************************************************************************/ | |
package org.eclipse.epsilon.emc.emf; | |
import java.io.File; | |
import java.io.IOException; | |
import java.util.*; | |
import java.util.stream.Collectors; | |
import java.util.stream.Stream; | |
import org.eclipse.emf.common.notify.Adapter; | |
import org.eclipse.emf.common.notify.Notification; | |
import org.eclipse.emf.common.util.EList; | |
import org.eclipse.emf.common.util.Enumerator; | |
import org.eclipse.emf.common.util.URI; | |
import org.eclipse.emf.ecore.EEnumLiteral; | |
import org.eclipse.emf.ecore.EObject; | |
import org.eclipse.emf.ecore.EPackage; | |
import org.eclipse.emf.ecore.EReference; | |
import org.eclipse.emf.ecore.EStructuralFeature; | |
import org.eclipse.emf.ecore.EcorePackage; | |
import org.eclipse.emf.ecore.resource.Resource; | |
import org.eclipse.emf.ecore.resource.ResourceSet; | |
import org.eclipse.emf.ecore.util.EContentAdapter; | |
import org.eclipse.emf.ecore.util.EcoreUtil; | |
import org.eclipse.emf.ecore.xmi.XMLResource; | |
import org.eclipse.epsilon.common.util.CollectionUtil; | |
import org.eclipse.epsilon.common.util.StringProperties; | |
import org.eclipse.epsilon.eol.compile.m3.Metamodel; | |
import org.eclipse.epsilon.eol.exceptions.models.EolModelElementTypeNotFoundException; | |
import org.eclipse.epsilon.eol.exceptions.models.EolModelLoadingException; | |
import org.eclipse.epsilon.eol.exceptions.models.EolNotAnEnumerationValueException; | |
import org.eclipse.epsilon.eol.execute.introspection.IReflectivePropertySetter; | |
import org.eclipse.epsilon.eol.models.IReflectiveModel; | |
import org.eclipse.epsilon.eol.models.IRelativePathResolver; | |
public class EmfModel extends AbstractEmfModel implements IReflectiveModel { | |
/** | |
* @deprecated {@link #PROPERTY_METAMODEL_URI} and {@link #PROPERTY_FILE_BASED_METAMODEL_URI} are now | |
* interpreted as comma-separated lists of 0+ metamodel locations, and it is allowed to mix both types | |
* of metamodels now. This property is no longer used. | |
*/ | |
@Deprecated | |
public static final String PROPERTY_IS_METAMODEL_FILE_BASED = "isMetamodelFileBased"; | |
/** | |
* One of the keys used to construct the first argument to {@link EmfModel#load(StringProperties, String)}. | |
* | |
* This key is a comma-separated list of zero or more namespaces URI of some of the metamodels to which | |
* this model conforms. Users may combine this key with {@link #PROPERTY_FILE_BASED_METAMODEL_URI} to load | |
* both file-based and URI-based metamodels at the same time. | |
*/ | |
public static final String PROPERTY_METAMODEL_URI = "metamodelUri"; | |
/** | |
* One of the keys used to construct the first argument to {@link EmfModel#load(StringProperties, String)}. | |
* | |
* This key is a comma-separated list of zero or more {@link URI}s that can be used to locate some of the | |
* metamodels to which this model conforms. Users may combine this key with {@link #PROPERTY_METAMODEL_URI} | |
* to load both file-based and URI-based metamodels at the same time. | |
*/ | |
public static final String PROPERTY_FILE_BASED_METAMODEL_URI = "fileBasedMetamodelUri"; | |
/** | |
* @deprecated Replaced by | |
* {@link #PROPERTY_FILE_BASED_METAMODEL_URI}. | |
* This property will be removed in a future release of Epsilon. | |
*/ | |
@Deprecated | |
public static final String PROPERTY_METAMODEL_FILE = "metamodelFile"; | |
/** | |
* One of the keys used to construct the first argument to {@link EmfModel#load(StringProperties, String)}. | |
* | |
* This key is paired with a {@link URI} that can be used to locate this model. | |
* This key must always be paired with a value. | |
*/ | |
public static final String PROPERTY_MODEL_URI = "modelUri"; | |
/** | |
* @deprecated Replaced by | |
* {@link #PROPERTY_MODEL_URI}. | |
* This property will be removed in a future release of Epsilon. | |
*/ | |
@Deprecated | |
public static final String PROPERTY_MODEL_FILE = "modelFile"; | |
/** | |
* One of the keys used to construct the first argument to | |
* {@link EmfModel#load(StringProperties, String)}. | |
* | |
* This key is a Boolean value that if set to <code>true</code> (the | |
* default), tries to reuse previously registered file-based EPackages that | |
* have not been modified since the last time they were registered. | |
*/ | |
public static final String PROPERTY_REUSE_UNMODIFIED_FILE_BASED_METAMODELS = "reuseUnmodifiedFileBasedMetamodels"; | |
protected List<URI> metamodelUris = new ArrayList<>(); | |
protected List<EPackage> packages; | |
/** | |
* @deprecated | |
*/ | |
@Deprecated | |
protected boolean isMetamodelFileBased = false; | |
protected URI modelUri; | |
protected List<URI> metamodelFileUris = new ArrayList<>(); | |
protected boolean useExtendedMetadata = false; | |
protected boolean reuseUnmodifiedFileBasedMetamodels = true; | |
protected static Map<String, List<EPackage>> fileBasedMetamodels = new HashMap<>(); | |
protected static Map<String, Long> fileBasedMetamodelTimestamps = new HashMap<>(); | |
public EmfModel() { | |
propertySetter = new EmfPropertySetter(); | |
} | |
@Override | |
public IReflectivePropertySetter getPropertySetter() { | |
return (IReflectivePropertySetter) propertySetter; | |
} | |
@Override | |
public Collection<String> getPropertiesOf(String type) throws EolModelElementTypeNotFoundException { | |
Collection<EStructuralFeature> features = featuresForType(type); | |
final Collection<String> properties = new ArrayList<>(features.size()); | |
for (EStructuralFeature feature : features) { | |
properties.add(feature.getName()); | |
} | |
return properties; | |
} | |
@Override | |
public boolean preventLoadingOfExternalModelElements() { | |
if (isExpand()) { | |
setExpand(false); | |
return true; | |
} | |
else { | |
return false; | |
} | |
} | |
/** | |
* Load the model using the set of properties specified by the first argument. | |
* | |
* @see #PROPERTY_MODEL_URI | |
* @see #PROPERTY_IS_METAMODEL_FILE_BASED | |
* @see #PROPERTY_EXPAND | |
* @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=341481">Rationale for deprecating the FILE properties</a>. | |
*/ | |
@Override | |
public void load(StringProperties properties, IRelativePathResolver resolver) throws EolModelLoadingException { | |
PropertyMigrator.migrateDeprecatedProperties(properties); | |
super.load(properties, resolver); | |
this.modelUri = URI.createURI(properties.getProperty(PROPERTY_MODEL_URI)); | |
this.isMetamodelFileBased = properties.getBooleanProperty(PROPERTY_IS_METAMODEL_FILE_BASED, isMetamodelFileBased); | |
this.metamodelUris = toURIList(properties.getProperty(PROPERTY_METAMODEL_URI)); | |
setMetamodelFileUris(toURIList(properties.getProperty(PROPERTY_FILE_BASED_METAMODEL_URI))); | |
setReuseUnmodifiedFileBasedMetamodels(properties.getBooleanProperty(PROPERTY_REUSE_UNMODIFIED_FILE_BASED_METAMODELS, reuseUnmodifiedFileBasedMetamodels)); | |
load(); | |
} | |
@Override | |
protected void loadModel() throws EolModelLoadingException { | |
loadModelFromUri(); | |
setupContainmentChangeListeners(); | |
} | |
/** | |
* This listener is the one that keeps the cached .allInstances | |
* and model .allContents() lists up to date, instead of the usual | |
* createInstance/deleteInstance methods. | |
* | |
* This prevents the cache from becoming inconsistent if EcoreUtil | |
* or any other code outside Epsilon is used to create instances | |
* within the model. | |
*/ | |
protected class CachedContentsAdapter extends EContentAdapter { | |
@Override | |
public void notifyChanged(Notification notification) { | |
super.notifyChanged(notification); | |
Object notifier = notification.getNotifier(); | |
Object feature = notification.getFeature(); | |
if (notifier instanceof Resource && notification.getFeatureID(Resource.class) == Resource.RESOURCE__CONTENTS) { | |
handle(notification); | |
} | |
else if (feature instanceof EReference && ((EReference)feature).isContainment()) { | |
handle(notification); | |
} | |
} | |
protected void handle(Notification notification) { | |
try { | |
switch (notification.getEventType()) { | |
case Notification.UNSET: { | |
Object oldValue = notification.getOldValue(); | |
if (oldValue != Boolean.TRUE && oldValue != Boolean.FALSE) { | |
if (oldValue != null) { | |
forceRemoveFromCache((EObject) oldValue); | |
} | |
EObject newValue = (EObject) notification.getNewValue(); | |
if (newValue != null) { | |
forceAddToCache(newValue); | |
} | |
} | |
break; | |
} | |
case Notification.SET: { | |
EObject oldValue = (EObject) notification.getOldValue(); | |
if (oldValue != null) { | |
forceRemoveFromCache(oldValue); | |
} | |
EObject newValue = (EObject) notification.getNewValue(); | |
if (newValue != null) { | |
forceAddToCache(newValue); | |
} | |
break; | |
} | |
case Notification.ADD_MANY: { | |
@SuppressWarnings("unchecked") | |
Collection<EObject> newValues = (Collection<EObject>) notification.getNewValue(); | |
for (EObject newValue : newValues) { | |
forceAddToCache(newValue); | |
} | |
break; | |
} | |
case Notification.REMOVE_MANY: { | |
@SuppressWarnings("unchecked") | |
Collection<EObject> oldValues = (Collection<EObject>) notification.getOldValue(); | |
for (EObject oldContentValue : oldValues) { | |
forceRemoveFromCache(oldContentValue); | |
} | |
break; | |
} | |
case Notification.ADD: { | |
EObject added = (EObject) notification.getNewValue(); | |
forceAddToCache(added); | |
break; | |
} | |
case Notification.REMOVE: { | |
EObject removed = (EObject) notification.getOldValue(); | |
forceRemoveFromCache(removed); | |
break; | |
} | |
default: | |
// do nothing | |
break; | |
} | |
} catch (EolModelElementTypeNotFoundException ex) { | |
ex.printStackTrace(); | |
} | |
} | |
} | |
/** | |
* Used for backwards-compatibility with existing Eclipse launch configurations. | |
* | |
* See #341481 | |
*/ | |
static class PropertyMigrator { | |
public static void migrateDeprecatedProperties(StringProperties properties) { | |
migrateModelFileProperty(properties); | |
migrateMetamodelFileProperty(properties); | |
} | |
private static void migrateModelFileProperty(StringProperties properties) { | |
migrateUriValue(properties, PROPERTY_MODEL_FILE, PROPERTY_MODEL_URI); | |
} | |
private static void migrateMetamodelFileProperty(StringProperties properties) { | |
migrateUriValue(properties, PROPERTY_METAMODEL_FILE, PROPERTY_FILE_BASED_METAMODEL_URI); | |
} | |
private static void migrateUriValue(StringProperties properties, String oldProperty, String newProperty) { | |
if (properties.hasProperty(oldProperty) && !properties.hasProperty(newProperty)) { | |
final String oldValue = properties.getProperty(oldProperty); | |
final File oldFile = new File(oldValue); | |
if (oldFile.canRead() || oldFile.getParentFile() != null && oldFile.getParentFile().canRead()) { | |
// This is a regular path to a readable file (to be read) or in a readable directory (to be saved) | |
properties.put(newProperty, EmfUtil.createFileBasedURI(oldValue)); | |
} | |
else { | |
// It's not a regular file path: treat it as a platform:/resource/ URI | |
properties.put(newProperty, EmfUtil.createPlatformResourceURI(oldValue)); | |
} | |
} | |
} | |
} | |
@Override | |
protected void addToCache(String type, EObject instance) throws EolModelElementTypeNotFoundException { | |
// do nothing (we want to trigger changes through EMF adapters instead) | |
} | |
@Override | |
protected void removeFromCache(EObject instance) throws EolModelElementTypeNotFoundException { | |
// do nothing (we want to trigger changes through EMF adapters instead) | |
} | |
/** | |
* We want to use the overridden method, but not from | |
* {@link #createInstance(String)}, but rather from the adapter we set up to | |
* track additions and removals from the contents of a model. For this reason, | |
* we leave the overridden method empty and define this one that can be safely | |
* called from the adapter. | |
*/ | |
protected void forceAddToCache(EObject instance) throws EolModelElementTypeNotFoundException { | |
super.addToCache(getFullyQualifiedTypeNameOf(instance), instance); | |
for (EObject child : instance.eContents()) { | |
forceAddToCache(child); | |
} | |
} | |
/** @see #forceAddToCache(String, EObject) */ | |
protected void forceRemoveFromCache(EObject instance) throws EolModelElementTypeNotFoundException { | |
super.removeFromCache(instance); | |
for (EObject child : instance.eContents()) { | |
forceRemoveFromCache(child); | |
} | |
} | |
public void setupContainmentChangeListeners() { | |
// Add a notification adapter to all objects in the model | |
// so that they get moved when their containment changes | |
if (modelImpl != null) { | |
for (EObject eObject : allContents()) { | |
boolean isAdapted = false; | |
for (Adapter adapter : eObject.eAdapters()) { | |
if (adapter instanceof ContainmentChangeAdapter) { | |
isAdapted = true; | |
} | |
} | |
if (!isAdapted) { | |
eObject.eAdapters().add(new ContainmentChangeAdapter(eObject, eObject.eResource())); | |
} | |
} | |
} | |
} | |
protected ResourceSet createResourceSet() { | |
return new CachedResourceSet(); | |
} | |
public void loadModelFromUri() throws EolModelLoadingException { | |
ResourceSet resourceSet = createResourceSet(); | |
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("model", new DefaultXMIResource.Factory()); | |
// Check if global package registry contains the EcorePackage | |
if (EPackage.Registry.INSTANCE.getEPackage(EcorePackage.eNS_URI) == null) { | |
EPackage.Registry.INSTANCE.put(EcorePackage.eNS_URI, EcorePackage.eINSTANCE); | |
} | |
determinePackagesFrom(resourceSet); | |
// Note that AbstractEmfModel#getPackageRegistry() is not usable yet, as modelImpl is not set | |
for (EPackage ep : packages) { | |
String nsUri = ep.getNsURI(); | |
if (nsUri == null || nsUri.trim().length() == 0) { | |
nsUri = ep.getName(); | |
} | |
resourceSet.getPackageRegistry().put(nsUri, ep); | |
} | |
resourceSet.getPackageRegistry().put(EcorePackage.eNS_URI, EcorePackage.eINSTANCE); | |
Resource model = resourceSet.createResource(modelUri); | |
if (this.readOnLoad) { | |
try { | |
model.load(getResourceLoadOptions()); | |
if (expand) { | |
EcoreUtil.resolveAll(model); | |
} | |
} catch (IOException e) { | |
throw new EolModelLoadingException(e, this); | |
} | |
} | |
modelImpl = model; | |
if (isCachingEnabled()) { | |
modelImpl.eAdapters().add(new CachedContentsAdapter()); | |
} | |
} | |
/** | |
* @since 1.6 | |
*/ | |
@Override | |
public void setCachingEnabled(boolean cachingEnabled) { | |
boolean wasEnabled = isCachingEnabled(); | |
super.setCachingEnabled(cachingEnabled); | |
if (modelImpl != null) { | |
if (!wasEnabled && cachingEnabled && !hasAdapter(CachedContentsAdapter.class)) { | |
modelImpl.eAdapters().add(new CachedContentsAdapter()); | |
} | |
else if (wasEnabled && !cachingEnabled) { | |
modelImpl.eAdapters().removeIf(a -> a instanceof CachedContentsAdapter); | |
} | |
} | |
} | |
public List<String> getMetamodelFiles() { | |
final List<String> files = new ArrayList<>(metamodelFileUris.size()); | |
for (URI metamodelFileUri : this.metamodelFileUris) { | |
files.add(EmfUtil.getFile(metamodelFileUri)); | |
} | |
return files; | |
} | |
/** | |
* @deprecated This value is no longer used to load models: it is only | |
* kept for backwards compatibility, and it now simply indicates whether | |
* a file metamodel was loaded at all, or not. | |
*/ | |
@Deprecated | |
public boolean isMetamodelFileBased() { | |
return isMetamodelFileBased; | |
} | |
/** | |
* @deprecated This value is no longer honored anymore. Please populate the | |
* lists in {@link #getMetamodelUris()} (URI-based metamodels) | |
* and {@link #getMetamodelFileUris()} (file-based metamodels) | |
* appropriately instead. | |
*/ | |
@Deprecated | |
public void setMetamodelFileBased(boolean isMetamodelFileBased) { | |
this.isMetamodelFileBased = isMetamodelFileBased; | |
} | |
public List<String> getMetamodelUris() { | |
final List<String> uris = new ArrayList<>(metamodelUris.size()); | |
for (URI metamodelUri : this.metamodelUris) { | |
uris.add(metamodelUri.toString()); | |
} | |
return uris; | |
} | |
public String getModelFile() { | |
return EmfUtil.getFile(modelUri); | |
} | |
public URI getModelFileUri() { | |
return modelUri; | |
} | |
public void setModelFileUri(URI modelFileUri) { | |
this.modelUri = modelFileUri; | |
} | |
public List<URI> getMetamodelFileUris() { | |
return metamodelFileUris; | |
} | |
public void setMetamodelFileUris(List<URI> fileUris) { | |
this.metamodelFileUris = new ArrayList<>(fileUris); | |
} | |
public void setMetamodelFileUri(URI uri) { | |
metamodelFileUris = Arrays.asList(uri); | |
} | |
public void setMetamodelUris(List<String> uris) { | |
this.metamodelUris.clear(); | |
CollectionUtil.addCapacityIfArrayList(this.metamodelUris, uris.size()); | |
for (String sURI : uris) { | |
this.metamodelUris.add(URI.createURI(sURI)); | |
} | |
} | |
public void setMetamodelUri(String uri) { | |
metamodelUris = Arrays.asList(URI.createURI(uri)); | |
} | |
public void setMetamodelFiles(List<String> paths) { | |
this.metamodelFileUris.clear(); | |
CollectionUtil.addCapacityIfArrayList(this.metamodelFileUris, paths.size()); | |
for (String sPath : paths) { | |
this.metamodelFileUris.add(URI.createFileURI(sPath)); | |
} | |
} | |
public void setMetamodelFile(String path) { | |
setMetamodelFileUri(URI.createFileURI(path)); | |
} | |
public void setModelFile(String path) { | |
this.modelUri = URI.createFileURI(path); | |
} | |
public boolean isReuseUnmodifiedFileBasedMetamodels() { | |
return reuseUnmodifiedFileBasedMetamodels; | |
} | |
public void setReuseUnmodifiedFileBasedMetamodels(boolean reuseUnmodifiedFileBasedMetamodels) { | |
this.reuseUnmodifiedFileBasedMetamodels = reuseUnmodifiedFileBasedMetamodels; | |
} | |
@Override | |
public boolean hasProperty(String type, String property) throws EolModelElementTypeNotFoundException { | |
return getPropertiesOf(type).contains(property); | |
} | |
@Override | |
public boolean isEnumerationValue(Object object) { | |
return object instanceof Enumerator; | |
} | |
@Override | |
public String getEnumerationTypeOf(Object literal) throws EolNotAnEnumerationValueException { | |
if (!isEnumerationValue(literal)) | |
throw new EolNotAnEnumerationValueException(literal); | |
if (literal instanceof EEnumLiteral) { | |
return ((EEnumLiteral)literal).getEEnum().getName(); | |
} else { | |
return ((Enumerator)literal).getClass().getSimpleName(); | |
} | |
} | |
@Override | |
public String getEnumerationLabelOf(Object literal) throws EolNotAnEnumerationValueException { | |
if (!isEnumerationValue(literal)) | |
throw new EolNotAnEnumerationValueException(literal); | |
return ((Enumerator)literal).getName(); | |
} | |
@Override | |
public String toString() { | |
return "EmfModel [name=" + getName() + "]"; | |
} | |
protected void determinePackagesFrom(ResourceSet resourceSet) throws EolModelLoadingException { | |
packages = new ArrayList<>(); | |
for (URI metamodelFileUri : this.metamodelFileUris) { | |
List<EPackage> metamodelPackages = null; | |
try { | |
metamodelPackages = attemptFileBasedMetamodelReuse(metamodelFileUri); | |
if (metamodelPackages == null) { | |
metamodelPackages = EmfUtil.register(metamodelFileUri, resourceSet.getPackageRegistry(), false); | |
saveFileBasedMetamodelForReuse(metamodelFileUri, metamodelPackages); | |
} | |
} catch (Exception e) { | |
throw new EolModelLoadingException(e,this); | |
} | |
CollectionUtil.addCapacityIfArrayList(packages, metamodelPackages.size()); | |
for (EPackage metamodelPackage : metamodelPackages) { | |
packages.add(metamodelPackage); | |
EmfUtil.collectDependencies(metamodelPackage, packages); | |
} | |
} | |
CollectionUtil.addCapacityIfArrayList(packages, this.metamodelUris.size()); | |
for (URI metamodelUri : this.metamodelUris) { | |
EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(metamodelUri.toString()); | |
if (ePackage == null) { | |
throw new EolModelLoadingException(new IllegalArgumentException("Could not locate a metamodel with the URI '" + metamodelUri + "'. Please ensure that this metamodel has been registered with Epsilon."), this); | |
} | |
packages.add(ePackage); | |
EmfUtil.collectDependencies(ePackage, packages); | |
} | |
} | |
private List<EPackage> attemptFileBasedMetamodelReuse(URI uri) { | |
if (!reuseUnmodifiedFileBasedMetamodels || !uri.isFile()) { | |
// Reuse has been disabled, or the URI is not for a file: do nothing | |
return null; | |
} | |
final String path = uri.toFileString(); | |
final File metamodelFile = new File(path); | |
final Long lastTimestamp = fileBasedMetamodelTimestamps.get(path); | |
if (lastTimestamp == null || metamodelFile.lastModified() != lastTimestamp) { | |
// We don't have this URI in our cache yet, or the file | |
// has been modified since the last time we read it | |
return null; | |
} | |
return fileBasedMetamodels.get(path); | |
} | |
private static void saveFileBasedMetamodelForReuse(URI uri, List<EPackage> packages) { | |
// We always save the previously loaded metamodels, as we might want to force | |
// a reload first in one EmfModel and then reuse the metamodel in the next | |
// EmfModel. | |
if (!uri.isFile()) { | |
// The URI is not for a file: do nothing | |
return; | |
} | |
final String path = uri.toFileString(); | |
final File metamodelFile = new File(path); | |
final Long timestamp = metamodelFile.lastModified(); | |
fileBasedMetamodels.put(path, packages); | |
fileBasedMetamodelTimestamps.put(path, timestamp); | |
} | |
@Override | |
public boolean hasPackage(String packageName) { | |
return packageForName(packageName) != null; | |
} | |
private EPackage packageForName(String name) { | |
final String[] parts = name.split("::"); | |
int partIndex = 0; | |
EPackage current; | |
Collection<EPackage> next = getTopLevelPackages(); | |
do { | |
if ((current = packageForName(parts[partIndex++], next)) != null) { | |
next = current.getESubpackages(); | |
} | |
} | |
while (current != null && partIndex < parts.length); | |
return current; | |
} | |
private Collection<EPackage> getTopLevelPackages() { | |
return getPackageRegistry().values() | |
.stream() | |
.filter(pkg -> pkg instanceof EPackage) | |
.map(pkg -> (EPackage) pkg) | |
.collect(Collectors.toCollection(LinkedList::new)); | |
} | |
private static EPackage packageForName(String name, Collection<EPackage> packages) { | |
return packages.stream() | |
.filter(pkg -> name.equals(pkg.getName())) | |
.findAny() | |
.orElse(null); | |
} | |
private EList<EStructuralFeature> featuresForType(String type) throws EolModelElementTypeNotFoundException { | |
return classForName(type).getEAllStructuralFeatures(); | |
} | |
private static List<URI> toURIList(final String commaSeparatedList) { | |
return Stream.of(commaSeparatedList.trim().split("\\s*,\\s*")) | |
.filter(s -> !s.isEmpty()) | |
.map(URI::createURI) | |
.collect(Collectors.toList()); | |
} | |
@Override | |
public Metamodel getMetamodel(StringProperties properties, IRelativePathResolver resolver) { | |
return new EmfModelMetamodel(properties, resolver); | |
} | |
@Override | |
public boolean store() { | |
if (modelImpl == null) return false; | |
try { | |
Map<Object, Object> options = getResourceStoreOptions(); | |
if (!metamodelFileUris.isEmpty()) { | |
if (options == null) { | |
options = new HashMap<>(); | |
} | |
options.put(XMLResource.OPTION_SCHEMA_LOCATION, true); | |
} | |
modelImpl.save(options); | |
return true; | |
} | |
catch (IOException e) { | |
e.printStackTrace(); | |
return false; | |
} | |
} | |
} |