/*******************************************************************************
* Copyright (c) 2010, 2015 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.resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
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.Element;
import org.eclipse.ocl.pivot.Model;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.Package;
import org.eclipse.ocl.pivot.PivotFactory;
import org.eclipse.ocl.pivot.internal.manager.Orphanage;
import org.eclipse.ocl.pivot.internal.utilities.AS2Moniker;
import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal;
import org.eclipse.ocl.pivot.resource.ASResource;
import org.eclipse.ocl.pivot.util.Visitable;
import org.eclipse.ocl.pivot.utilities.ASSaverLocateVisitor;
import org.eclipse.ocl.pivot.utilities.ASSaverNormalizeVisitor;
import org.eclipse.ocl.pivot.utilities.ASSaverResolveVisitor;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.PivotConstants;
/**
* ASSaver ensures that all references to synthesized types are terminated
* by local copies of the synthesized types.
*/
public class ASSaver
{
protected final @NonNull Resource resource;
public ASSaver(@NonNull Resource resource) {
this.resource = resource;
}
/**
* The moniker to operation map for operations defined with the saved resource.
*/
private Map<String, Operation> operations = new HashMap<String, Operation>();
/**
* TypedElements that refer to specializations.
*/
private @NonNull List<Element> specializingElements = new ArrayList<Element>();
/**
* The extra package for copies of specializations.
*/
private /*@LazyNonNull*/ org.eclipse.ocl.pivot.Package orphanage = null;
/**
* Map of original specialization to local specialization
*/
private @NonNull Map<org.eclipse.ocl.pivot.Class, org.eclipse.ocl.pivot.Class> specializations = new HashMap<org.eclipse.ocl.pivot.Class, org.eclipse.ocl.pivot.Class>();
/**
* The extra package for copies of specializations.
*/
private /*@LazyNonNull*/ org.eclipse.ocl.pivot.Class orphanageClass = null;
/**
* The appropriate normalization visitor for each Resource.
*/
private /*@LazyNonNull*/ Map<@NonNull Resource, @NonNull ASSaverNormalizeVisitor> resource2normalizeVisitor = null;
public void addSpecializingElement(@NonNull Element object) {
specializingElements.add(object);
}
public boolean addSpecializingElement(@NonNull Element object, @NonNull Operation referredOperation) {
if (!isOrphanOperation(referredOperation)) {
return false;
}
else {
specializingElements.add(object);
return true;
}
}
public boolean addSpecializingElement(@NonNull Element object, org.eclipse.ocl.pivot.@NonNull Class referredType) {
if (PivotUtilInternal.isLibraryType(referredType)) {
return false;
}
else {
specializingElements.add(object);
return true;
}
}
protected @NonNull ASSaverLocateVisitor getLocateVisitor(@NonNull EObject eObject) {
Resource resource = eObject.eResource();
if (resource instanceof ASResource) {
return ((ASResource)resource).getASResourceFactory().createASSaverLocateVisitor(this);
}
else if (resource == null) {
throw new IllegalStateException("Cannot locate " + ASSaverLocateVisitor.class.getName() + " for resource-less " + eObject.eClass().getName());
}
else {
throw new IllegalStateException("Cannot locate " + ASSaverLocateVisitor.class.getName() + " for non-OCL " + resource.getClass().getName());
}
}
protected @NonNull ASSaverNormalizeVisitor getNormalizeVisitor(@NonNull EObject eObject) {
Resource resource = eObject.eResource();
if (resource == null) {
throw new IllegalStateException("Cannot locate " + ASSaverLocateVisitor.class.getName() + " for resource-less " + eObject.eClass().getName());
}
if (resource2normalizeVisitor == null) {
resource2normalizeVisitor = new HashMap<@NonNull Resource, @NonNull ASSaverNormalizeVisitor>();
}
ASSaverNormalizeVisitor visitor = resource2normalizeVisitor.get(resource);
if (visitor != null) {
return visitor;
}
if (resource instanceof ASResource) {
ASResource asResource = (ASResource)resource;
visitor = asResource.getASResourceFactory().createASSaverNormalizeVisitor(this);
resource2normalizeVisitor.put(resource, visitor);
return visitor;
}
else {
throw new IllegalStateException("Cannot locate " + ASSaverLocateVisitor.class.getName() + " for non-OCL " + resource.getClass().getName());
}
}
protected org.eclipse.ocl.pivot.@NonNull Class getOrphanClass(org.eclipse.ocl.pivot.@NonNull Package orphanagePackage) {
org.eclipse.ocl.pivot.Class orphanageClass2 = orphanageClass;
if (orphanageClass2 == null) {
orphanageClass = orphanageClass2 = PivotFactory.eINSTANCE.createAnyType(); // No superclasses
orphanageClass2.setName(PivotConstants.ORPHANAGE_NAME);
orphanagePackage.getOwnedClasses().add(orphanageClass2);
}
return orphanageClass2;
}
protected org.eclipse.ocl.pivot.@NonNull Package getOrphanPackage(@NonNull Resource resource) {
Package orphanage2 = orphanage;
if (orphanage2 == null) {
orphanage = orphanage2 = PivotFactory.eINSTANCE.createPackage();
orphanage2.setName(PivotConstants.ORPHANAGE_NAME);
orphanage2.setNsPrefix(PivotConstants.ORPHANAGE_PREFIX);
orphanage2.setURI(PivotConstants.ORPHANAGE_URI);
EList<EObject> contents = resource.getContents();
if ((contents.size() > 0) && (contents.get(0) instanceof Model)) {
((Model)contents.get(0)).getOwnedPackages().add(orphanage2);
}
else {
contents.add(orphanage2);
}
}
return orphanage2;
}
protected @NonNull ASSaverResolveVisitor getResolveVisitor(@NonNull EObject eObject) {
Resource resource = eObject.eResource();
if (resource instanceof ASResource) {
return ((ASResource)resource).getASResourceFactory().createASSaverResolveVisitor(this);
}
else if (resource == null) {
throw new IllegalStateException("Cannot locate " + ASSaverResolveVisitor.class.getName() + " for resource-less " + eObject.eClass().getName());
}
else {
throw new IllegalStateException("Cannot locate " + ASSaverResolveVisitor.class.getName() + " for non-OCL " + resource.getClass().getName());
}
}
protected boolean isOrphanOperation(@NonNull Operation operation) { // FIXME Non-static PivotUtils
// FIXME surely an orphan is one for which eResource() is null,
// or one that is in the orphanage.
if (operation.getOwnedBindings().size() > 0) {
return true;
}
return false;
}
/**
* Prepare a pivot resource for save by redirecting all type references to
* specializations to local copies of the specializations.
*/
public void localizeSpecializations() {
locateSpecializations(resource.getContents());
if (specializingElements.size() > 0) {
loadOrphanage(resource);
for (int i = 0; i < specializingElements.size(); i++) { // Domain may grow
Element element = specializingElements.get(i);
if (element != null) {
ASSaverResolveVisitor resolveVisitor = getResolveVisitor(element);
resolveVisitor.safeVisit(element);
}
}
// List<Type> ownedTypes = orphanage.getOwnedType();
// List<Type> sorted = ownedTypes; //WIP PivotUtil.sortByMoniker(new ArrayList<Type>(ownedTypes));
// ownedTypes.clear();
// ownedTypes.addAll(sorted);
}
}
protected void loadOrphanage(@NonNull Resource resource) {
Model root = null;
Package orphanage2 = orphanage;
if (orphanage2 == null) {
for (EObject eRoot : resource.getContents()) {
if (eRoot instanceof Model) {
if (root == null) {
root = (Model) eRoot;
}
if (orphanage2 == null) {
for (org.eclipse.ocl.pivot.Package asPackage : ((Model)eRoot).getOwnedPackages()) {
if (Orphanage.isTypeOrphanage(asPackage)) {
orphanage = orphanage2 = asPackage;
for (org.eclipse.ocl.pivot.Class asType : orphanage2.getOwnedClasses()) {
if (PivotConstants.ORPHANAGE_NAME.equals(asType.getName())) {
orphanageClass = asType;
}
else {
specializations.put(asType, asType);
}
}
break;
}
}
}
}
if ((eRoot instanceof org.eclipse.ocl.pivot.Package) && Orphanage.isTypeOrphanage((org.eclipse.ocl.pivot.Package)eRoot)) { // FIXME Obsolete
orphanage = orphanage2 = (org.eclipse.ocl.pivot.Package)eRoot;
for (org.eclipse.ocl.pivot.Class asType : orphanage2.getOwnedClasses()) {
if (PivotConstants.ORPHANAGE_NAME.equals(asType.getName())) {
orphanageClass = asType;
}
else {
specializations.put(asType, asType);
}
}
break;
}
}
}
}
protected void locateSpecializations(/*@NonNull*/ List<? extends EObject> eObjects) {
for (EObject eObject : eObjects) {
if (eObject instanceof Visitable) {
ASSaverLocateVisitor locateVisitor = getLocateVisitor(eObject);
locateVisitor.safeVisit((Visitable) eObject);
}
locateSpecializations(eObject.eContents());
}
}
public void normalizeContents() {
for (@NonNull TreeIterator<EObject> tit = resource.getAllContents(); tit.hasNext(); ) {
EObject eObject = tit.next();
if (eObject instanceof Visitable) {
ASSaverNormalizeVisitor normalizeVisitor = getNormalizeVisitor(eObject);
normalizeVisitor.safeVisit((Visitable) eObject);
}
}
}
/**
* Return the resolved variant of referredType, which may require creation
* of a local copy of a specialization.
*/
public @NonNull <T extends Operation> T resolveOperation(@NonNull T referredOperation) {
if (!isOrphanOperation(referredOperation)) {
return referredOperation;
}
String moniker = AS2Moniker.toString(referredOperation);
Operation operation = operations.get(moniker);
if (operation != null) {
@SuppressWarnings("unchecked")
T castOperation = (T) operation;
return castOperation;
}
T resolvedOperation = ClassUtil.nonNullEMF(EcoreUtil.copy(referredOperation));
if (orphanageClass == null) {
Package orphanage2 = orphanage;
if (orphanage2 == null) {
orphanage2 = getOrphanPackage(resource);
}
orphanageClass = getOrphanClass(orphanage2);
}
orphanageClass.getOwnedOperations().add(resolvedOperation);
operations.put(moniker, resolvedOperation);
String newMoniker = AS2Moniker.toString(resolvedOperation);
assert moniker.equals(newMoniker);
locateSpecializations(Collections.singletonList(resolvedOperation));
return resolvedOperation;
}
/**
* Return the resolved variant of referredType, which may require creation
* of a local copy of a specialization.
*/
public @NonNull <T extends org.eclipse.ocl.pivot.Class> T resolveType(@NonNull T referredType) {
if (PivotUtilInternal.isLibraryType(referredType)) {
return referredType;
}
org.eclipse.ocl.pivot.Class resolvedType = specializations.get(referredType);
if (resolvedType == null) {
resolvedType = ClassUtil.nonNullEMF(EcoreUtil.copy(referredType)); // FIXME cast
specializations.put(referredType, resolvedType); // FIXME cast
specializations.put(resolvedType, resolvedType);
EObject eContainer = resolvedType.eContainer();
if (eContainer == null) {
Package orphanage2 = orphanage;
if (orphanage2 == null) {
orphanage2 = getOrphanPackage(resource);
}
orphanage.getOwnedClasses().add(resolvedType);
}
}
/* String moniker = AS2Moniker.toString(referredType);
Type type = types.get(moniker);
if (type != null) {
@SuppressWarnings("unchecked")
T castType = (T) type;
return castType;
}
T resolvedType = EcoreUtil.copy(referredType);
orphanage.getOwnedType().add(resolvedType);
types.put(moniker, resolvedType);
String newMoniker = AS2Moniker.toString(resolvedType);
// assert moniker.equals(newMoniker) : newMoniker + " is not equal to " + moniker;
if (!moniker.equals(newMoniker)) {
String moniker2 = AS2Moniker.toString(referredType);
String newMoniker2 = AS2Moniker.toString(resolvedType);
assert moniker.equals(newMoniker) : newMoniker + " is not equal to " + moniker;
} */
locateSpecializations(Collections.singletonList(resolvedType));
@SuppressWarnings("unchecked")
T castType = (T)resolvedType;
return castType;
}
}