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    
021    /**
022     * A class name which is used to uniquely identify a Kotlin class.
023     *
024     * If local = true, the class represented by this id is either itself local or is an inner class of some local class. This also means that
025     * the first non-class container of the class is not a package.
026     * In the case of a local class, relativeClassName consists of a single name including all callables' and class' names all the way up to
027     * the package, separated by dollar signs. If a class is an inner of local, relativeClassName would consist of two names,
028     * the second one being the class' short name.
029     */
030    public final class ClassId {
031        @NotNull
032        public static ClassId topLevel(@NotNull FqName topLevelFqName) {
033            return new ClassId(topLevelFqName.parent(), topLevelFqName.shortName());
034        }
035    
036        private final FqName packageFqName;
037        private final FqName relativeClassName;
038        private final boolean local;
039    
040        public ClassId(@NotNull FqName packageFqName, @NotNull FqName relativeClassName, boolean local) {
041            this.packageFqName = packageFqName;
042            assert !relativeClassName.isRoot() :
043                    "Class name must not be root: " + packageFqName + (local ? " (local)" : "");
044            this.relativeClassName = relativeClassName;
045            this.local = local;
046        }
047    
048        public ClassId(@NotNull FqName packageFqName, @NotNull Name topLevelName) {
049            this(packageFqName, FqName.topLevel(topLevelName), false);
050        }
051    
052        @NotNull
053        public FqName getPackageFqName() {
054            return packageFqName;
055        }
056    
057        @NotNull
058        public FqName getRelativeClassName() {
059            return relativeClassName;
060        }
061    
062        @NotNull
063        public Name getShortClassName() {
064            return relativeClassName.shortName();
065        }
066    
067        public boolean isLocal() {
068            return local;
069        }
070    
071        @NotNull
072        public ClassId createNestedClassId(@NotNull Name name) {
073            return new ClassId(getPackageFqName(), relativeClassName.child(name), local);
074        }
075    
076        @NotNull
077        public ClassId getOuterClassId() {
078            return new ClassId(getPackageFqName(), relativeClassName.parent(), local);
079        }
080    
081        public boolean isNestedClass() {
082            return !relativeClassName.parent().isRoot();
083        }
084    
085        @NotNull
086        public FqName asSingleFqName() {
087            if (packageFqName.isRoot()) return relativeClassName;
088            return new FqName(packageFqName.asString() + "." + relativeClassName.asString());
089        }
090    
091        /**
092         * @return a string where packages are delimited by '/' and classes by '.', e.g. "kotlin/Map.Entry"
093         */
094        @NotNull
095        public String asString() {
096            if (packageFqName.isRoot()) return relativeClassName.asString();
097            return packageFqName.asString().replace('.', '/') + "/" + relativeClassName.asString();
098        }
099    
100        @Override
101        public boolean equals(Object o) {
102            if (this == o) return true;
103            if (o == null || getClass() != o.getClass()) return false;
104    
105            ClassId id = (ClassId) o;
106    
107            return packageFqName.equals(id.packageFqName) &&
108                   relativeClassName.equals(id.relativeClassName) &&
109                   local == id.local;
110        }
111    
112        @Override
113        public int hashCode() {
114            int result = packageFqName.hashCode();
115            result = 31 * result + relativeClassName.hashCode();
116            result = 31 * result + Boolean.valueOf(local).hashCode();
117            return result;
118        }
119    
120        @Override
121        public String toString() {
122            return packageFqName.isRoot() ? "/" + asString() : asString();
123        }
124    }