001 /*
002 * Copyright 2010-2015 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.name;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.kotlin.utils.StringsKt;
021
022 import java.util.ArrayList;
023 import java.util.List;
024
025 public final class FqName extends FqNameBase {
026
027 @NotNull
028 public static FqName fromSegments(@NotNull List<String> names) {
029 return new FqName(StringsKt.join(names, "."));
030 }
031
032 public static final FqName ROOT = new FqName("");
033
034 @NotNull
035 private final FqNameUnsafe fqName;
036
037 // cache
038 private transient FqName parent;
039
040 public FqName(@NotNull String fqName) {
041 this.fqName = new FqNameUnsafe(fqName, this);
042 }
043
044 public FqName(@NotNull FqNameUnsafe fqName) {
045 this.fqName = fqName;
046 }
047
048 private FqName(@NotNull FqNameUnsafe fqName, FqName parent) {
049 this.fqName = fqName;
050 this.parent = parent;
051 }
052
053 /*package*/ static boolean isValidAfterUnsafeCheck(@NotNull String qualifiedName) {
054 // TODO: There's a valid name with escape char ``
055 return qualifiedName.indexOf('<') < 0;
056 }
057
058 @Override
059 @NotNull
060 public String asString() {
061 return fqName.asString();
062 }
063
064 @NotNull
065 public FqNameUnsafe toUnsafe() {
066 return fqName;
067 }
068
069 public boolean isRoot() {
070 return fqName.isRoot();
071 }
072
073 @NotNull
074 public FqName parent() {
075 if (parent != null) {
076 return parent;
077 }
078
079 if (isRoot()) {
080 throw new IllegalStateException("root");
081 }
082
083 parent = new FqName(fqName.parent());
084
085 return parent;
086 }
087
088 @NotNull
089 public FqName child(@NotNull Name name) {
090 return new FqName(fqName.child(name), this);
091 }
092
093 @NotNull
094 public Name shortName() {
095 return fqName.shortName();
096 }
097
098 @Override
099 @NotNull
100 public Name shortNameOrSpecial() {
101 return fqName.shortNameOrSpecial();
102 }
103
104 @NotNull
105 public List<FqName> path() {
106 final List<FqName> path = new ArrayList<FqName>();
107 path.add(ROOT);
108 fqName.walk(new FqNameUnsafe.WalkCallback() {
109 @Override
110 public void segment(@NotNull Name shortName, @NotNull FqNameUnsafe fqName) {
111 // TODO: do not validate
112 path.add(new FqName(fqName));
113 }
114 });
115 return path;
116 }
117
118 @Override
119 @NotNull
120 public List<Name> pathSegments() {
121 return fqName.pathSegments();
122 }
123
124 public boolean firstSegmentIs(@NotNull Name segment) {
125 return fqName.firstSegmentIs(segment);
126 }
127
128 public boolean lastSegmentIs(@NotNull Name segment) {
129 return fqName.lastSegmentIs(segment);
130 }
131
132 public boolean isAncestorOf(@NotNull FqName other) {
133 String thisString = this.asString();
134 String otherString = other.asString();
135 return otherString.equals(thisString) || otherString.startsWith(thisString + ".");
136 }
137
138 @NotNull
139 public static FqName topLevel(@NotNull Name shortName) {
140 return new FqName(FqNameUnsafe.topLevel(shortName));
141 }
142
143
144 @Override
145 public String toString() {
146 return fqName.toString();
147 }
148
149 @Override
150 public boolean equals(Object o) {
151 if (this == o) return true;
152 if (!(o instanceof FqName)) return false;
153
154 FqName otherFqName = (FqName) o;
155
156 if (!fqName.equals(otherFqName.fqName)) return false;
157
158 return true;
159 }
160
161 @Override
162 public int hashCode() {
163 return fqName.hashCode();
164 }
165 }