blob: 774ad1a1517e8e93e6547dce2d8769fa7779eeda [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005-2014 Obeo
*
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.query.legacy.gen.template.eval;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.sirius.query.legacy.ecore.tools.ETools;
import org.eclipse.sirius.query.legacy.tools.strings.Int2;
/**
* Mapping between the model and the text value.
*
*
*/
public class TextModelMapping {
/**
* The element that compares positions (Int2).
*/
private static class InversePosComparator implements Comparator, Serializable {
private static final long serialVersionUID = 1L;
public int compare(Object arg0, Object arg1) {
Int2 pos0 = ((Int2) arg0);
Int2 pos1 = ((Int2) arg1);
if (pos0.b() < pos1.b()) {
return 1;
} else if (pos0.b() > pos1.b()) {
return -1;
} else {
if (pos0.e() < pos1.e()) {
return -1;
} else if (pos0.e() > pos1.e()) {
return 1;
} else {
return 0;
}
}
}
}
/**
* The element that compares positions (Int2).
*/
private static class PosComparator implements Comparator {
/**
* {@inheritDoc}
*/
public int compare(Object arg0, Object arg1) {
Int2 pos0 = ((Int2) arg0);
Int2 pos1 = ((Int2) arg1);
int res;
if (pos0.b() < pos1.b()) {
res = -1;
} else if (pos0.b() > pos1.b()) {
res = 1;
} else {
if (pos0.e() < pos1.e()) {
res = 1;
} else if (pos0.e() > pos1.e()) {
res = -1;
} else {
res = 0;
}
}
return res;
}
}
/**
* An object that maps text position to EObject.
*/
protected Map pos2EObject = new TreeMap(new InversePosComparator());
/**
* An object that maps EObject to text position.
*/
protected Map eObject2Positions = new HashMap();
/**
* An object that maps EObject to comment text position.
*/
protected Map eObject2CommentPositions = new HashMap();
/**
* An object that maps text position to an EObject declaration. It is used
* for an open link action.
*/
protected Map pos2LinkEObject = new TreeMap(new InversePosComparator());
/**
* Highlight positions.
* <p>
* <li>highlightedPos[HIGHLIGHTED_STATIC_TEXT] is a list of positions for
* the text which is not in the models.</li>
* <li>highlightedPos[HIGHLIGHTED_COMMENT] is a list of positions for
* comments.</li>
*/
protected List[] highlightedPos = new List[2];
// Default highlight.
public static final int HIGHLIGHTED_DEFAULT = -1;
// Highlight for the text which is not in the models.
public static final int HIGHLIGHTED_STATIC_TEXT = 0;
// Highlight for comments.
public static final int HIGHLIGHTED_COMMENT = 1;
/**
* Default object for text mapping.
*/
protected EObject object;
/**
* Current size of the text used for the mapping.
*/
protected int shift;
/**
* Indicates if the changes were validated.
* <p>
* It is impossible to change mappings when commit is true.
*/
protected boolean commit = false;
/**
* Constructor.
*
* @param object
* is the default object for text mapping
* @param freeze
* is used to freeze the mappings, this helps to improve the
* performance
*/
public TextModelMapping(EObject object, boolean freeze) {
this.object = object;
for (int i = 0; i < highlightedPos.length; i++) {
if (highlightedPos[i] == null) {
highlightedPos[i] = new ArrayList();
}
}
if (freeze) {
commit = true;
} else {
reset();
}
}
/**
* Reset all informations.
*/
protected void reset() {
if (!commit) {
this.shift = 0;
pos2EObject.clear();
index2EObjects.clear();
eObject2Positions.clear();
eObject2CommentPositions.clear();
pos2LinkEObject.clear();
index2LinkEObject.clear();
for (int i = 0; i < highlightedPos.length; i++) {
if (highlightedPos[i] != null) {
highlightedPos[i].clear();
} else {
highlightedPos[i] = new ArrayList();
}
}
}
}
/**
* Appends other mappings to current mappings.
*
* @param other
* is the other mappings between the model and the text value
*/
public void from(TextModelMapping other) {
if (!commit && other != null) {
// mapping pos2EObject, eObject2Pos
Iterator it = other.pos2EObject.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
int b = shift + ((Int2) entry.getKey()).b();
int e = shift + ((Int2) entry.getKey()).e();
EObject object = (EObject) entry.getValue();
addMapping(object, b, e);
}
// mapping eObject2CommentPositions
it = other.eObject2CommentPositions.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
List value = (List) entry.getValue();
Iterator positions = value.iterator();
while (positions.hasNext()) {
Int2 pos = (Int2) positions.next();
int b = shift + pos.b();
int e = shift + pos.e();
EObject object = (EObject) entry.getKey();
addCommentMapping(object, b, e);
}
}
// mapping pos2LinkEObject
it = other.pos2LinkEObject.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
int b = shift + ((Int2) entry.getKey()).b();
int e = shift + ((Int2) entry.getKey()).e();
EObject object = (EObject) entry.getValue();
pos2LinkEObject.put(new Int2(b, e), object);
}
// highlightedPos
for (int i = 0; i < other.highlightedPos.length; i++) {
it = other.highlightedPos[i].iterator();
while (it.hasNext()) {
Int2 pos = (Int2) it.next();
highlightedPos[i].add(new Int2(shift + pos.b(), shift + pos.e()));
}
}
// shift
shift += other.shift;
}
}
/**
* Returns all used objects.
*
* @return all used objects
*/
public Set getEObjects() {
return eObject2Positions.keySet();
}
/**
* Shifts text positions used for the mapping and puts the default
* highlight.
*
* @param size
* is the size of the shift
*/
public void shift(int size) {
shift(size, TextModelMapping.HIGHLIGHTED_DEFAULT);
}
/**
* Shifts text positions used for the mapping and puts the given highlight.
*
* @param size
* is the size of the shift
* @param highlightedType
* is the text highlight
*/
public void shift(int size, int highlightedType) {
if (!commit) {
if (linkEObject != null) {
Int2 pos = new Int2(shift, shift + size);
pos2LinkEObject.put(pos, linkEObject);
}
if (highlightedType == TextModelMapping.HIGHLIGHTED_STATIC_TEXT) {
Int2 pos = new Int2(shift, shift + size);
highlightedPos[TextModelMapping.HIGHLIGHTED_STATIC_TEXT].add(pos);
} else if (highlightedType == TextModelMapping.HIGHLIGHTED_COMMENT) {
Int2 pos = new Int2(shift, shift + size);
highlightedPos[TextModelMapping.HIGHLIGHTED_COMMENT].add(pos);
addCommentMapping(object, pos.b(), pos.e());
}
shift += size;
}
}
/**
* Adds a mapping between the model object and the text begins at the
* specified begin and extends to the character at index end - 1.
*
* @param object
* is the model object
* @param begin
* is the beginning index, inclusive
* @param end
* is the ending index, exclusive
*/
protected void addMapping(EObject object, int begin, int end) {
if (begin > -1 && end > -1) {
// Mapping pos2EObject
Int2 newPos = new Int2(begin, end);
if (!pos2EObject.containsKey(newPos)) {
pos2EObject.put(newPos, object);
}
// Mapping eObject2Pos
List objectPositions = (List) eObject2Positions.get(object);
if (objectPositions == null) {
objectPositions = new ArrayList();
eObject2Positions.put(object, objectPositions);
}
add(objectPositions, begin, end);
}
}
/**
* Adds a comment mapping between the model object and the text begins at
* the specified begin and extends to the character at index end - 1.
*
* @param object
* is the model object
* @param begin
* is the beginning index, inclusive
* @param end
* is the ending index, exclusive
*/
protected void addCommentMapping(EObject object, int begin, int end) {
if (begin > -1 && end > -1) {
List commentPositions = (List) eObject2CommentPositions.get(object);
if (commentPositions == null) {
commentPositions = new ArrayList();
eObject2CommentPositions.put(object, commentPositions);
}
add(commentPositions, begin, end);
}
}
/**
* Amalgamates the new position and the other positions.
*
* @param positions
* is the list of positions
* @param begin
* is the beginning index of the new position, inclusive
* @param end
* is the ending index of the new position, exclusive
*/
private void add(List positions, int begin, int end) {
boolean insert = false;
Iterator it = positions.iterator();
while (!insert && it.hasNext()) {
Int2 pos = (Int2) it.next();
if (begin <= pos.e() && begin >= pos.b()) {
if (end > pos.e()) {
positions.remove(pos);
add(positions, pos.b(), end);
}
insert = true;
} else if (end >= pos.b() && end <= pos.e()) {
if (begin < pos.b()) {
positions.remove(pos);
add(positions, begin, pos.e());
}
insert = true;
} else if (begin < pos.b() && end > pos.e()) {
positions.remove(pos);
add(positions, begin, end);
insert = true;
}
}
if (!insert) {
positions.add(new Int2(begin, end));
}
}
/**
* Marks the begin of an open link action.
*
* @param linkEObject
* is the linked object
*/
public void linkBegin(EObject linkEObject) {
this.linkEObject = linkEObject;
}
/**
* Marks the end of the current open link action.
*/
public void linkEnd() {
this.linkEObject = null;
}
/**
* Open link result for the next added text
*/
protected EObject linkEObject = null;
/**
* Validate the changes.
* <p>
* It is impossible to change mappings now.
*/
protected void commit() {
if (!commit) {
commit = true;
addMapping(object, 0, shift);
}
}
/**
* Returns the object at the given index.
*
* @param index
* is the position in the text
* @return the object at the index.
*/
public EObject index2EObject(int index) {
List eObjects = index2EObjects(index);
EObject res = null;
if (eObjects != null && eObjects.size() > 0) {
res = (EObject) eObjects.get(0);
}
return res;
}
/**
* Returns objects at the given index.
*
* @param index
* is the position in the text
* @return objects at the index.
*/
public List index2EObjects(int index) {
List eObjects = (List) index2EObjects.get(new Integer(index));
if (eObjects == null) {
eObjects = new ArrayList();
if (!commit) {
commit();
}
Iterator it = pos2EObject.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
int b = ((Int2) entry.getKey()).b();
if (index >= b) {
int e = ((Int2) entry.getKey()).e();
if (index < e) {
eObjects.add(entry.getValue());
}
}
}
index2EObjects.put(new Integer(index), eObjects);
}
return eObjects;
}
private Map index2EObjects = new HashMap();
/**
* Gets a serializable object that maps the ranges of the text and the URIs
* of the model's objects.
*
* @return a serializable map
* @deprecated
*/
@Deprecated
public Map position2uriSerializableMap() {
Map result = new TreeMap(new InversePosComparator());
if (!commit) {
commit();
}
Iterator it = pos2EObject.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
Int2 pos = (Int2) entry.getKey();
EObject object = (EObject) entry.getValue();
String uriFragment = ETools.getURI(object);
result.put(pos, uriFragment);
}
return result;
}
/**
* Returns the positions of the given object.
*
* @param object
* is the model object
* @return the positions of the object
*/
public Int2[] eObject2Positions(EObject object) {
if (object == null) {
return new Int2[] {};
}
if (!commit) {
commit();
}
List positions = (List) eObject2Positions.get(object);
if (positions != null) {
return (Int2[]) positions.toArray(new Int2[positions.size()]);
} else {
return new Int2[] {};
}
}
/**
* Gets a serializable object that maps the URIs of the model's objects and
* the ranges of the text.
*
* @return a serializable map
* @deprecated
*/
@Deprecated
public Map uri2positionsSerializableMap() {
Map result = new TreeMap(new StringComparator());
if (!commit) {
commit();
}
Iterator entries = eObject2Positions.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
EObject object = (EObject) entry.getKey();
String uriFragment = ETools.getURI(object);
Set positions = new TreeSet(new PosComparator());
positions.addAll((List) entry.getValue());
result.put(uriFragment, positions.toArray(new Int2[positions.size()]));
}
return result;
}
/**
* Returns the first comment position of the given object.
*
* @param object
* is the model object
* @param limits
* delimits the part of the text where the comment can be
* searched
* @return the first comment position of the object
*/
public Int2 eObject2CommentPositionIn(EObject object, Int2 limits) {
if (object == null) {
return Int2.NOT_FOUND;
}
if (!commit) {
commit();
}
List positions = (List) eObject2CommentPositions.get(object);
if (positions != null) {
Iterator it = positions.iterator();
while (it.hasNext()) {
Int2 pos = (Int2) it.next();
if (pos.b() >= limits.b() && pos.e() <= limits.e()) {
return pos;
}
}
}
return Int2.NOT_FOUND;
}
/**
* Returns the linked object at the given index. It is used to make an open
* link action.
*
* @param index
* is the position in the text
* @return the linked object at the index
*/
public EObject index2LinkEObject(int index) {
EObject object = (EObject) index2LinkEObject.get(new Integer(index));
if (object == null) {
if (!commit) {
commit();
}
Iterator it = pos2LinkEObject.entrySet().iterator();
while (object == null && it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
int b = ((Int2) entry.getKey()).b();
if (index >= b) {
int e = ((Int2) entry.getKey()).e();
if (index < e) {
object = (EObject) entry.getValue();
}
}
}
index2LinkEObject.put(new Integer(index), object);
}
return object;
}
private Map index2LinkEObject = new HashMap();
/**
* Returns all positions for the given highlight.
*
* @param highlightedType
* is the text highlight
* @return all positions for the given highlight
*/
public Int2[] getHighlightedPos(int highlightedType) {
if (highlightedType == TextModelMapping.HIGHLIGHTED_STATIC_TEXT) {
return (Int2[]) highlightedPos[TextModelMapping.HIGHLIGHTED_STATIC_TEXT].toArray(new Int2[] {});
} else if (highlightedType == TextModelMapping.HIGHLIGHTED_COMMENT) {
return (Int2[]) highlightedPos[TextModelMapping.HIGHLIGHTED_COMMENT].toArray(new Int2[] {});
} else {
return new Int2[] {};
}
}
/**
* Moves the positions into the given range
*
* @param range
* are the new bounds
*/
public void range(Int2 range) {
if (!commit) {
shift = range.e() - range.b();
// mapping pos2EObject
Iterator it = pos2EObject.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
Int2 pos = (Int2) entry.getKey();
Int2 copy = new Int2(pos.b(), pos.e());
copy.range(range);
if (copy.b() == -1 || (!copy.equals(pos) && pos2EObject.containsKey(copy))) {
it.remove();
} else {
pos.range(range);
}
}
// mapping eObject2Positions
it = eObject2Positions.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
List value = (List) entry.getValue();
Iterator positions = value.iterator();
while (positions.hasNext()) {
Int2 pos = (Int2) positions.next();
pos.range(range);
if (pos.b() == -1) {
positions.remove();
}
}
}
// mapping eObject2CommentPositions
it = eObject2CommentPositions.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
List value = (List) entry.getValue();
Iterator positions = value.iterator();
while (positions.hasNext()) {
Int2 pos = (Int2) positions.next();
pos.range(range);
if (pos.b() == -1) {
positions.remove();
}
}
}
// mapping pos2LinkEObject
it = pos2LinkEObject.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
Int2 pos = (Int2) entry.getKey();
Int2 copy = new Int2(pos.b(), pos.e());
copy.range(range);
if (copy.b() == -1 || (!copy.equals(pos) && pos2LinkEObject.containsKey(copy))) {
it.remove();
} else {
pos.range(range);
}
}
// highlightedPos
for (List highlightedPo : highlightedPos) {
it = highlightedPo.iterator();
while (it.hasNext()) {
Int2 pos = (Int2) it.next();
pos.range(range);
if (pos.b() == -1) {
it.remove();
}
}
}
}
}
/**
* Applies the indent strategy to the positions (each line adds one
* character).
*
* @param lines
* are the positions of the lines
*/
public void indent(Int2[] lines) {
if (!commit) {
shift += lines.length;
// mapping pos2EObject
Iterator it = pos2EObject.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
((Int2) entry.getKey()).indent(lines);
}
// mapping eObject2Positions
it = eObject2Positions.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
List value = (List) entry.getValue();
Iterator positions = value.iterator();
while (positions.hasNext()) {
Int2 pos = (Int2) positions.next();
pos.indent(lines);
}
}
// mapping eObject2CommentPositions
it = eObject2CommentPositions.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
List value = (List) entry.getValue();
Iterator positions = value.iterator();
while (positions.hasNext()) {
Int2 pos = (Int2) positions.next();
pos.indent(lines);
}
}
// mapping pos2LinkEObject
it = pos2LinkEObject.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
((Int2) entry.getKey()).indent(lines);
}
// highlightedPos
for (List highlightedPo : highlightedPos) {
it = highlightedPo.iterator();
while (it.hasNext()) {
Int2 pos = (Int2) it.next();
pos.indent(lines);
}
}
}
}
}