[linking] story lazy proxy information in a lits rather then serializing it to a string. Use a simple index as the actual fragment pointing to the entry in the list. (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=378088)

Change-Id: I04fdcc1c06c5ca484ef29ce7ce1f3db38f1d466f
This commit is contained in:
Sven Efftinge 2014-06-20 14:31:16 +02:00
parent c0c958d7d4
commit c58636c1d6
8 changed files with 210 additions and 12 deletions

View file

@ -312,4 +312,13 @@
</message_arguments>
</filter>
</resource>
<resource path="src/org/eclipse/xtext/linking/lazy/LazyURIEncoder.java" type="org.eclipse.xtext.linking.lazy.LazyURIEncoder">
<filter id="388194388">
<message_arguments>
<message_argument value="org.eclipse.xtext.linking.lazy.LazyURIEncoder"/>
<message_argument value="XTEXT_LINK"/>
<message_argument value="xtextLink_"/>
</message_arguments>
</filter>
</resource>
</component>

View file

@ -11,12 +11,16 @@ package org.eclipse.xtext.linking.impl;
import java.util.Iterator;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.AbstractTreeIterator;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.diagnostics.IDiagnosticConsumer;
import org.eclipse.xtext.linking.lazy.LazyLinkingResource;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.util.internal.Stopwatches;
@ -62,19 +66,46 @@ public abstract class AbstractCleaningLinker extends AbstractLinker {
protected abstract void doLinkModel(EObject model, IDiagnosticConsumer diagnosticsConsumer);
protected void beforeModelLinked(EObject model, IDiagnosticConsumer diagnosticsConsumer) {
clearAllReferences(model);
ImportedNamesAdapter adapter = ImportedNamesAdapter.find(model.eResource());
Resource resource = model.eResource();
if (resource instanceof LazyLinkingResource) {
((LazyLinkingResource) resource).clearLazyProxyInformation();
}
ImportedNamesAdapter adapter = ImportedNamesAdapter.find(resource);
if (adapter!=null)
adapter.clear();
}
/**
* @since 2.7
*/
protected boolean isClearAllReferencesRequired(Resource resource) {
return true;
}
/**
* @deprecated no longer called, override {@link #clearReferences(EObject)} instead
*/
@Deprecated
protected void clearAllReferences(EObject model) {
clearReferences(model);
final Iterator<EObject> iter = model.eAllContents();
final Iterator<EObject> iter = getAllLinkableContents(model);
while (iter.hasNext())
clearReferences(iter.next());
}
/**
* @since 2.7
*/
@SuppressWarnings("serial")
protected TreeIterator<EObject> getAllLinkableContents(EObject model) {
return new AbstractTreeIterator<EObject>(model) {
@Override
public Iterator<EObject> getChildren(Object object) {
return ((EObject) object).eContents().iterator();
}
};
}
protected void clearReferences(EObject obj) {
for (EReference ref : obj.eClass().getEAllReferences())
clearReference(obj, ref);

View file

@ -90,10 +90,13 @@ public class LazyLinker extends AbstractCleaningLinker {
cache.execWithoutCacheClear(model.eResource(), new IUnitOfWork.Void<Resource>() {
@Override
public void process(Resource state) throws Exception {
installProxies(model, producer, settingsToLink);
TreeIterator<EObject> iterator = model.eAllContents();
TreeIterator<EObject> iterator = getAllLinkableContents(model);
boolean clearAllReferencesRequired = isClearAllReferencesRequired(state);
while (iterator.hasNext()) {
EObject eObject = iterator.next();
if (clearAllReferencesRequired) {
clearReferences(eObject);
}
installProxies(eObject, producer, settingsToLink);
}
}
@ -131,6 +134,7 @@ public class LazyLinker extends AbstractCleaningLinker {
settingsToLink.put(new SettingDelegate(setting), node);
} else {
createAndSetProxy(obj, node, eRef);
afterCreateAndSetProxy(obj, node, eRef, crossReference, producer);
}
}
}
@ -139,6 +143,13 @@ public class LazyLinker extends AbstractCleaningLinker {
}
}
/**
* @since 2.7
*/
protected void afterCreateAndSetProxy(EObject obj, INode node, EReference eRef, CrossReference crossReference,
IDiagnosticProducer producer) {
}
/**
* @since 2.4
*/
@ -182,13 +193,21 @@ public class LazyLinker extends AbstractCleaningLinker {
throw new IllegalStateException("object must be contained in a resource");
final URI uri = resource.getURI();
final URI encodedLink = uri.appendFragment(encoder.encode(obj, eRef, node));
EClass referenceType = ecoreGenericsUtil.getReferenceType(eRef, obj.eClass());
EClass instantiableType = instantiableSubTypes.get(referenceType);
final EObject proxy = EcoreUtil.create(instantiableType);
EClass referenceType = getProxyType(obj, eRef);
final EObject proxy = EcoreUtil.create(referenceType);
((InternalEObject) proxy).eSetProxyURI(encodedLink);
return proxy;
}
/**
* @since 2.7
*/
protected EClass getProxyType(EObject obj, EReference eRef) {
EClass referenceType = ecoreGenericsUtil.getReferenceType(eRef, obj.eClass());
EClass instantiableType = instantiableSubTypes.get(referenceType);
return instantiableType;
}
protected EClass findInstantiableCompatible(EClass eType) {
if (!isInstantiatableSubType(eType, eType)) {
// check local Package

View file

@ -8,8 +8,11 @@
*******************************************************************************/
package org.eclipse.xtext.linking.lazy;
import static com.google.common.collect.Lists.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
@ -40,6 +43,7 @@ import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.util.CancelIndicator;
import org.eclipse.xtext.util.Triple;
import org.eclipse.xtext.util.Tuples;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
@ -450,4 +454,47 @@ public class LazyLinkingResource extends XtextResource {
});
return unresolveableProxies;
}
private ArrayList<Triple<EObject, EReference, INode>> proxyInformation = newArrayList();
/**
* @since 2.7
*/
public int addLazyProxyInformation(EObject obj, EReference ref, INode node) {
int index = proxyInformation.size();
proxyInformation.add(Tuples.create(obj, ref, node));
return index;
}
/**
* @since 2.7
*/
public boolean hasLazyProxyInformation(int idx) {
return proxyInformation.get(idx) != null;
}
/**
* @since 2.7
*/
public Triple<EObject,EReference,INode> getLazyProxyInformation(int idx) {
if (!hasLazyProxyInformation(idx)) {
throw new IllegalArgumentException("No proxy information for index '"+idx+"' available.");
}
return proxyInformation.get(idx);
}
/**
* @since 2.7
*/
public Triple<EObject,EReference,INode> removeLazyProxyInformation(int idx) {
return proxyInformation.set(idx, null);
}
/**
* @since 2.7
*/
public void clearLazyProxyInformation() {
proxyInformation = newArrayListWithCapacity(proxyInformation.size());
}
}

View file

@ -22,7 +22,9 @@ import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.util.Triple;
import org.eclipse.xtext.util.Tuples;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
/**
* @author Sven Efftinge - Initial contribution and API
@ -32,12 +34,32 @@ public class LazyURIEncoder {
/**
* @since 2.3
*
* deprecated use {@link #isCrossLinkFragment(Resource, String)} instead
*
* @noreference This field is not intended to be referenced by clients.
*/
public static final String XTEXT_LINK = "xtextLink_";
public static final String XTEXT_LINK = "|";
/**
* @since 2.3
*
*/
public static final String SEP = "::";
/**
* @since 2.7
*/
public final static String USE_INDEXED_FRAGMENTS_BINDING = "org.eclipse.xtext.linking.lazy.LazyURIEncoder.isUseIndexFragment";
@Inject(optional=true) @Named(value=USE_INDEXED_FRAGMENTS_BINDING)
private boolean isUseIndexFragment = false;
/**
* @since 2.7
*/
public boolean isUseIndexFragment(Resource context) {
return isUseIndexFragment && context instanceof LazyLinkingResource;
}
/**
* encodes the given three parameters into a string, so that they can be
@ -49,7 +71,10 @@ public class LazyURIEncoder {
* @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);
if (isUseIndexFragment(obj.eResource())) {
return getIndexFragment(obj, ref, node);
}
StringBuilder fragment = new StringBuilder(4).append(XTEXT_LINK).append(SEP);
appendShortFragment(obj, fragment);
fragment.append(SEP);
fragment.append(toShortExternalForm(obj.eClass(), ref)).append(SEP);
@ -57,6 +82,19 @@ public class LazyURIEncoder {
return fragment.toString();
}
/**
* @since 2.7
*/
protected String getIndexFragment(EObject obj, EReference ref, INode node) {
Resource resource = obj.eResource();
if (!(resource instanceof LazyLinkingResource)) {
throw new IllegalStateException("Context object must be contained in a LazyLinkingResource : "+obj.eResource());
}
LazyLinkingResource lazyResource = (LazyLinkingResource) resource;
int idx = lazyResource.addLazyProxyInformation(obj,ref,node);
return XTEXT_LINK + idx;
}
public void appendShortFragment(EObject obj, StringBuilder target) {
EReference containmentFeature = obj.eContainmentFeature();
if (containmentFeature == null) {
@ -85,6 +123,9 @@ public class LazyURIEncoder {
* @see LazyURIEncoder#encode(EObject, EReference, INode)
*/
public Triple<EObject, EReference, INode> decode(Resource res, String uriFragment) {
if (isUseIndexFragment(res)) {
return getLazyProxyInformation(res, uriFragment);
}
List<String> split = Strings.split(uriFragment, SEP);
EObject source = resolveShortFragment(res, split.get(1));
EReference ref = fromShortExternalForm(source.eClass(), split.get(2));
@ -95,6 +136,32 @@ public class LazyURIEncoder {
return Tuples.create(source, ref, textNode);
}
/**
* @since 2.7
*/
protected Triple<EObject, EReference, INode> getLazyProxyInformation(Resource res, String uriFragment) {
if (!(res instanceof LazyLinkingResource)) {
throw new IllegalArgumentException("Given resource not a LazyLinkingResource");
}
int idx = getIndex(uriFragment);
LazyLinkingResource lazyResource = (LazyLinkingResource) res;
return lazyResource.getLazyProxyInformation(idx);
}
/**
* @since 2.7
*/
public int getIndex(String uriFragment) {
int idx = -1;
try {
String string = uriFragment.substring(XTEXT_LINK.length());
idx = Integer.parseInt(string);
} catch (RuntimeException e) {
throw new IllegalArgumentException("Couldn't parse index from fragment '"+uriFragment+"'", e);
}
return idx;
}
public EObject resolveShortFragment(Resource res, String shortFragment) {
List<String> split = Strings.split(shortFragment, '.');
int contentsIdx = Integer.parseInt(split.get(0));
@ -121,6 +188,8 @@ public class LazyURIEncoder {
/**
* ONLY public to be testable
*
* @noreference This method is not intended to be referenced by clients.
*/
public void getRelativePath(StringBuilder result, INode parserNode, INode node) {
if (parserNode == node)
@ -154,10 +223,13 @@ public class LazyURIEncoder {
* @since 2.4
*/
public INode getNode(EObject object, String fragment) {
List<String> split = Strings.split(fragment, LazyURIEncoder.SEP);
if (isUseIndexFragment(object.eResource())) {
return decode(object.eResource(), fragment).getThird();
}
INode compositeNode = NodeModelUtils.getNode(object);
if (compositeNode == null)
throw new IllegalStateException("Couldn't resolve lazy link, because no node model is attached.");
List<String> split = Strings.split(fragment, LazyURIEncoder.SEP);
INode node = getNode(compositeNode, split.get(3));
return node;
}

View file

@ -21,6 +21,7 @@ import org.eclipse.xtext.linking.LinkingScopeProviderBinding;
import org.eclipse.xtext.linking.impl.DefaultLinkingService;
import org.eclipse.xtext.linking.lazy.LazyLinker;
import org.eclipse.xtext.linking.lazy.LazyLinkingResource;
import org.eclipse.xtext.linking.lazy.LazyURIEncoder;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.naming.SimpleNameProvider;
import org.eclipse.xtext.parser.IEncodingProvider;
@ -230,5 +231,10 @@ public abstract class DefaultRuntimeModule extends AbstractGenericModule {
binder.bind(ISemanticSequencer.class).annotatedWith(GenericSequencer.class).to(BacktrackingSemanticSequencer.class);
}
/**
* @since 2.7
*/
public void configureUseIndexFragmentsForLazyLinking(com.google.inject.Binder binder) {
binder.bind(Boolean.TYPE).annotatedWith(Names.named(LazyURIEncoder.USE_INDEXED_FRAGMENTS_BINDING)).toInstance(Boolean.TRUE);
}
}

View file

@ -23,4 +23,9 @@ public class LazyLinkingTestLanguageRuntimeModule extends org.eclipse.xtext.link
public void configureIScopeProviderDelegate(Binder binder) {
binder.bind(IScopeProvider.class).annotatedWith(Names.named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(ImportedNamespaceAwareLocalScopeProvider.class);
}
@Override
public void configureUseIndexFragmentsForLazyLinking(Binder binder) {
binder.bind(Boolean.TYPE).annotatedWith(Names.named(LazyURIEncoder.USE_INDEXED_FRAGMENTS_BINDING)).toInstance(Boolean.FALSE);
}
}

View file

@ -3,9 +3,18 @@
*/
package org.eclipse.xtext.parser.bug419429;
import org.eclipse.xtext.linking.lazy.LazyURIEncoder;
import com.google.inject.Binder;
import com.google.inject.name.Names;
/**
* Use this class to register components to be used at runtime / without the Equinox extension registry.
*/
public class Bug419429RuntimeModule extends org.eclipse.xtext.parser.bug419429.AbstractBug419429RuntimeModule {
@Override
public void configureUseIndexFragmentsForLazyLinking(Binder binder) {
binder.bind(Boolean.TYPE).annotatedWith(Names.named(LazyURIEncoder.USE_INDEXED_FRAGMENTS_BINDING)).toInstance(Boolean.FALSE);
}
}