/** * <copyright> * </copyright> * * */ package org.feature.multi.perspective.mapping.viewmapping.resource.mtext.mopp; /** * A basic implementation of the ILocationMap interface. Instances store * information about element locations using four maps. * <p> * The set-methods can be called multiple times by the parser that may visit * multiple children from which it copies the localization information for the * parent element (i.e., the element for which set-method is called). It * implements the following behavior: * <p> * Line: The lowest of all sources is used for target<br> * Column: The lowest of all sources is used for target<br> * Start: The lowest of all sources is used for target<br> * End: The highest of all sources is used for target<br> */ public class MtextLocationMap implements org.feature.multi.perspective.mapping.viewmapping.resource.mtext.IMtextLocationMap { /** * A basic interface that can be implemented to select EObjects based of their * location in a text resource. */ public interface ISelector { boolean accept(int startOffset, int endOffset); } protected java.util.Map<org.eclipse.emf.ecore.EObject, Integer> columnMap = new java.util.IdentityHashMap<org.eclipse.emf.ecore.EObject, Integer>(); protected java.util.Map<org.eclipse.emf.ecore.EObject, Integer> lineMap = new java.util.IdentityHashMap<org.eclipse.emf.ecore.EObject, Integer>(); protected java.util.Map<org.eclipse.emf.ecore.EObject, Integer> charStartMap = new java.util.IdentityHashMap<org.eclipse.emf.ecore.EObject, Integer>(); protected java.util.Map<org.eclipse.emf.ecore.EObject, Integer> charEndMap = new java.util.IdentityHashMap<org.eclipse.emf.ecore.EObject, Integer>(); public void setLine(org.eclipse.emf.ecore.EObject element, int line) { setMapValueToMin(lineMap, element, line); } public int getLine(org.eclipse.emf.ecore.EObject element) { return getMapValue(lineMap, element); } public void setColumn(org.eclipse.emf.ecore.EObject element, int column) { setMapValueToMin(columnMap, element, column); } public int getColumn(org.eclipse.emf.ecore.EObject element) { return getMapValue(columnMap, element); } public void setCharStart(org.eclipse.emf.ecore.EObject element, int charStart) { setMapValueToMin(charStartMap, element, charStart); } public int getCharStart(org.eclipse.emf.ecore.EObject element) { return getMapValue(charStartMap, element); } public void setCharEnd(org.eclipse.emf.ecore.EObject element, int charEnd) { setMapValueToMax(charEndMap, element, charEnd); } public int getCharEnd(org.eclipse.emf.ecore.EObject element) { return getMapValue(charEndMap, element); } private int getMapValue(java.util.Map<org.eclipse.emf.ecore.EObject, Integer> map, org.eclipse.emf.ecore.EObject element) { if (!map.containsKey(element)) return -1; Integer value = map.get(element); return value == null ? -1 : value.intValue(); } private void setMapValueToMin(java.util.Map<org.eclipse.emf.ecore.EObject, Integer> map, org.eclipse.emf.ecore.EObject element, int value) { // We need to synchronize the write access, because other threads may iterate over // the map concurrently. synchronized (this) { if (element == null || value < 0) return; if (map.containsKey(element) && map.get(element) < value) return; map.put(element, value); } } private void setMapValueToMax(java.util.Map<org.eclipse.emf.ecore.EObject, Integer> map, org.eclipse.emf.ecore.EObject element, int value) { // We need to synchronize the write access, because other threads may iterate over // the map concurrently. synchronized (this) { if (element == null || value < 0) return; if (map.containsKey(element) && map.get(element) > value) return; map.put(element, value); } } public java.util.List<org.eclipse.emf.ecore.EObject> getElementsAt(final int documentOffset) { java.util.List<org.eclipse.emf.ecore.EObject> result = getElements(new ISelector() { public boolean accept(int start, int end) { return start <= documentOffset && end >= documentOffset; } }); // sort elements according to containment hierarchy java.util.Collections.sort(result, new java.util.Comparator<org.eclipse.emf.ecore.EObject>() { public int compare(org.eclipse.emf.ecore.EObject objectA, org.eclipse.emf.ecore.EObject objectB) { if (org.eclipse.emf.ecore.util.EcoreUtil.isAncestor(objectA, objectB)) { return 1; } else { if (org.eclipse.emf.ecore.util.EcoreUtil.isAncestor(objectB, objectA)) { return -1; } else { return 0; } } } }); return result; } public java.util.List<org.eclipse.emf.ecore.EObject> getElementsBetween(final int startOffset, final int endOffset) { java.util.List<org.eclipse.emf.ecore.EObject> result = getElements(new ISelector() { public boolean accept(int start, int end) { return start >= startOffset && end <= endOffset; } }); return result; } private java.util.List<org.eclipse.emf.ecore.EObject> getElements(ISelector s) { // There might be more than one element at the given offset. Thus, we collect all // of them and sort them afterwards. java.util.List<org.eclipse.emf.ecore.EObject> result = new java.util.ArrayList<org.eclipse.emf.ecore.EObject>(); // We need to synchronize the write access, because other threads may iterate over // the map concurrently. synchronized (this) { for (org.eclipse.emf.ecore.EObject next : charStartMap.keySet()) { Integer start = charStartMap.get(next); Integer end = charEndMap.get(next); if (start == null || end == null) { continue; } if (s.accept(start, end)) { result.add(next); } } } java.util.Collections.sort(result, new java.util.Comparator<org.eclipse.emf.ecore.EObject>() { public int compare(org.eclipse.emf.ecore.EObject objectA, org.eclipse.emf.ecore.EObject objectB) { int lengthA = getCharEnd(objectA) - getCharStart(objectA); int lengthB = getCharEnd(objectB) - getCharStart(objectB); return lengthA - lengthB; } }); return result; } }