001 /*
002 * Copyright 2010-2013 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.jet.lang.resolve.name;
018
019 import com.google.common.collect.Lists;
020 import com.intellij.openapi.util.text.StringUtil;
021 import org.jetbrains.annotations.NotNull;
022
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 String fqName = StringUtil.join(names, ".");
030 return new FqName(fqName);
031 }
032
033 public static final FqName ROOT = new FqName("");
034
035 @NotNull
036 private final FqNameUnsafe fqName;
037
038 // cache
039 private transient FqName parent;
040
041 public FqName(@NotNull String fqName) {
042 this.fqName = new FqNameUnsafe(fqName, this);
043
044 validateFqName();
045 }
046
047 public FqName(@NotNull FqNameUnsafe fqName) {
048 this.fqName = fqName;
049
050 validateFqName();
051 }
052
053 private FqName(@NotNull FqNameUnsafe fqName, FqName parent) {
054 this.fqName = fqName;
055 this.parent = parent;
056
057 validateFqName();
058 }
059
060
061 private void validateFqName() {
062 if (!isValidAfterUnsafeCheck(fqName.asString())) {
063 throw new IllegalArgumentException("incorrect fq name: " + fqName);
064 }
065 }
066
067 /*package*/ static boolean isValidAfterUnsafeCheck(@NotNull String qualifiedName) {
068 // TODO: There's a valid name with escape char ``
069 return qualifiedName.indexOf('<') < 0;
070 }
071
072 @Override
073 @NotNull
074 public String asString() {
075 return fqName.asString();
076 }
077
078 @NotNull
079 public FqNameUnsafe toUnsafe() {
080 return fqName;
081 }
082
083 public boolean isRoot() {
084 return fqName.isRoot();
085 }
086
087 @NotNull
088 public FqName parent() {
089 if (parent != null) {
090 return parent;
091 }
092
093 if (isRoot()) {
094 throw new IllegalStateException("root");
095 }
096
097 parent = new FqName(fqName.parent());
098
099 return parent;
100 }
101
102 @NotNull
103 public FqName child(@NotNull Name name) {
104 return new FqName(fqName.child(name), this);
105 }
106
107 @NotNull
108 public Name shortName() {
109 return fqName.shortName();
110 }
111
112 @Override
113 @NotNull
114 public Name shortNameOrSpecial() {
115 return fqName.shortNameOrSpecial();
116 }
117
118 @NotNull
119 public List<FqName> path() {
120 final List<FqName> path = Lists.newArrayList();
121 path.add(ROOT);
122 fqName.walk(new FqNameUnsafe.WalkCallback() {
123 @Override
124 public void segment(@NotNull Name shortName, @NotNull FqNameUnsafe fqName) {
125 // TODO: do not validate
126 path.add(new FqName(fqName));
127 }
128 });
129 return path;
130 }
131
132 @Override
133 @NotNull
134 public List<Name> pathSegments() {
135 return fqName.pathSegments();
136 }
137
138 public boolean firstSegmentIs(@NotNull Name segment) {
139 return fqName.firstSegmentIs(segment);
140 }
141
142 public boolean lastSegmentIs(@NotNull Name segment) {
143 return fqName.lastSegmentIs(segment);
144 }
145
146 public boolean isAncestorOf(@NotNull FqName other) {
147 String thisString = this.asString();
148 String otherString = other.asString();
149 return otherString.equals(thisString) || otherString.startsWith(thisString + ".");
150 }
151
152 @NotNull
153 public static FqName topLevel(@NotNull Name shortName) {
154 return new FqName(FqNameUnsafe.topLevel(shortName));
155 }
156
157
158 @Override
159 public String toString() {
160 return fqName.toString();
161 }
162
163 @Override
164 public boolean equals(Object o) {
165 if (this == o) return true;
166 if (!(o instanceof FqName)) return false;
167
168 FqName otherFqName = (FqName) o;
169
170 if (!fqName.equals(otherFqName.fqName)) return false;
171
172 return true;
173 }
174
175 @Override
176 public int hashCode() {
177 return fqName.hashCode();
178 }
179 }