blob: d0459199fd80609f14892240b294363d072c98aa [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2017 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.pivot.internal.library.executor;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.evaluation.AbstractModelManager;
import org.omg.CORBA.Environment;
/**
* A self-populating map that lazily creates the extent of a class when asked
* for it.
* <p>
* <b>Note</b> that this implementation violates the contract of the
* {@link Map} API as follows:
* <ul>
* <li>the {@link Map#entrySet()} method does not return entries
* for any keys that have not already been queried via the
* {@link Map#get(java.lang.Object)} method</li>
* <li>concomitant to the above item, the {@link Map#keySet()}
* and {@link Map#values()} methods also are not complete</li>
* </ul>
* In practice, this does not matter because this map is only used for providing
* class extents to the OCL engine, and it only does look-up by key.
* Moreover, this isn't strictly a violation of any contract because there is
* no way to distinguish this behaviour from concurrent updates.
* </p>
* <p>
* See the {@link Environment} class for a description of the
* generic type parameters of this class.
* </p>
*/
public abstract class LazyModelManager extends AbstractModelManager {
private final @NonNull Map<@NonNull Type, @NonNull Set<@NonNull EObject>> modelManager = new HashMap<@NonNull Type, @NonNull Set<@NonNull EObject>>();
private final @NonNull Collection<@NonNull EObject> roots;
/**
* Initializes me with the context element of an OCL
* expression evaluation. I discover the scope of the model from this
* element.
*
* @param context my context element
*/
public LazyModelManager(EObject context) {
context = EcoreUtil.getRootContainer(context);
Resource eResource = context.eResource();
if (eResource != null) {
roots = eResource.getContents(); // the extent is the resource
} else {
roots = Collections.singleton(context); // can only search this object tree
}
}
/**
* Lazily computes the extent of the specified class <code>key</code>.
*
* @param type a class in the model
*/
@Override
public @NonNull Set<@NonNull ? extends Object> get(org.eclipse.ocl.pivot.@NonNull Class type) {
// TODO: Optimize by parsing ahead of time to find all EClasses that we will query
Set<@NonNull EObject> result = modelManager.get(type);
if (result == null) {
synchronized (modelManager) {
result = modelManager.get(type);
if (result == null) {
result = new HashSet<@NonNull EObject>();
modelManager.put(type, result);
for (Iterator<EObject> iter = EcoreUtil.getAllContents(roots); iter.hasNext();) {
EObject next = iter.next();
if ((next != null) && isInstance(type, next)) {
result.add(next);
}
}
}
}
}
// FIXME subclasses
return result;
}
/**
* Implemented by subclasses to determine whether the specified element
* is an instance of the specified class, according to the metamodel
* semantics implemented by the environment that created this extent map.
*
* @param type a class in the model
* @param element a potential run-time (M0) instance of that class
* @return <code>true</code> if this element is an instance of the given
* class; <code>false</code> otherwise
*/
protected abstract boolean isInstance(@NonNull Type type, @NonNull EObject element);
@Override
public String toString() {
return modelManager.toString();
}
}