FqName class
for type safety, to easier distinguish between: * short names * qualified names * jvm names (slash-separated) * special names like <root> * null values that mean "undefined" and "root ns" in different contexts
This commit is contained in:
@@ -26,6 +26,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.psi.stubs.PsiJetFunctionStub;
|
||||
import org.jetbrains.jet.lang.psi.stubs.elements.JetStubElementTypes;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
|
||||
/**
|
||||
@@ -96,7 +97,7 @@ public class JetNamedFunction extends JetFunction implements StubBasedPsiElement
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
public String getQualifiedName() {
|
||||
public FqName getQualifiedName() {
|
||||
final PsiJetFunctionStub stub = getStub();
|
||||
if (stub != null) {
|
||||
// TODO (stubs): return stub.getQualifiedName();
|
||||
@@ -105,12 +106,8 @@ public class JetNamedFunction extends JetFunction implements StubBasedPsiElement
|
||||
PsiElement parent = getParent();
|
||||
if (parent instanceof JetFile) {
|
||||
JetFile jetFile = (JetFile) parent;
|
||||
final String fileFQN = JetPsiUtil.getFQName(jetFile);
|
||||
if (!fileFQN.isEmpty()) {
|
||||
return fileFQN + "." + getName();
|
||||
}
|
||||
|
||||
return getName();
|
||||
final FqName fileFQN = JetPsiUtil.getFQName(jetFile);
|
||||
return fileFQN.child(getName());
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.intellij.psi.PsiFileFactory;
|
||||
import com.intellij.util.LocalTimeCounter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.lexer.JetKeywordToken;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
import org.jetbrains.jet.plugin.JetFileType;
|
||||
@@ -144,11 +145,15 @@ public class JetPsiFactory {
|
||||
// return file.getRootNamespace();
|
||||
// }
|
||||
|
||||
public static JetImportDirective createImportDirective(Project project, String classPath) {
|
||||
public static JetImportDirective createImportDirective(Project project, @NotNull String classPath) {
|
||||
JetFile namespace = createFile(project, "import " + classPath);
|
||||
return namespace.getImportDirectives().iterator().next();
|
||||
}
|
||||
|
||||
public static JetImportDirective createImportDirective(Project project, @NotNull FqName fqName) {
|
||||
return createImportDirective(project, fqName.getFqName());
|
||||
}
|
||||
|
||||
public static PsiElement createPrimaryConstructor(Project project) {
|
||||
JetClass aClass = createClass(project, "class A()");
|
||||
return aClass.findElementAt(7).getParent();
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
import org.jetbrains.jet.util.QualifiedNamesUtil;
|
||||
|
||||
@@ -129,21 +130,26 @@ public class JetPsiUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFQName(JetNamespaceHeader header) {
|
||||
private static FqName getFQName(JetNamespaceHeader header) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (JetSimpleNameExpression nameExpression : header.getParentNamespaceNames()) {
|
||||
builder.append(nameExpression.getReferencedName());
|
||||
builder.append(".");
|
||||
}
|
||||
builder.append(header.getName());
|
||||
return builder.toString();
|
||||
return new FqName(builder.toString());
|
||||
}
|
||||
|
||||
public static String getFQName(JetFile file) {
|
||||
public static FqName getFQName(JetFile file) {
|
||||
return getFQName(file.getNamespaceHeader());
|
||||
}
|
||||
|
||||
public static String getFQName(JetClassOrObject jetClass) {
|
||||
@Nullable
|
||||
public static FqName getFQName(@NotNull JetClassOrObject jetClass) {
|
||||
if (jetClass.getName() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiElement parent = jetClass.getParent();
|
||||
if (parent instanceof JetFile) {
|
||||
return makeFQName(getFQName((JetFile) parent), jetClass);
|
||||
@@ -157,14 +163,15 @@ public class JetPsiUtil {
|
||||
if (parent instanceof JetClassOrObject) {
|
||||
return makeFQName(getFQName(((JetClassOrObject) parent)), jetClass);
|
||||
}
|
||||
return jetClass.getName();
|
||||
return new FqName(jetClass.getName());
|
||||
}
|
||||
|
||||
public static String getFQName(JetNamedFunction jetNamedFunction) {
|
||||
@Nullable
|
||||
public static FqName getFQName(@NotNull JetNamedFunction jetNamedFunction) {
|
||||
|
||||
String functionName = jetNamedFunction.getName();
|
||||
if (functionName == null) {
|
||||
return functionName;
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -172,7 +179,7 @@ public class JetPsiUtil {
|
||||
jetNamedFunction,
|
||||
JetFile.class, JetClassOrObject.class, JetNamedFunction.class);
|
||||
|
||||
String firstPart = "";
|
||||
FqName firstPart = FqName.ROOT;
|
||||
if (qualifiedElement instanceof JetFile) {
|
||||
firstPart = getFQName((JetFile) qualifiedElement);
|
||||
}
|
||||
@@ -183,7 +190,7 @@ public class JetPsiUtil {
|
||||
firstPart = getFQName((JetNamedFunction) qualifiedElement);
|
||||
}
|
||||
|
||||
return QualifiedNamesUtil.combine(firstPart, functionName);
|
||||
return firstPart.child(functionName);
|
||||
}
|
||||
|
||||
@Nullable @JetElement.IfNotParsed
|
||||
@@ -197,8 +204,9 @@ public class JetPsiUtil {
|
||||
return text.replaceAll(" ", "") + (importDirective.isAllUnder() ? ".*" : "");
|
||||
}
|
||||
|
||||
private static String makeFQName(String prefix, JetClassOrObject jetClass) {
|
||||
return ((prefix == null || prefix.length() == 0) ? "" : prefix + ".") + jetClass.getName();
|
||||
@NotNull
|
||||
private static FqName makeFQName(@NotNull FqName prefix, @NotNull JetClassOrObject jetClass) {
|
||||
return prefix.child(jetClass.getName());
|
||||
}
|
||||
|
||||
public static boolean isIrrefutable(JetWhenEntry entry) {
|
||||
|
||||
+3
-1
@@ -30,6 +30,7 @@ import org.jetbrains.jet.lang.psi.JetClass;
|
||||
import org.jetbrains.jet.lang.psi.JetPsiUtil;
|
||||
import org.jetbrains.jet.lang.psi.stubs.PsiJetClassStub;
|
||||
import org.jetbrains.jet.lang.psi.stubs.impl.PsiJetClassStubImpl;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -60,7 +61,8 @@ public class JetClassElementType extends JetStubElementType<PsiJetClassStub, Jet
|
||||
|
||||
@Override
|
||||
public PsiJetClassStub createStub(@NotNull JetClass psi, StubElement parentStub) {
|
||||
return new PsiJetClassStubImpl(JetStubElementTypes.CLASS, parentStub, JetPsiUtil.getFQName(psi), psi.getName());
|
||||
FqName fqName = JetPsiUtil.getFQName(psi);
|
||||
return new PsiJetClassStubImpl(JetStubElementTypes.CLASS, parentStub, fqName != null ? fqName.getFqName() : null, psi.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@ package org.jetbrains.jet.lang.psi.stubs.impl;
|
||||
import com.intellij.psi.stubs.StubBase;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.intellij.util.io.StringRef;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.psi.JetClass;
|
||||
import org.jetbrains.jet.lang.psi.stubs.PsiJetClassStub;
|
||||
import org.jetbrains.jet.lang.psi.stubs.elements.JetClassElementType;
|
||||
@@ -34,7 +35,7 @@ public class PsiJetClassStubImpl extends StubBase<JetClass> implements PsiJetCla
|
||||
public PsiJetClassStubImpl(
|
||||
JetClassElementType type,
|
||||
final StubElement parent,
|
||||
final String qualifiedName,
|
||||
@Nullable final String qualifiedName,
|
||||
final String name) {
|
||||
|
||||
this(type, parent, StringRef.fromString(qualifiedName), StringRef.fromString(name));
|
||||
|
||||
@@ -171,8 +171,8 @@ public interface BindingContext {
|
||||
WritableSlice<JetReferenceExpression, PsiElement> LABEL_TARGET = Slices.<JetReferenceExpression, PsiElement>sliceBuilder().build();
|
||||
WritableSlice<JetParameter, PropertyDescriptor> VALUE_PARAMETER_AS_PROPERTY = Slices.<JetParameter, PropertyDescriptor>sliceBuilder().build();
|
||||
|
||||
WritableSlice<String, ClassDescriptor> FQNAME_TO_CLASS_DESCRIPTOR = new BasicWritableSlice<String, ClassDescriptor>(DO_NOTHING, true);
|
||||
WritableSlice<String, NamespaceDescriptor> FQNAME_TO_NAMESPACE_DESCRIPTOR = new BasicWritableSlice<String, NamespaceDescriptor>(DO_NOTHING);
|
||||
WritableSlice<FqName, ClassDescriptor> FQNAME_TO_CLASS_DESCRIPTOR = new BasicWritableSlice<FqName, ClassDescriptor>(DO_NOTHING, true);
|
||||
WritableSlice<FqName, NamespaceDescriptor> FQNAME_TO_NAMESPACE_DESCRIPTOR = new BasicWritableSlice<FqName, NamespaceDescriptor>(DO_NOTHING);
|
||||
|
||||
WritableSlice<ClassDescriptor, Boolean> INCOMPLETE_HIERARCHY = Slices.createCollectiveSetSlice();
|
||||
|
||||
|
||||
@@ -178,14 +178,25 @@ public class DescriptorUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getFQName(DeclarationDescriptor descriptor) {
|
||||
DeclarationDescriptor container = descriptor.getContainingDeclaration();
|
||||
if (container != null && !(container instanceof ModuleDescriptor)) {
|
||||
String baseName = getFQName(container);
|
||||
if (!baseName.isEmpty()) return baseName + "." + descriptor.getName();
|
||||
public static FqName getFQName(@NotNull DeclarationDescriptor descriptor) {
|
||||
if (descriptor instanceof ModuleDescriptor) {
|
||||
return FqName.ROOT;
|
||||
}
|
||||
|
||||
return descriptor.getName();
|
||||
if (descriptor.getContainingDeclaration() == null) {
|
||||
if (descriptor instanceof NamespaceDescriptor) {
|
||||
// TODO: namespace must always have parent
|
||||
if (descriptor.getName().equals("jet")) {
|
||||
return FqName.topLevel("jet");
|
||||
}
|
||||
if (descriptor.getName().equals("<java_root>")) {
|
||||
return FqName.ROOT;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("descriptor is not module descriptor and has null containingDeclaration: " + descriptor.getContainingDeclaration());
|
||||
}
|
||||
|
||||
return getFQName(descriptor.getContainingDeclaration()).child(descriptor.getName());
|
||||
}
|
||||
|
||||
public static boolean isTopLevelFunction(@NotNull SimpleFunctionDescriptor functionDescriptor) {
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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 org.jetbrains.jet.lang.resolve;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Stepan Koltsov
|
||||
*/
|
||||
public class FqName {
|
||||
|
||||
public static final FqName ROOT = new FqName("");
|
||||
|
||||
@NotNull
|
||||
private final String fqName;
|
||||
|
||||
// cache
|
||||
private transient FqName parent;
|
||||
private transient String shortName;
|
||||
|
||||
|
||||
public FqName(@NotNull String fqName) {
|
||||
this.fqName = fqName;
|
||||
|
||||
validateFqName();
|
||||
}
|
||||
|
||||
private FqName(@NotNull String fqName, FqName parent, String shortName) {
|
||||
this.fqName = fqName;
|
||||
this.parent = parent;
|
||||
this.shortName = shortName;
|
||||
|
||||
validateFqName();
|
||||
}
|
||||
|
||||
|
||||
private void validateFqName() {
|
||||
if (fqName.indexOf('/') >= 0 || fqName.indexOf('<') >= 0) {
|
||||
throw new IllegalArgumentException("incorrect fq name: " + fqName);
|
||||
}
|
||||
}
|
||||
|
||||
private void compute() {
|
||||
int lastDot = fqName.lastIndexOf('.');
|
||||
if (lastDot >= 0) {
|
||||
shortName = fqName.substring(lastDot + 1);
|
||||
parent = new FqName(fqName.substring(0, lastDot));
|
||||
} else {
|
||||
shortName = fqName;
|
||||
parent = ROOT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@NotNull
|
||||
public String getFqName() {
|
||||
return fqName;
|
||||
}
|
||||
|
||||
public boolean isRoot() {
|
||||
return fqName.equals("");
|
||||
}
|
||||
@NotNull
|
||||
public FqName parent() {
|
||||
if (parent != null) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
if (isRoot()) {
|
||||
throw new IllegalStateException("root");
|
||||
}
|
||||
|
||||
compute();
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FqName child(@NotNull String name) {
|
||||
String childFqName;
|
||||
if (isRoot()) {
|
||||
childFqName = name;
|
||||
} else {
|
||||
childFqName = fqName + "." + name;
|
||||
}
|
||||
return new FqName(childFqName, this, name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String shortName() {
|
||||
if (shortName != null) {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
if (isRoot()) {
|
||||
throw new IllegalStateException("root");
|
||||
}
|
||||
|
||||
compute();
|
||||
|
||||
return shortName;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public static FqName topLevel(@NotNull String shortName) {
|
||||
if (shortName.indexOf('.') >= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return new FqName(shortName, ROOT, shortName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return isRoot() ? "<root>" : fqName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
// generated by Idea
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
FqName that = (FqName) o;
|
||||
|
||||
if (fqName != null ? !fqName.equals(that.fqName) : that.fqName != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// generated by Idea
|
||||
return fqName != null ? fqName.hashCode() : 0;
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public class OverloadResolver {
|
||||
}
|
||||
|
||||
Key(NamespaceDescriptor namespaceDescriptor, String name) {
|
||||
this(DescriptorUtils.getFQName(namespaceDescriptor), name);
|
||||
this(DescriptorUtils.getFQName(namespaceDescriptor).getFqName(), name);
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
|
||||
+2
-2
@@ -177,13 +177,13 @@ public class ExpressionTypingUtils {
|
||||
* @return
|
||||
*/
|
||||
public static List<CallableDescriptor> canFindSuitableCall(
|
||||
@NotNull String callableFQN,
|
||||
@NotNull FqName callableFQN,
|
||||
@NotNull Project project,
|
||||
@NotNull JetExpression receiverExpression,
|
||||
@NotNull JetType receiverType,
|
||||
@NotNull JetScope scope) {
|
||||
|
||||
JetImportDirective importDirective = JetPsiFactory.createImportDirective(project, callableFQN);
|
||||
JetImportDirective importDirective = JetPsiFactory.createImportDirective(project, callableFQN.getFqName());
|
||||
|
||||
Collection<? extends DeclarationDescriptor> declarationDescriptors = ImportsResolver.analyseImportReference(importDirective, scope, new BindingTraceContext());
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
package org.jetbrains.jet.util;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.resolve.FqName;
|
||||
|
||||
/**
|
||||
* Common methods for working with qualified names strings.
|
||||
@@ -27,14 +27,14 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public final class QualifiedNamesUtil {
|
||||
|
||||
public static boolean isSubpackageOf(@NotNull final String subpackageName, @NotNull String packageName) {
|
||||
public static boolean isSubpackageOf(@NotNull final FqName subpackageName, @NotNull FqName packageName) {
|
||||
return subpackageName.equals(packageName) ||
|
||||
(subpackageName.startsWith(packageName) && subpackageName.charAt(packageName.length()) == '.');
|
||||
(subpackageName.getFqName().startsWith(packageName.getFqName()) && subpackageName.getFqName().charAt(packageName.getFqName().length()) == '.');
|
||||
}
|
||||
|
||||
public static boolean isShortNameForFQN(@NotNull final String name, @NotNull final String fqn) {
|
||||
return fqn.equals(name) ||
|
||||
(fqn.endsWith(name) && fqn.charAt(fqn.length() - name.length() - 1) == '.');
|
||||
public static boolean isShortNameForFQN(@NotNull final String name, @NotNull final FqName fqn) {
|
||||
return fqn.getFqName().equals(name) ||
|
||||
(fqn.getFqName().endsWith(name) && fqn.getFqName().charAt(fqn.getFqName().length() - name.length() - 1) == '.');
|
||||
}
|
||||
|
||||
public static boolean isOneSegmentFQN(@NotNull final String fqn) {
|
||||
@@ -46,14 +46,13 @@ public final class QualifiedNamesUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String fqnToShortName(@NotNull String fqn) {
|
||||
public static String fqnToShortName(@NotNull FqName fqn) {
|
||||
return getLastSegment(fqn);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getLastSegment(@NotNull String fqn) {
|
||||
int lastDotIndex = fqn.lastIndexOf('.');
|
||||
return (lastDotIndex != -1) ? fqn.substring(lastDotIndex + 1, fqn.length()) : fqn;
|
||||
public static String getLastSegment(@NotNull FqName fqn) {
|
||||
return fqn.shortName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -72,9 +71,14 @@ public final class QualifiedNamesUtil {
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String combine(@Nullable String first, @NotNull String second) {
|
||||
if (StringUtil.isEmpty(first)) return second;
|
||||
return first + "." + second;
|
||||
@NotNull
|
||||
public static FqName withoutLastSegment(@NotNull FqName fqName) {
|
||||
return fqName.parent();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FqName combine(@NotNull FqName first, @NotNull String second) {
|
||||
return first.child(second);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,14 +89,14 @@ public final class QualifiedNamesUtil {
|
||||
* @return tail fqn. If first part is not a begging of the full fqn, fullFQN will be returned.
|
||||
*/
|
||||
@NotNull
|
||||
public static String tail(@NotNull String headFQN, @NotNull String fullFQN) {
|
||||
public static String tail(@NotNull FqName headFQN, @NotNull FqName fullFQN) {
|
||||
if (!isSubpackageOf(fullFQN, headFQN)) {
|
||||
return fullFQN;
|
||||
return fullFQN.getFqName();
|
||||
}
|
||||
|
||||
return fullFQN.equals(headFQN) ?
|
||||
"" :
|
||||
fullFQN.substring(headFQN.length() + 1); // (headFQN + '.').length
|
||||
fullFQN.getFqName().substring(headFQN.getFqName().length() + 1); // (headFQN + '.').length
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +107,7 @@ public final class QualifiedNamesUtil {
|
||||
* @return qualified name with one more segment or null if fqn is not head part of fullFQN or there's no additional segment.
|
||||
*/
|
||||
@Nullable
|
||||
public static String plusOneSegment(String fqn, String fullFQN) {
|
||||
public static FqName plusOneSegment(@NotNull FqName fqn, @NotNull FqName fullFQN) {
|
||||
if (!isSubpackageOf(fullFQN, fqn)) {
|
||||
return null;
|
||||
}
|
||||
@@ -121,11 +125,11 @@ public final class QualifiedNamesUtil {
|
||||
* Check that given fqn could be imported with import.
|
||||
*
|
||||
* @param importPath path from the import. Could contain .* part
|
||||
* @param fqn
|
||||
* @return
|
||||
* @param fqn or another import directive
|
||||
*/
|
||||
public static boolean isImported(@NotNull String importPath, @NotNull String fqn) {
|
||||
if (importPath.endsWith("*")) {
|
||||
// TODO: import path is not valid FQN
|
||||
return withoutLastSegment(importPath).equals(withoutLastSegment(fqn));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user