Skip to content

Commit fc7b271

Browse files
committed
Use the name of descriptions which came from the index
Until now to register the names of the linked objects the LinkingService always looked for the IEObjectDescription within the eResource of the linked object, then used the qualified name of this description as the name to register. This is very inefficient, as for each linked object, an object descriptor is looked up again within the resource by comparing the URI's fragment of the object with all the URI's fragments of all objects within the given resource (until one match is found). The linear search is very slow because URI#getFragment is an expensive operation, it will convert the internal representation of the fragment into a String, then put the string into an EMF String pool, and from them use Sring#intern before returning the String. As it happens, for objects which are linked using the index (the Proxy objects), the descriptor that scope#getSingleElement() returns has already the exported name of the object as its name, so we can take its names without looking again for a descriptors from where to extract the name. For objects which are not a Proxy, the returned descriptor is not the one exported to the index, just a descriptor created on the fly for scoping, and its name is unrelated to the one exported. In this case we still must fallback to lookup up the resource again by comparing the URI's fragment.
1 parent 4d63159 commit fc7b271

File tree

2 files changed

+133
-60
lines changed

2 files changed

+133
-60
lines changed

com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/linking/LinkingService.java

Lines changed: 119 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.eclipse.xtext.scoping.IScope;
2929

3030
import com.avaloq.tools.ddk.xtext.linking.ImportedNamesTypesAdapter.WrappingTypedScope;
31+
import com.avaloq.tools.ddk.xtext.scoping.AliasingEObjectDescription;
3132
import com.google.common.collect.Sets;
3233
import com.google.inject.Inject;
3334
import com.google.inject.Provider;
@@ -60,100 +61,161 @@ public class LinkingService extends DefaultLinkingService {
6061
@Inject
6162
private Provider<ImportedNamesTypesAdapter> importedNamesAdapterProvider;
6263

64+
private void registerNamedType(final EObject context, final QualifiedName name, final EClass type) {
65+
ImportedNamesTypesAdapter adapter = getImportedNamesAdapter(context);
66+
adapter.getImportedNames().add(name);
67+
if (adapter.getImportedNamesTypes().containsKey(name)) {
68+
adapter.getImportedNamesTypes().get(name).add(type);
69+
} else {
70+
adapter.getImportedNamesTypes().put(name, Sets.newHashSet(type));
71+
}
72+
}
73+
6374
/**
6475
* Register a name as imported.
6576
*
6677
* @param context
67-
* context object within which a reference is being set to some object using "name" for the lookup, must not be {@code null}
68-
* @param name
69-
* the lookup name, may be {@code null}
78+
* context object within which a reference is being set to some object, must not be {@code null}
79+
* @param target
80+
* the associated target eObject. Its exported name is recorded as imported, must not be {@code null}
7081
* @param type
7182
* the lookup type, may be {@code null}
72-
* @param target
73-
* the associated target eObject. If not {@code null} then all names for this eObject are also recorded
74-
* as imported, provided they're not the same as the {@code name} parameter. May be {@code null}
7583
*/
76-
public void importObject(final EObject context, final String name, final EObject target, final EClass type) {
77-
// import exported name for resolved references
78-
ImportedNamesTypesAdapter adapter = getImportedNamesAdapter(context);
79-
if (target != null) {
80-
QualifiedName targetName = null;
81-
final IResourceDescriptions resourceDescriptions = provider.getResourceDescriptions(context.eResource());
82-
Iterator<IEObjectDescription> exports = resourceDescriptions.getExportedObjectsByObject(target).iterator();
83-
if (exports.hasNext()) {
84-
targetName = exports.next().getName();
85-
if (targetName != null && !targetName.isEmpty()) {
86-
final QualifiedName lowerCaseName = targetName.toLowerCase();// NOPMD targetName not a String!
87-
adapter.getImportedNames().add(lowerCaseName);
88-
if (adapter.getImportedNamesTypes().containsKey(lowerCaseName)) {
89-
adapter.getImportedNamesTypes().get(lowerCaseName).add(type);
90-
} else {
91-
adapter.getImportedNamesTypes().put(lowerCaseName, Sets.newHashSet(type));
92-
}
93-
94-
}
95-
}
96-
} else if (name != null && name.length() > 0) { // import parsed string for unresolved references
97-
QualifiedName unresolvedName = crossRefHelper.toUnresolvedReferenceName(name);
98-
adapter.getImportedNames().add(unresolvedName);
99-
if (adapter.getImportedNamesTypes().containsKey(unresolvedName)) {
100-
adapter.getImportedNamesTypes().get(unresolvedName).add(type);
101-
} else {
102-
adapter.getImportedNamesTypes().put(unresolvedName, Sets.newHashSet(type));
84+
public void importObject(final EObject context, final EObject target, final EClass type) {
85+
final IResourceDescriptions resourceDescriptions = provider.getResourceDescriptions(context.eResource());
86+
Iterator<IEObjectDescription> exports = resourceDescriptions.getExportedObjectsByObject(target).iterator();
87+
if (exports.hasNext()) {
88+
QualifiedName targetName = exports.next().getName();
89+
if (targetName != null && !targetName.isEmpty()) {
90+
registerNamedType(context, targetName.toLowerCase(), type); // NOPMD targetName not a String!
10391
}
10492
}
10593
}
10694

95+
/**
96+
* Register a name as imported.
97+
*
98+
* @param context
99+
* context object within which a reference is being set to some object, must not be {@code null}
100+
* @param desc
101+
* the associated description. Its name is recorded as imported, must not be {@code null}
102+
* @param type
103+
* the lookup type, may be {@code null}
104+
*/
105+
public void importObject(final EObject context, final IEObjectDescription desc, final EClass type) {
106+
QualifiedName targetName;
107+
if (desc instanceof AliasingEObjectDescription) {
108+
targetName = ((AliasingEObjectDescription) desc).getOriginalName();
109+
} else {
110+
targetName = desc.getName();
111+
}
112+
if (targetName != null && !targetName.isEmpty()) {
113+
registerNamedType(context, targetName.toLowerCase(), type); // NOPMD targetName not a String!
114+
}
115+
}
116+
117+
/**
118+
* Register a typed unresolved reference.
119+
*
120+
* @param context
121+
* context object within which a reference is being set to unresolved, must not be {@code null}
122+
* @param name
123+
* the lookup name, may be {@code null}
124+
* @param type
125+
* the lookup type, may be {@code null}
126+
*/
127+
public void registerUnresolvedReference(final EObject context, final String name, final EClass type) {
128+
registerNamedType(context, crossRefHelper.toUnresolvedReferenceName(name), type);
129+
}
130+
107131
/**
108132
* Check whether a name import should be added for the given cross-reference.
109133
* <p>
110-
* This default implementation will return {@code true} in either of the following cases:
111-
* <ul>
112-
* <li>the target is {@code null} and the reference is <em>not</em> {@link ICrossReferenceHelper#isOptionalReference(EObject, EReference, INode) optional}
113-
* </li>
114-
* <li>the reference is <em>not</em> {@code null} and should be {@link ICrossReferenceHelper#exportReference(EObject, EReference, EObject) exported}</li>
115-
* </ul>
134+
* This default implementation will return {@code true} if the reference should be {@link ICrossReferenceHelper#exportReference(EObject, EReference, EObject)}
135+
* exported
116136
*
117137
* @param context
118138
* The context of the reference
119139
* @param ref
120140
* The meta model cross-reference
121141
* @param target
122-
* The target object of the reference; may also be {@code null}
142+
* The target object of the reference; may be {@code null}
143+
* @return {@code true} if the object is imported
144+
*/
145+
protected boolean isImportRequired(final EObject context, final EReference ref, final EObject target) {
146+
return crossRefHelper.exportReference(context, ref, target);
147+
}
148+
149+
/**
150+
* Check whether the name of the unresolved reference should be registered.
151+
* <p>
152+
* This default implementation will return {@code true} if the reference is <em>not</em>
153+
* {@link ICrossReferenceHelper#isOptionalReference(EObject, EReference, INode)} optional
154+
*
155+
* @param context
156+
* The context of the reference
157+
* @param ref
158+
* The meta model cross-reference
123159
* @param node
124160
* The parse tree node corresponding to the cross-reference
125-
* @return {@code true} if the object is imported or unresolved
161+
* @return {@code true} if the unresolved references should be registered
126162
*/
127-
protected boolean isImportRequired(final EObject context, final EReference ref, final EObject target, final INode node) {
128-
// TODO never import names for optional references? whether they resolve or not?
129-
return (target == null && !crossRefHelper.isOptionalReference(context, ref, node))
130-
|| (target != null && crossRefHelper.exportReference(context, ref, target));
163+
protected boolean doRegisterUnresolvedReference(final EObject context, final EReference ref, final INode node) {
164+
return !crossRefHelper.isOptionalReference(context, ref, node);
165+
}
166+
167+
/**
168+
* Gets the actual element or a proxy.
169+
* <p>
170+
* This default implementation will return just the {@code the actual element or a proxy} of the given candidate,
171+
* languages with more complex logic (e.g. overloading) can override this method.
172+
*
173+
* @param context
174+
* The context of the reference
175+
* @param desc
176+
* The descriptor, never {@code null}
177+
* @param ref
178+
* The meta model cross-reference, never {@code null}
179+
* @return the actual element or a proxy
180+
*/
181+
protected EObject getEObjectOrProxy(final EObject context, final IEObjectDescription desc, final EReference ref) {
182+
return desc.getEObjectOrProxy();
131183
}
132184

133-
/** {@inheritDoc} */
134185
@Override
135186
public List<EObject> getLinkedObjects(final EObject context, final EReference ref, final INode node) {
136187
final EClass requiredType = ref.getEReferenceType();
137188
if (requiredType == null) {
138189
return Collections.emptyList();
139190
}
140191

141-
final String s = getCrossRefNodeAsString(node);
142-
if (s != null && s.length() > 0) {
143-
QualifiedName qualifiedLinkName = qualifiedNameConverter.toQualifiedName(s);
144-
final EObject target = getSingleElement(context, ref, qualifiedLinkName);
145-
if (isImportRequired(context, ref, target, node)) {
146-
importObject(context, s, target, requiredType);
147-
}
192+
final String linkName = getCrossRefNodeAsString(node);
193+
if (linkName != null && !linkName.isEmpty()) {
194+
final QualifiedName qualifiedLinkName = qualifiedNameConverter.toQualifiedName(linkName);
195+
final IEObjectDescription desc = getSingleElement(context, ref, qualifiedLinkName);
196+
final EObject target = desc == null ? null : getEObjectOrProxy(context, desc, ref);
197+
148198
if (target != null) {
199+
if (isImportRequired(context, ref, target)) {
200+
if (target.eIsProxy()) {
201+
importObject(context, desc, ref.getEReferenceType());
202+
} else {
203+
importObject(context, target, ref.getEReferenceType());
204+
}
205+
}
149206
return Collections.singletonList(target);
150207
}
208+
209+
if (doRegisterUnresolvedReference(context, ref, node)) {
210+
registerUnresolvedReference(context, linkName, requiredType);
211+
}
151212
}
152213
return Collections.emptyList();
214+
153215
}
154216

155217
/**
156-
* Gets an {@link EObject} that best matches a given context, reference, and qualified name.
218+
* Gets an {@link IEObjectDescription} that best matches a given context, reference, and qualified name.
157219
* <p>
158220
* The concept of a "best match" is "in the eye of the beholder", that is, the context and reference. The default case is the first element returned from the
159221
* scope for the given context and reference that matches the given qualified name.
@@ -165,23 +227,20 @@ public List<EObject> getLinkedObjects(final EObject context, final EReference re
165227
* the reference for which the result element should be suitable, must not be {@code null}
166228
* @param qualifiedLinkName
167229
* the name that the result element should match, must not be {@code null}
168-
* @return an eObject that best matches {@code qualifiedLinkName} in {@code scope} given the {@code context} and {@code reference},
230+
* @return an IEObjectDescription that best matches {@code qualifiedLinkName} in {@code scope} given the {@code context} and {@code reference},
169231
* may by {@code null}
170232
*/
171-
protected EObject getSingleElement(final EObject context, final EReference reference, final QualifiedName qualifiedLinkName) {
233+
protected IEObjectDescription getSingleElement(final EObject context, final EReference reference, final QualifiedName qualifiedLinkName) {
172234
IEObjectDescription desc = null;
173235
IScope scope = getScope(context, reference);
174236
if (scope instanceof WrappingTypedScope) {
175237
desc = ((WrappingTypedScope) scope).getSingleElement(qualifiedLinkName, reference);
176238
} else {
177239
desc = scope.getSingleElement(qualifiedLinkName);
178240
}
179-
return desc == null ? null : desc.getEObjectOrProxy();
241+
return desc;
180242
}
181243

182-
/**
183-
* {@inheritDoc}
184-
*/
185244
@Override
186245
protected ImportedNamesTypesAdapter getImportedNamesAdapter(final EObject context) {
187246
ImportedNamesAdapter adapter = ImportedNamesAdapter.find(context.eResource());

com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/scoping/AliasingEObjectDescription.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ public AliasingEObjectDescription(final QualifiedName name, final IEObjectDescri
4242
this.alias = name;
4343
}
4444

45+
/**
46+
* @return the original name, this element can be accessed by.
47+
*/
48+
public QualifiedName getOriginalName() {
49+
return delegate.getName();
50+
}
51+
52+
/**
53+
* @return the original qualified name of the element.
54+
*/
55+
public QualifiedName getOriginalQualifiedName() {
56+
return delegate.getQualifiedName();
57+
}
58+
4559
/** {@inheritDoc} */
4660
@Override
4761
public QualifiedName getName() {

0 commit comments

Comments
 (0)