blob: b99af9bfe6d140753e9a2b189b2248c0b43af1ce [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012 E.D.Willink 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.examples.xtext.base.utilities;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.examples.pivot.Element;
import org.eclipse.ocl.examples.pivot.manager.MetaModelManager;
import org.eclipse.ocl.examples.pivot.manager.MetaModelManagerListener;
import org.eclipse.ocl.examples.pivot.resource.ASResource;
import org.eclipse.ocl.examples.xtext.base.basecs.ModelElementCS;
/**
* The CSI2PivotMapping maintains the mapping between CS elements or rather their CSIs
* that remain stable after recreation and the Pivot elements. This mapping may be used
* repeatedly while editing (CS2Pivot conversions) to associate changing CS elements with
* stable Pivot elements.
* The mapping is also created during a Pivot2CS conversion to allow subsequent CS2Pivot
* conversions to reuse the original Pivot elements.
*/
public class CSI2PivotMapping extends AdapterImpl implements MetaModelManagerListener
{
public static @NonNull CSI2PivotMapping getAdapter(@NonNull MetaModelManager metaModelManager) {
List<Adapter> eAdapters = metaModelManager.getASResourceSet().eAdapters();
for (Adapter adapter : eAdapters) {
if (adapter instanceof CSI2PivotMapping) {
return (CSI2PivotMapping) adapter;
}
}
CSI2PivotMapping adapter = new CSI2PivotMapping();
eAdapters.add(adapter);
return adapter;
}
/**
* Mapping of each CS resource to its corresponding pivot Resource.
*/
protected final @NonNull Map<BaseCSResource, ASResource> cs2asResourceMap = new HashMap<BaseCSResource, ASResource>();
/**
* Mapping of each CS resource's URI to a short alias used in URI maps.
*/
private Map<String, String> csURI2aliasMap = new HashMap<String, String>();
/**
* The map from CS element (identified by URI) to pivot element at the end of the last update. This map enables
* the next update from a potentially different CS Resource and elements but the same URIs to re-use the pivot elements
* and to kill off the obsolete elements.
*/
private Map<String, Element> csi2pivot = new HashMap<String, Element>();
/**
* A lazily created inverse map that may be required for navigation from an outline.
*/
private Map<Element, ModelElementCS> pivot2cs = null;
private int nextAlias = 0;
private CSI2PivotMapping() {}
public void add(Map<? extends BaseCSResource, ? extends ASResource> cs2asResourceMap) {
pivot2cs = null;
this.cs2asResourceMap.putAll(cs2asResourceMap);
}
public void clear() {
csURI2aliasMap.clear();
csi2pivot.clear();
pivot2cs = null;
}
public Set<String> computeCSIs(Collection<? extends Resource> csResources) {
Set<String> map = new HashSet<String>();
for (Resource csResource : csResources) {
for (Iterator<EObject> it = csResource.getAllContents(); it.hasNext(); ) {
EObject eObject = it.next();
if (eObject instanceof ModelElementCS) {
ModelElementCS csElement = (ModelElementCS)eObject;
String csURI = getCSI(csElement);
map.add(csURI);
}
}
}
return map;
}
protected @NonNull Map<Element, ModelElementCS> computePivot2CSMap() {
Map<Element, ModelElementCS> map = new HashMap<Element, ModelElementCS>();
for (Resource csResource : cs2asResourceMap.keySet()) {
for (Iterator<EObject> it = csResource.getAllContents(); it.hasNext(); ) {
EObject eObject = it.next();
if (eObject instanceof ModelElementCS) {
ModelElementCS csElement = (ModelElementCS)eObject;
Element pivotElement = csElement.getPivot();
if (pivotElement != null) {
map.put(pivotElement, csElement);
}
// System.out.println(DomainUtil.debugSimpleName(pivotElement) + " => " + DomainUtil.debugSimpleName(csElement));
}
}
}
return map;
}
/**
* Return the Pivot element corresponding to a given CS element.
*/
public @Nullable Element get(@NonNull ModelElementCS csElement) {
String csi = getCSI(csElement);
return csi2pivot.get(csi);
}
/**
* Return the AS Resource corresponding to a given CS Resource.
*/
public @Nullable ASResource getASResource(@Nullable BaseCSResource csResource) {
return cs2asResourceMap.get(csResource);
}
/**
* Get the Concrete Syntax Identifier for a CS element. This is a form of URI. It is significantly compacted to
* save on memory.
* @param csResource2aliasMap
*/
private @NonNull String getCSI(@NonNull ModelElementCS csElement) {
String csi = csElement.getCsi();
if (csi == null) {
Resource csResource = csElement.eResource();
String csResourceURI = csResource.getURI().toString();
String fragment = csResource.getURIFragment(csElement); // FIXME Use more optimized compressing algorithm
String alias = csURI2aliasMap.get(csResourceURI);
if (alias == null) {
alias = Integer.toString(nextAlias++);
csURI2aliasMap.put(csResourceURI, alias);
}
csi = alias + '#' + fragment;
csElement.setCsi(csi);
}
return csi;
}
/**
* Return all mapped CS Resources.
*/
@SuppressWarnings("null")
public @NonNull Set<BaseCSResource> getCSResources() {
return cs2asResourceMap.keySet();
}
public @Nullable ModelElementCS getCSElement(@NonNull Element pivotElement) {
if (pivot2cs == null) {
pivot2cs = computePivot2CSMap();
}
return pivot2cs.get(pivotElement);
}
public Map<String, Element> getMapping() {
return csi2pivot;
}
@Override
public boolean isAdapterForType(Object type) {
return type == CSI2PivotMapping.class;
}
public void metaModelManagerDisposed(@NonNull MetaModelManager metaModelManager) {
clear();
}
/**
* Install the Pivot element corresponding to a given CS element.
*/
public void put(@NonNull ModelElementCS csElement, @Nullable Element pivotElement) {
String csi = getCSI(csElement);
csi2pivot.put(csi, pivotElement);
}
/**
* Remove the Resource mappings for all csResources. The CSI mappings persist until update() is called.
*/
public void removeCSResources(@NonNull Set<? extends BaseCSResource> csResources) {
pivot2cs = null;
for (Resource csResource : csResources) {
cs2asResourceMap.remove(csResource);
}
}
/**
* Update the mapping to cache the Pivot elements with respect to the CSIs for all CS elements in csResources.
*/
public void update() {
pivot2cs = null;
csi2pivot.clear();
Set<String> deadURIs = new HashSet<String>(csURI2aliasMap.keySet());
for (Resource csResource : cs2asResourceMap.keySet()) {
deadURIs.remove(csResource.getURI().toString());
for (Iterator<EObject> it = csResource.getAllContents(); it.hasNext(); ) {
EObject eObject = it.next();
if (eObject instanceof ModelElementCS) {
ModelElementCS csElement = (ModelElementCS)eObject;
Element pivotElement = csElement.getPivot();
put(csElement, pivotElement);
}
}
}
// for (String deadURI : deadURIs) {
// FIXME Imported CS kills off Importing CS csURI2aliasMap.remove(deadURI);
// }
}
}