blob: f4cd44e9179a5146207f0194677e4d13d55dc7c2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2022 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* E.D.Willink - initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.pivot.internal.manager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.WeakHashMap;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.AbstractEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.Model;
import org.eclipse.ocl.pivot.PivotFactory;
import org.eclipse.ocl.pivot.internal.PackageImpl;
import org.eclipse.ocl.pivot.internal.resource.ASResourceImpl;
import org.eclipse.ocl.pivot.internal.resource.OCLASResourceFactory;
import org.eclipse.ocl.pivot.internal.utilities.PivotConstantsInternal;
import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.PivotConstants;
/**
* An Orphanage provides a Package that weakly contains elements such as type specializations that
* should require a container for the purposes of validation, but which should be eligible for
* garbage collection whenever no longer in use.
*/
public class Orphanage extends PackageImpl
{
/**
* The OrphanResource tailors the inherited ASREsorce functionality to support the single Resource shared by all
* OCL consumers, but not included in any REsourceSEt. It is nt saved and hso has no xmi:ids but it does have LUSSIDs
* in order to contribute to the signatures of operations.
*/
protected static class OrphanResource extends ASResourceImpl
{
protected OrphanResource(@NonNull URI uri) {
super(uri, OCLASResourceFactory.getInstance());
setUpdating(true);
}
@Override
public NotificationChain basicSetResourceSet(ResourceSet resourceSet, NotificationChain notifications) {
return notifications; // The OrphanResource is in no, rather than latest, ResourceSt
}
@Override
protected void doUnload() {
if (contents != null) {
for (EObject aContent : contents) {
if (aContent instanceof Orphanage) {
((Orphanage)aContent).dispose();
}
}
contents = null;
}
// super.doUnload();
}
@Override
public String getURIFragment(EObject eObject) {
// The OrphanResource cannot be saved so has no LUSSID-based xmi:ids, but Xtext serialization needs a URI
return superGetURIFragment(eObject);
}
@Override
public ResourceSet getResourceSet() {
// OrphanResource can be shared across many ResourceSets - use the current one
return PivotUtilInternal.getEnvironmentFactory(null).getMetamodelManager().getASResourceSet();
}
@Override
public boolean isOrphanage() {
return true;
}
}
/**
* WeakEList enables a WeakHashMap to be used as a Package.ownedType. The weakness allows stale synthesized types to vanish.
* The Map ensures that duplicates are avoided.
* <br>
* Only the minimal support necessary to make aType.setPackage() and subsequent usage is provided.
* <br>
* A cached sorted list copy of the map is created on demand and may be shared by multiple iterators. However it must not be modified
* since its staleness is detected by a simple size comparison with the map.
*/
private static class WeakEList<T> extends AbstractEList<T> implements InternalEList<T>
{
/**
* A simple immutable iterator that caches the list image on construction to avoid changes.
*/
protected static class ListIterator<T> implements java.util.ListIterator<T>
{
protected final @NonNull List<Map.@NonNull Entry<@NonNull T, @NonNull Integer>> list;
private final int size;
private int cursor;
public ListIterator(@NonNull List<Map.@NonNull Entry<@NonNull T, @NonNull Integer>> list, int index) {
this.list = list;
this.size = list.size();
this.cursor = index;
if ((cursor < 0) || (size < cursor)) {
throw new NoSuchElementException(cursor + "/" + size);
}
}
@Override
public void add(T o) {
throw new UnsupportedOperationException();
}
@Override
public boolean hasNext() {
return cursor < size;
}
@Override
public boolean hasPrevious() {
return 0 < cursor;
}
@Override
public T next() {
if ((cursor < 0) || (size <= cursor)) {
throw new NoSuchElementException(cursor + "/" + size);
}
return list.get(cursor++).getKey();
}
@Override
public int nextIndex() {
return cursor;
}
@Override
public T previous() {
int previousCursor = cursor - 1;
if ((previousCursor < 0) || (size <= previousCursor)) {
throw new NoSuchElementException(previousCursor + "/" + size);
}
cursor = previousCursor;
return list.get(previousCursor).getKey();
}
@Override
public int previousIndex() {
return cursor - 1;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void set(T o) {
throw new UnsupportedOperationException();
}
}
/**
* Map of content-value to list-index.
*/
private final @NonNull WeakHashMap<@NonNull T, @NonNull Integer> weakMap = new WeakHashMap<>();
/**
* Incrermenting counter used to sort the list into a predictable order.
*/
private int counter = 0;
/**
* The most recent ordered view of the weakMap.
*/
private @Nullable List<@NonNull Entry<@NonNull T, @NonNull Integer>> weakList = null;
@Override
public boolean addAllUnique(Object[] objects, int start, int end) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAllUnique(int index, Object[] objects, int start, int end) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAllUnique(Collection<? extends T> collection) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAllUnique(int index, Collection<? extends T> collection) {
throw new UnsupportedOperationException();
}
@Override
public void addUnique(T object) {
throw new UnsupportedOperationException();
}
@Override
public void addUnique(int index, T object) {
throw new UnsupportedOperationException();
}
@Override
public NotificationChain basicAdd(T object, NotificationChain notifications) {
assert object != null;
synchronized (weakMap) {
if (!weakMap.containsKey(object)) {
weakMap.put(object, Integer.valueOf(counter++));
weakList = null;
}
}
return notifications;
}
@Override
public boolean basicContains(Object object) {
throw new UnsupportedOperationException();
}
@Override
public boolean basicContainsAll(Collection<?> collection) {
throw new UnsupportedOperationException();
}
@Override
public T basicGet(int index) {
throw new UnsupportedOperationException();
}
@Override
public int basicIndexOf(Object object) {
throw new UnsupportedOperationException();
}
@Override
public Iterator<T> basicIterator() {
throw new UnsupportedOperationException();
}
@Override
public int basicLastIndexOf(Object object) {
throw new UnsupportedOperationException();
}
@Override
public List<T> basicList() {
throw new UnsupportedOperationException();
}
@Override
public ListIterator<T> basicListIterator() {
return listIterator();
}
@Override
public ListIterator<T> basicListIterator(int index) {
return listIterator(index);
}
@Override
public NotificationChain basicRemove(Object object, NotificationChain notifications) {
throw new UnsupportedOperationException();
}
@Override
public Object[] basicToArray() {
throw new UnsupportedOperationException();
}
@Override
public <T2> T2[] basicToArray(T2[] array) {
throw new UnsupportedOperationException();
}
public void dispose() {
synchronized (weakMap) {
weakMap.clear();
weakList = null;
}
}
@Override
public T get(int index) {
List<@NonNull Entry<@NonNull T, @NonNull Integer>> weakList2 = getList();
return weakList2.get(index).getKey();
}
private @NonNull List<@NonNull Entry<@NonNull T, @NonNull Integer>> getList() {
List<@NonNull Entry<@NonNull T, @NonNull Integer>> weakList2;
synchronized (weakMap) {
weakList2 = weakList;
if (weakList2 == null) {
weakList2 = weakList = new ArrayList<>(weakMap.entrySet());
Collections.sort(weakList2, new Comparator<Entry<T, Integer>>()
{
@Override
public int compare(Entry<T, Integer> o1, Entry<T, Integer> o2) {
return o1.getValue().intValue() - o2.getValue().intValue();
}
});
}
}
return weakList2;
}
@Override
public boolean isEmpty() {
return weakMap.size() == 0;
}
// @Override
// protected boolean isUnique() {
// return true; -- implementing this makes things really really slow.
// }
@Override
public @NonNull Iterator<T> iterator() {
return listIterator();
}
@Override
public @NonNull ListIterator<T> listIterator() {
return new ListIterator<T>(getList(), 0);
}
@Override
public @NonNull ListIterator<T> listIterator(int index) {
return new ListIterator<T>(getList(), index);
}
@Override
public void move(int newPosition, T object) {
throw new UnsupportedOperationException();
}
@Override
public T move(int newPosition, int oldPosition) {
throw new UnsupportedOperationException();
}
@Override
protected T primitiveGet(int index) {
throw new UnsupportedOperationException();
}
@Override
public T remove(int index) {
throw new UnsupportedOperationException();
}
@Override
public T setUnique(int index, T object) {
throw new UnsupportedOperationException();
}
@Override
public int size() {
return weakMap.size();
}
@Override
public String toString() {
return weakMap.toString();
}
}
public static final @NonNull URI ORPHANAGE_URI = ClassUtil.nonNullEMF(URI.createURI(PivotConstants.ORPHANAGE_URI + PivotConstants.DOT_OCL_AS_FILE_EXTENSION));
private static Orphanage ORPHAN_PACKAGE = null;
private static OrphanResource ORPHAN_RESOURCE = null;
public static void disposeInstance() {
if (ORPHAN_PACKAGE != null) {
ORPHAN_PACKAGE.dispose();
ORPHAN_PACKAGE = null;
ORPHAN_RESOURCE = null;
}
}
/**
* Return the Orphanage for an eObject, which is the Orphanage resource in the same ResourceSet as
* the eObject, else the global Orphanage.
*/
@Deprecated /* @deprecated Not used, the global orphanage should always be used - See Bug 579051 */
public static @Nullable Orphanage getOrphanage(@NonNull EObject eObject) {
// if (eObject == null) {
// return null;
// }
Resource resource = eObject.eResource();
if (resource == null) {
return null;
}
ResourceSet resourceSet = resource.getResourceSet();
return getOrphanage(resourceSet);
}
/**
* Return the global Orphanage.
*/
public static @NonNull Orphanage getOrphanage() {
OrphanResource orphanResource = ORPHAN_RESOURCE;
Orphanage orphanPackage = ORPHAN_PACKAGE;
if (orphanPackage == null) {
orphanPackage = ORPHAN_PACKAGE = new Orphanage(PivotConstants.ORPHANAGE_NAME, PivotConstants.ORPHANAGE_URI);
Model orphanModel = PivotFactory.eINSTANCE.createModel();
orphanModel.setName(PivotConstants.ORPHANAGE_NAME);;
orphanModel.setExternalURI(PivotConstants.ORPHANAGE_URI);
orphanModel.getOwnedPackages().add(orphanPackage);
orphanResource = ORPHAN_RESOURCE = new OrphanResource(ORPHANAGE_URI);
orphanResource.getContents().add(orphanModel);
orphanResource.setSaveable(false);
}
return orphanPackage;
}
/**
* Return the Orphanage for resourceSet.
* The global orphanage contains the globally unique shared orphan,
* A local orphanage holds the subset of the overall orphans necessary to support serialization.
*/
@Deprecated /* @deprecated - not used - use the shared global Prphange */
public static @NonNull Orphanage getOrphanage(@NonNull ResourceSet resourceSet) {
if (ORPHAN_PACKAGE == null) {
return getOrphanage();
}
Orphanage orphanPackage = ORPHAN_PACKAGE;
OrphanResource orphanResource = ORPHAN_RESOURCE;
assert orphanPackage != null;
assert orphanResource != null;
EList<@NonNull Resource> resources = resourceSet.getResources();
for (Resource aResource : resources) {
if (aResource == orphanResource) {
return orphanPackage;
}
}
resources.add(orphanResource);
return orphanPackage;
}
/**
* Return true if asPackage is an orphanage for synthesized types.
*/
public static boolean isTypeOrphanage(org.eclipse.ocl.pivot.@Nullable Package asPackage) {
if (asPackage == null) {
return false;
}
else {
String uri = asPackage.getURI();
return PivotConstants.ORPHANAGE_URI.equals(uri) || PivotConstantsInternal.OLD_ORPHANAGE_URI.equals(uri);
}
}
public Orphanage(@NonNull String name, @NonNull String nsURI) {
// super(uri);
// setLoaded(true);
setName(name);
setURI(nsURI);
}
public void dispose() {
if (ownedClasses != null) {
((WeakEList<?>)ownedClasses).dispose();
}
}
@Override
public @NonNull EList<org.eclipse.ocl.pivot.Class> getOwnedClasses() {
EList<org.eclipse.ocl.pivot.Class> ownedType2 = ownedClasses;
if (ownedType2 == null)
{
ownedType2 = ownedClasses = new WeakEList<org.eclipse.ocl.pivot.Class>(/*WeakReference.class, this, PivotPackage.PACKAGE__OWNED_TYPE, PivotPackage.TYPE__PACKAGE*/);
}
return ownedType2;
}
}