| /******************************************************************************* |
| * 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(); |
| } |
| } |