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    }