org.eclipse.xtext.linking.lazy.LazyURIEncoder Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2008 itemis AG (http://www.itemis.eu) 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
*
*******************************************************************************/
package org.eclipse.xtext.linking.lazy;
import java.util.List;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.util.Triple;
import org.eclipse.xtext.util.Tuples;
import com.google.inject.Singleton;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@Singleton
public class LazyURIEncoder {
/**
* @since 2.3
*/
public static final String XTEXT_LINK = "xtextLink_";
/**
* @since 2.3
*/
public static final String SEP = "::";
/**
* encodes the given three parameters into a string, so that they can be
* retrieved from a resource using {@link #decode(Resource, String)}
*
* @param obj the feature holder
* @param ref the cross reference
* @param node the node that provided the value for the reference
* @return a portable string that may be used as a {@link URI#fragment() fragment}
*/
public String encode(EObject obj, EReference ref, INode node) {
StringBuilder fragment = new StringBuilder(20).append(XTEXT_LINK).append(SEP);
appendShortFragment(obj, fragment);
fragment.append(SEP);
fragment.append(toShortExternalForm(obj.eClass(), ref)).append(SEP);
getRelativePath(fragment, NodeModelUtils.getNode(obj), node);
return fragment.toString();
}
public void appendShortFragment(EObject obj, StringBuilder target) {
EReference containmentFeature = obj.eContainmentFeature();
if (containmentFeature == null) {
target.append(obj.eResource().getContents().indexOf(obj));
} else {
EObject container = obj.eContainer();
appendShortFragment(container, target);
target.append('.').append(container.eClass().getFeatureID(containmentFeature));
if (containmentFeature.isMany()) {
List> list = (List>) container.eGet(containmentFeature);
target.append('.').append(list.indexOf(obj));
}
}
}
public String toShortExternalForm(EClass clazz, EReference ref) {
return Integer.toString(clazz.getFeatureID(ref));
}
/**
* decodes the uriFragment
*
* @param res the resource that contains the feature holder
* @param uriFragment the fragment that should be decoded
* @return the decoded information
* @see LazyURIEncoder#encode(EObject, EReference, INode)
*/
public Triple decode(Resource res, String uriFragment) {
List split = Strings.split(uriFragment, SEP);
EObject source = resolveShortFragment(res, split.get(1));
EReference ref = fromShortExternalForm(source.eClass(), split.get(2));
INode compositeNode = NodeModelUtils.getNode(source);
if (compositeNode==null)
throw new IllegalStateException("Couldn't resolve lazy link, because no node model is attached.");
INode textNode = getNode(compositeNode, split.get(3));
return Tuples.create(source, ref, textNode);
}
public EObject resolveShortFragment(Resource res, String shortFragment) {
List split = Strings.split(shortFragment, '.');
int contentsIdx = Integer.parseInt(split.get(0));
EObject result = res.getContents().get(contentsIdx);
int splitIdx = 1;
while(splitIdx < split.size()) {
int featureId = Integer.parseInt(split.get(splitIdx++));
EReference reference = (EReference) result.eClass().getEStructuralFeature(featureId);
if (reference.isMany()) {
List> list = (List>) result.eGet(reference);
int listIdx = Integer.parseInt(split.get(splitIdx++));
result = (EObject) list.get(listIdx);
} else {
result = (EObject) result.eGet(reference);
}
}
return result;
}
public EReference fromShortExternalForm(EClass clazz, String shortForm) {
int featureId = Integer.parseInt(shortForm);
return (EReference) clazz.getEStructuralFeature(featureId);
}
/**
* ONLY public to be testable
*/
public void getRelativePath(StringBuilder result, INode parserNode, INode node) {
if (parserNode == node)
return;
if (isAncestor(parserNode, node)) {
ICompositeNode parent = node.getParent();
getRelativePath(result, parserNode, parent);
int idx = 0;
INode child = parent.getFirstChild();
while(child != node && child.hasNextSibling()) {
idx++;
child = child.getNextSibling();
}
result.append("/").append(idx);
} else {
result.append("/..");
getRelativePath(result, parserNode.getParent(), node);
}
}
protected boolean isAncestor(INode parent, INode child) {
for (INode node = child; node != null; node = node.getParent()) {
if (node.equals(parent)) {
return true;
}
}
return false;
}
/**
* @since 2.4
*/
public INode getNode(EObject object, String fragment) {
List split = Strings.split(fragment, LazyURIEncoder.SEP);
INode compositeNode = NodeModelUtils.getNode(object);
if (compositeNode == null)
throw new IllegalStateException("Couldn't resolve lazy link, because no node model is attached.");
INode node = getNode(compositeNode, split.get(3));
return node;
}
/**
* ONLY public to be testable
*/
public INode getNode(final INode node, String path) {
final List split = Strings.split(path, '/');
INode result = node;
for (String string : split) {
String trimmed = string.trim();
if (trimmed.length() > 0) {
if ("..".equals(trimmed)) {
if (result.getParent() == null)
throw new IllegalStateException("node has no parent");
result = result.getParent();
} else {
int index = Integer.parseInt(string);
if (index >= 0) {
INode child = ((ICompositeNode) result).getFirstChild();
while(index > 0) {
child = child.getNextSibling();
index--;
}
result = child;
}
}
}
}
return result;
}
public boolean isCrossLinkFragment(Resource res, String s) {
return s.startsWith(XTEXT_LINK);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy