Override or cancel vanilla PsiReference

Answered

Hi guys, i'm facing a reference issue.
I need to reference from this kind of xml structure :

<compound-widgets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns:sc="http://ofbiz.apache.org/Site-Conf"
                  xmlns:wf="http://ofbiz.apache.org/Widget-Form"
                  xmlns:wm="http://ofbiz.apache.org/Widget-Menu"
                  xmlns:ws="http://ofbiz.apache.org/Widget-Screen"
                  xsi:noNamespaceSchemaLocation="../../../../framework/widget/dtd/compound-widgets.xsd">

    <ws:screens>
        <ws:screen name="FooScreen">
            <ws:section>
                <ws:widgets>
                    <ws:include-form name="Foo<caret>Form" location="component://foo/bar/CpdFormReferenceFromCpdScreen.xml"/>
                </ws:widgets>
            </ws:section>
        </ws:screen>
    </ws:screens>

    <wf:forms>
        <wf:form name="FooForm">
        </wf:form>
    </wf:forms>
</compound-widgets>

I thought the element at caret position (next to include-form tag) would not have reference at all from vanilla IDEA, so that i could use my own reference class.
But it appears that there is a reference found, with class 
`com.intellij.psi.impl.source.resolve.reference.impl.providers.IdReferenceProvider$GlobalAttributeValueSelfReference@58d305c`
And i'd like to somehow overwite this reference and get my own, is there a way for doing that ?
Or is it that my patterns or dom description that are not correct ? 

class CompoundFileDescription<S extends DomElement> extends DomFileDescription<CompoundFile> {
    private static final String rootTagName = "compound-widgets"

    public static final String SITE_CONF_NS_URL = 'http://ofbiz.apache.org/Site-Conf'
    public static final String SITE_CONF_NS = 'sc'

    public static final String FORM_NS_URL = 'http://ofbiz.apache.org/Widget-Form'
    public static final String FORM_NS = 'wf'

    public static final String MENU_NS_URL = 'http://ofbiz.apache.org/Widget-Menu'
    public static final String MENU_NS = 'wm'

    public static final String SCREEN_NS_URL = 'http://ofbiz.apache.org/Widget-Screen'
    public static final String SCREEN_NS = 'ws'

    CompoundFileDescription() { super(CompoundFile.class, rootTagName) }

    @Override
    protected void initializeFileDescription() {
        registerNamespacePolicy(SITE_CONF_NS_URL, SITE_CONF_NS)
        registerNamespacePolicy(FORM_NS_URL, FORM_NS)
        registerNamespacePolicy(MENU_NS_URL, MENU_NS)
        registerNamespacePolicy(SCREEN_NS_URL, SCREEN_NS)
    }
}
  public static final XmlNamedElementPattern.XmlAttributePattern FORM_CALL = XmlPatterns.xmlAttribute().andOr(
            XmlPatterns.xmlAttribute()
                    .withParent(XmlPatterns.xmlTag().withName("include-form")
                            .withNamespace(CompoundFileDescription.FORM_NS))
                    .withName("name"),
    //...
  )

Thanks in advance for any help

8 comments
Comment actions Permalink

Hi Gaëtan,

What issue does the existing reference cause? Why can't you have both?

0
Comment actions Permalink

My problem is that i'm currently working with TDD, and i'm not getting the right type of reference.
Here is my test code :

void testCpdFormReferenceFromCpdScreen() {
    String file = 'CpdFormReferenceFromCpdScreen.xml'
    myFixture.configureByFile(file)
    PsiReference ref = myFixture.getReferenceAtCaretPositionWithAssertion(file)
    assertEquals 'FooForm', ref.getElement().getName() as String
    assert ref instanceof FormReference
    assertNotNull ref.resolve()
}

I also added the order first to my EP declaration, but it didn't change anything

<psi.referenceContributor order="first" language="XML" implementation="fr.nereide.reference.contributor.XmlReferenceContributor"/>

But from what i understand i should have two different references ? (or PsiMultiRef ?)

0
Comment actions Permalink

I suggest trying something like:

PsiReference[] references = myFixture.getElementAtCaret().getReferences();
// find your reference in 'references' by your reference type (instanceof)
0
Comment actions Permalink

Thanks for this. I now know the problem is elsewhere (i don't know where yet) :

0
Comment actions Permalink

I suspect you add your reference in the higher level of the syntax tree, so PsiTreeUtil.getParentOfType(elementAtCaret, YourElementWithReferenceType.class) should do the job.

1
Comment actions Permalink

Hi again Karol, and thanks for your patience. I fixed the namespacing in the DomFileDescription wich was inverted and double checked the pattern.

        public static final XmlNamedElementPattern.XmlAttributePattern FORM_CALL = XmlPatterns.xmlAttribute().andOr(
                XmlPatterns.xmlAttribute()
                        .withParent(XmlPatterns.xmlTag().withName("include-form")
                                .withNamespace(CompoundFileDescription.SCREEN_NS))
                        .withName("name"),

It turns out that the XmlAttributeValue is called when registering the reference provider (i dit come copy-paste in the first days of this project).

        registrar.registerReferenceProvider(XmlPatterns.xmlAttributeValue()
                .withParent(OfbizPatterns.XML.FORM_CALL), new FormReferenceProvider()
        )

AFAIU the pattern sould a least trigger, but it doesn't. I obiously missed something, because there is still not my custom reference but i don't see it :
- patterns looks okay
- DomDescription looks ok (Even if i don't think it plays much of a role in references before resole gets called)
- Namespacing looks okay

0
Comment actions Permalink

Hi Gaëtan,
It’s hard to tell what can be wrong just by looking at these snippets.
I would change the pattern to match the xmlAttributeValue().inside(...). Also, make sure that withParent() is actually correct - it checks for direct parents, and there can be some intermediate node in the tree.
I also suggest setting a breakpoint in com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry#getReferencesFromProviders(PsiElement, PsiReferenceService.Hints) and checking what happens and why your reference is not provided.

1
Comment actions Permalink

Hi Karol, i made it work. The pattern wasn't ok after all. Turns out i had to specify the Namespace in the pattern like this :

                xmlAttributeValue().inside(
                        xmlAttribute()
                                .withName("name")
                                .inside(xmlTag()
                                        .withName("${SCREEN_NS}:include-form")
                                        .withNamespace(SCREEN_NS_URL)
                                ),
                )

I didn't thought it would be needed but it is.

Thanks again !

0

Please sign in to leave a comment.