Skip to content

Commit 9a92a6f

Browse files
authored
Merge pull request #283 from rubenporras/ImproveNameRegistration
Improve name registration
2 parents 54eaefd + fc7b271 commit 9a92a6f

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)