2828import org .eclipse .xtext .scoping .IScope ;
2929
3030import com .avaloq .tools .ddk .xtext .linking .ImportedNamesTypesAdapter .WrappingTypedScope ;
31+ import com .avaloq .tools .ddk .xtext .scoping .AliasingEObjectDescription ;
3132import com .google .common .collect .Sets ;
3233import com .google .inject .Inject ;
3334import 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 ());
0 commit comments