| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * 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.psi.impl.source.resolve.reference.impl.providers; |
| |
| import com.intellij.codeInsight.daemon.JavaErrorMessages; |
| import com.intellij.lang.xml.XMLLanguage; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiReference; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.util.ArrayUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * @author peter |
| */ |
| public class JavaClassReferenceSet { |
| public static final char DOT = '.'; |
| public static final char DOLLAR = '$'; |
| private static final char LT = '<'; |
| private static final char COMMA = ','; |
| |
| private JavaClassReference[] myReferences; |
| private List<JavaClassReferenceSet> myNestedGenericParameterReferences; |
| private JavaClassReferenceSet myContext; |
| private PsiElement myElement; |
| private final int myStartInElement; |
| private final JavaClassReferenceProvider myProvider; |
| |
| public JavaClassReferenceSet(@NotNull String str, @NotNull PsiElement element, int startInElement, final boolean isStatic, @NotNull JavaClassReferenceProvider provider) { |
| this(str, element, startInElement, isStatic, provider, null); |
| } |
| |
| private JavaClassReferenceSet(@NotNull String str, @NotNull PsiElement element, int startInElement, final boolean isStatic, @NotNull JavaClassReferenceProvider provider, |
| JavaClassReferenceSet context) { |
| myStartInElement = startInElement; |
| myProvider = provider; |
| reparse(str, element, isStatic, context); |
| } |
| |
| @NotNull |
| public JavaClassReferenceProvider getProvider() { |
| return myProvider; |
| } |
| |
| @NotNull |
| public TextRange getRangeInElement() { |
| PsiReference[] references = getReferences(); |
| return new TextRange(references[0].getRangeInElement().getStartOffset(), references[references.length - 1].getRangeInElement().getEndOffset()); |
| } |
| |
| private void reparse(@NotNull String str, @NotNull PsiElement element, final boolean isStaticImport, JavaClassReferenceSet context) { |
| myElement = element; |
| myContext = context; |
| final List<JavaClassReference> referencesList = new ArrayList<JavaClassReference>(); |
| int currentDot = -1; |
| int referenceIndex = 0; |
| boolean allowDollarInNames = isAllowDollarInNames(); |
| boolean allowGenerics = false; |
| boolean allowGenericsCalculated = false; |
| boolean parsingClassNames = true; |
| |
| while (parsingClassNames) { |
| int nextDotOrDollar = -1; |
| for(int curIndex = currentDot + 1; curIndex < str.length(); ++curIndex) { |
| final char ch = str.charAt(curIndex); |
| |
| if (ch == DOT || ch == DOLLAR && allowDollarInNames) { |
| nextDotOrDollar = curIndex; |
| break; |
| } |
| |
| if (ch == LT || ch == COMMA) { |
| if (!allowGenericsCalculated) { |
| allowGenerics = !isStaticImport && PsiUtil.isLanguageLevel5OrHigher(element); |
| allowGenericsCalculated = true; |
| } |
| |
| if (allowGenerics) { |
| nextDotOrDollar = curIndex; |
| break; |
| } |
| } |
| } |
| |
| if (nextDotOrDollar == -1) { |
| nextDotOrDollar = currentDot + 1; |
| for(int i = nextDotOrDollar; i < str.length() && Character.isJavaIdentifierPart(str.charAt(i)); ++i) nextDotOrDollar++; |
| parsingClassNames = false; |
| int j = nextDotOrDollar; |
| while(j < str.length() && Character.isWhitespace(str.charAt(j))) ++j; |
| |
| if (j < str.length()) { |
| char ch = str.charAt(j); |
| boolean recognized = false; |
| |
| if (ch == '[') { |
| j++; |
| while(j < str.length() && Character.isWhitespace(str.charAt(j))) ++j; |
| |
| if (j < str.length()) { |
| ch = str.charAt(j); |
| |
| if (ch == ']') { |
| j++; |
| while(j < str.length() && Character.isWhitespace(str.charAt(j))) ++j; |
| |
| recognized = j == str.length(); |
| } |
| } |
| } |
| |
| final Boolean aBoolean = JavaClassReferenceProvider.JVM_FORMAT.getValue(getOptions()); |
| if (aBoolean == null || !aBoolean.booleanValue()) { |
| if (!recognized) nextDotOrDollar = -1; // nonsensible characters anyway, don't do resolve |
| } |
| } |
| } |
| |
| if (nextDotOrDollar != -1 && nextDotOrDollar < str.length()) { |
| final char c = str.charAt(nextDotOrDollar); |
| if (c == LT) { |
| int end = str.lastIndexOf('>'); |
| if (end != -1 && end > nextDotOrDollar) { |
| if (myNestedGenericParameterReferences == null) myNestedGenericParameterReferences = new ArrayList<JavaClassReferenceSet>(1); |
| myNestedGenericParameterReferences.add( |
| new JavaClassReferenceSet( |
| str.substring(nextDotOrDollar + 1, end), |
| myElement, |
| myStartInElement + nextDotOrDollar + 1, |
| isStaticImport, |
| myProvider, |
| this |
| ) |
| ); |
| parsingClassNames = false; |
| } else { |
| nextDotOrDollar = -1; // nonsensible characters anyway, don't do resolve |
| } |
| } else if (COMMA == c && myContext != null) { |
| if (myContext.myNestedGenericParameterReferences == null) myContext.myNestedGenericParameterReferences = new ArrayList<JavaClassReferenceSet>(1); |
| myContext.myNestedGenericParameterReferences.add( |
| new JavaClassReferenceSet( |
| str.substring(nextDotOrDollar + 1), |
| myElement, |
| myStartInElement + nextDotOrDollar + 1, |
| isStaticImport, |
| myProvider, |
| this |
| ) |
| ); |
| parsingClassNames = false; |
| } |
| } |
| |
| int beginIndex = currentDot + 1; |
| while (beginIndex < nextDotOrDollar && Character.isWhitespace(str.charAt(beginIndex))) beginIndex++; |
| |
| final String subreferenceText = nextDotOrDollar > 0 ? str.substring(beginIndex, nextDotOrDollar) : str.substring(beginIndex); |
| |
| TextRange textRange = |
| new TextRange(myStartInElement + beginIndex, myStartInElement + (nextDotOrDollar > 0 ? nextDotOrDollar : str.length())); |
| JavaClassReference currentContextRef = createReference(referenceIndex, subreferenceText, textRange, isStaticImport); |
| referenceIndex++; |
| referencesList.add(currentContextRef); |
| if ((currentDot = nextDotOrDollar) < 0) { |
| break; |
| } |
| } |
| |
| myReferences = referencesList.toArray(new JavaClassReference[referencesList.size()]); |
| } |
| |
| @NotNull |
| protected JavaClassReference createReference(final int referenceIndex, @NotNull String subreferenceText, @NotNull TextRange textRange, |
| final boolean staticImport) { |
| return new JavaClassReference(this, textRange, referenceIndex, subreferenceText, staticImport); |
| } |
| |
| public boolean isAllowDollarInNames() { |
| final Boolean aBoolean = myProvider.getOption(JavaClassReferenceProvider.ALLOW_DOLLAR_NAMES); |
| return !Boolean.FALSE.equals(aBoolean) && myElement.getLanguage() instanceof XMLLanguage; |
| } |
| |
| protected boolean isStaticSeparator(char c, boolean strict) { |
| return isAllowDollarInNames() ? c == DOLLAR : c == DOT; |
| } |
| |
| public void reparse(@NotNull PsiElement element, @NotNull TextRange range) { |
| final String text = range.substring(element.getText()); |
| reparse(text, element, false, myContext); |
| } |
| |
| public JavaClassReference getReference(int index) { |
| return myReferences[index]; |
| } |
| |
| @NotNull |
| public JavaClassReference[] getAllReferences() { |
| JavaClassReference[] result = myReferences; |
| if (myNestedGenericParameterReferences != null) { |
| for(JavaClassReferenceSet set:myNestedGenericParameterReferences) { |
| result = ArrayUtil.mergeArrays(result, set.getAllReferences()); |
| } |
| } |
| return result; |
| } |
| |
| public boolean canReferencePackage(int index) { |
| if (index == myReferences.length - 1) return false; |
| String text = getElement().getText(); |
| return text.charAt(myReferences[index].getRangeInElement().getEndOffset()) != '$'; |
| } |
| |
| public boolean isSoft() { |
| return myProvider.isSoft(); |
| } |
| |
| @NotNull |
| public PsiElement getElement() { |
| return myElement; |
| } |
| |
| @NotNull |
| public PsiReference[] getReferences() { |
| return myReferences; |
| } |
| |
| @Nullable |
| public Map<CustomizableReferenceProvider.CustomizationKey, Object> getOptions() { |
| return myProvider.getOptions(); |
| } |
| |
| @SuppressWarnings({"UnresolvedPropertyKey"}) |
| @NotNull |
| public String getUnresolvedMessagePattern(int index){ |
| if (canReferencePackage(index)) { |
| return JavaErrorMessages.message("error.cannot.resolve.class.or.package"); |
| } |
| return JavaErrorMessages.message("error.cannot.resolve.class"); |
| } |
| } |