/* * Copyright 2010 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.reference.web; import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.javaee.model.xml.ParamValue; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiPackage; import com.intellij.psi.PsiReference; import com.intellij.psi.PsiReferenceBase; import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReference; import com.intellij.psi.xml.XmlTag; import com.intellij.struts2.model.constant.StrutsConstantKey; import com.intellij.struts2.model.constant.StrutsConstantManager; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; import com.intellij.util.xml.*; import com.intellij.util.xml.impl.ConvertContextFactory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; /** * Delegates {@link com.intellij.struts2.model.constant.StrutsConstant#getConverter()} references. * * @author Yann Cébron */ class StrutsConstantValueReference extends PsiReferenceBase<XmlTag> implements EmptyResolveMessageProvider { @Nullable private final Pair<DomElement, Converter> elementConverterPair; StrutsConstantValueReference(@NotNull final XmlTag xmlTag) { super(xmlTag, false); elementConverterPair = getElementConverterPair(); } @SuppressWarnings({"unchecked"}) public PsiElement resolve() { if (elementConverterPair == null) { return myElement; } final Converter converter = elementConverterPair.getSecond(); final ConvertContext convertContext = ConvertContextFactory.createConvertContext(elementConverterPair.first); // additional variants (String only) if (converter instanceof ResolvingConverter) { final Set additionalVariants = ((ResolvingConverter) converter).getAdditionalVariants(convertContext); if (additionalVariants.contains(getValue())) { return myElement; } } // "normal" reference final Object resolveObject = converter.fromString(getValue(), convertContext); if (resolveObject == null) { return null; } // DomElement if (resolveObject instanceof DomElement) { return ((DomElement) resolveObject).getXmlTag(); } // fake self-reference (e.g. String value from Converter) if (!(resolveObject instanceof PsiElement)) { return myElement; } // "real" reference return (PsiElement) resolveObject; } @NotNull public String getUnresolvedMessagePattern() { assert elementConverterPair != null; return elementConverterPair.second .getErrorMessage(getValue(), ConvertContextFactory.createConvertContext(elementConverterPair.first)); } @NotNull @SuppressWarnings({"unchecked"}) public Object[] getVariants() { if (elementConverterPair == null) { return ArrayUtil.EMPTY_OBJECT_ARRAY; } final Converter converter = elementConverterPair.second; if (!(converter instanceof ResolvingConverter)) { return ArrayUtil.EMPTY_OBJECT_ARRAY; } final ResolvingConverter resolvingConverter = (ResolvingConverter) converter; // merge "normal" + additional variants final DomElement paramValueElement = elementConverterPair.first; final ConvertContext convertContext = ConvertContextFactory.createConvertContext(paramValueElement); // wrap explicitly for empty list final Collection converterVariants = new ArrayList(resolvingConverter.getVariants(convertContext)); final Collection variants; if (!converterVariants.isEmpty() && converterVariants.iterator().next() instanceof DomElement) { variants = Arrays.asList(ElementPresentationManager.getInstance().createVariants(converterVariants)); } else { variants = converterVariants; } variants.addAll(resolvingConverter.getAdditionalVariants(convertContext)); // add custom created references if (resolvingConverter instanceof CustomReferenceConverter) { final PsiReference[] references = ((CustomReferenceConverter) resolvingConverter). createReferences((GenericDomValue) paramValueElement, myElement, convertContext); for (final PsiReference customReference : references) { if (customReference instanceof JavaClassReference) { JavaClassReference javaClassReference = (JavaClassReference)customReference; String[] names = javaClassReference.getExtendClassNames(); PsiElement context = javaClassReference.getCompletionContext(); if (names != null && context instanceof PsiPackage) { javaClassReference.processSubclassVariants((PsiPackage)context, names, element -> variants.add(element)); continue; } } Collections.addAll(variants, customReference.getVariants()); } } return ArrayUtil.toObjectArray(variants); } /** * Gets the DomElement and corresponding converter. * * @return {@code null} on errors or if one of both could not be resolved. */ @Nullable private Pair<DomElement, Converter> getElementConverterPair() { final DomElement paramValueElement = DomUtil.getDomElement(myElement); assert paramValueElement != null; final DomElement domElement = paramValueElement.getParent(); assert domElement instanceof ParamValue; final ParamValue initParamElement = (ParamValue) domElement; final String paramName = initParamElement.getParamName().getStringValue(); if (StringUtil.isEmpty(paramName)) { return null; } final StrutsConstantManager constantManager = StrutsConstantManager.getInstance(myElement.getProject()); @SuppressWarnings({"ConstantConditions"}) final Converter converter = constantManager.findConverter(myElement, StrutsConstantKey.create(paramName)); if (converter == null) { return null; } return Pair.create(paramValueElement, converter); } }