/* * Copyright 2011 The authors * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.struts2.dom.params.custom; import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.references.PomService; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.psi.PsiReferenceBase; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.struts2.dom.params.ParamNameConverter; import com.intellij.struts2.dom.struts.strutspackage.InterceptorOrStackBase; import com.intellij.struts2.dom.struts.strutspackage.InterceptorRef; import com.intellij.struts2.dom.struts.strutspackage.InterceptorStack; import com.intellij.struts2.reference.common.BeanPropertyPathReferenceSet; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomTarget; import com.intellij.util.xml.ElementPresentationManager; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Resolves to {@code interceptorName.interceptorProperty} when referencing {@link InterceptorStack}. * * @author Yann Cébron */ public class InterceptorRefInStackParamNameCustomConverter extends ParamNameConverter.ParamNameCustomConverter { @NotNull @Override public PsiReference[] getCustomReferences(final XmlAttributeValue nameAttributeValue, final DomElement paramsElement) { if (!(paramsElement instanceof InterceptorRef)) { return PsiReference.EMPTY_ARRAY; } InterceptorRef interceptorRef = (InterceptorRef) paramsElement; final InterceptorOrStackBase value = interceptorRef.getName().getValue(); if (!(value instanceof InterceptorStack)) { return PsiReference.EMPTY_ARRAY; } final String text = nameAttributeValue.getValue(); final boolean hasDot = StringUtil.containsChar(text, '.'); final int idx = hasDot ? text.indexOf('.') : text.length(); final String refName = text.substring(0, idx); final InterceptorStack stack = (InterceptorStack) value; final InterceptorRef resolvedInterceptorRef = ContainerUtil.find(stack.getInterceptorRefs(), ref -> Comparing.strEqual(refName, ref.getName().getStringValue())); final List<PsiReference> customReferences = new ArrayList<>(2); customReferences.add(new InterceptorRefPsiReference(nameAttributeValue, TextRange.from(1, idx), resolvedInterceptorRef, stack)); if (resolvedInterceptorRef == null) { return ArrayUtil.toObjectArray(customReferences, PsiReference.class); } final String propertyText = text.substring(idx + 1, text.length()); final PsiClass paramsClass = resolvedInterceptorRef.getParamsClass(); final BeanPropertyPathReferenceSet beanPropertyPathReferenceSet = new BeanPropertyPathReferenceSet(propertyText, nameAttributeValue, idx + 2, '.', paramsClass, false); Collections.addAll(customReferences, beanPropertyPathReferenceSet.getPsiReferences()); return ArrayUtil.toObjectArray(customReferences, PsiReference.class); } private static class InterceptorRefPsiReference extends PsiReferenceBase<PsiElement> implements EmptyResolveMessageProvider { private final InterceptorRef resolvedInterceptorRef; private final InterceptorStack interceptorStack; private InterceptorRefPsiReference(final XmlAttributeValue psiElement, final TextRange textRange, final InterceptorRef resolvedInterceptorRef, final InterceptorStack interceptorStack) { super(psiElement, textRange, true); this.resolvedInterceptorRef = resolvedInterceptorRef; this.interceptorStack = interceptorStack; } @Override public PsiElement resolve() { if (resolvedInterceptorRef == null) { return null; } final InterceptorOrStackBase interceptorDeclaration = resolvedInterceptorRef.getName().getValue(); if (interceptorDeclaration == null) { return null; } final DomTarget domTarget = DomTarget.getTarget(interceptorDeclaration); return domTarget == null ? null : PomService.convertToPsi(domTarget); } @NotNull @Override public Object[] getVariants() { final List<InterceptorRef> allInterceptorRefs = interceptorStack.getInterceptorRefs(); List<LookupElement> names = new ArrayList<>(allInterceptorRefs.size()); for (InterceptorRef interceptorRef : allInterceptorRefs) { final InterceptorOrStackBase resolvedInterceptor = interceptorRef.getName().getValue(); if (resolvedInterceptor == null) { continue; } final String interceptorName = StringUtil.notNullize(resolvedInterceptor.getName().getStringValue()); names.add(LookupElementBuilder.create(resolvedInterceptor, interceptorName) .withIcon(ElementPresentationManager.getIcon(resolvedInterceptor))); } return ArrayUtil.toObjectArray(names, LookupElement.class); } @NotNull @Override public String getUnresolvedMessagePattern() { return "Cannot resolve interceptor '" + getValue() + "' " + "in interceptor-stack '" + interceptorStack.getName().getStringValue() + "'"; } } }