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    }