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