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.resolve.jvm;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.kotlin.load.java.JvmAbi;
021    import org.jetbrains.kotlin.name.ClassId;
022    import org.jetbrains.kotlin.name.FqName;
023    
024    public class JvmClassName {
025        @NotNull
026        public static JvmClassName byInternalName(@NotNull String internalName) {
027            return new JvmClassName(internalName);
028        }
029    
030        @NotNull
031        public static JvmClassName byClassId(@NotNull ClassId classId) {
032            FqName packageFqName = classId.getPackageFqName();
033            String relativeClassName = classId.getRelativeClassName().asString().replace('.', '$');
034            return packageFqName.isRoot()
035                   ? new JvmClassName(relativeClassName)
036                   : new JvmClassName(packageFqName.asString().replace('.', '/') + "/" + relativeClassName);
037        }
038    
039        /**
040         * WARNING: fq name cannot be uniquely mapped to JVM class name.
041         */
042        @NotNull
043        public static JvmClassName byFqNameWithoutInnerClasses(@NotNull FqName fqName) {
044            JvmClassName r = new JvmClassName(fqName.asString().replace('.', '/'));
045            r.fqName = fqName;
046            return r;
047        }
048    
049        @NotNull
050        public static JvmClassName byFqNameWithoutInnerClasses(@NotNull String fqName) {
051            return byFqNameWithoutInnerClasses(new FqName(fqName));
052        }
053    
054        private final static String CLASS_OBJECT_REPLACE_GUARD = "<class_object>";
055        private final static String TRAIT_IMPL_REPLACE_GUARD = "<trait_impl>";
056    
057        // Internal name:  kotlin/Map$Entry
058        // FqName:         kotlin.Map.Entry
059    
060        private final String internalName;
061        private FqName fqName;
062    
063        private JvmClassName(@NotNull String internalName) {
064            this.internalName = internalName;
065        }
066    
067        /**
068         * WARNING: internal name cannot be converted to FQ name for a class which contains dollars in the name
069         */
070        @NotNull
071        public FqName getFqNameForClassNameWithoutDollars() {
072            if (fqName == null) {
073                String fqName = internalName
074                        .replace(JvmAbi.CLASS_OBJECT_CLASS_NAME, CLASS_OBJECT_REPLACE_GUARD)
075                        .replace(JvmAbi.TRAIT_IMPL_CLASS_NAME, TRAIT_IMPL_REPLACE_GUARD)
076                        .replace('$', '.')
077                        .replace('/', '.')
078                        .replace(TRAIT_IMPL_REPLACE_GUARD, JvmAbi.TRAIT_IMPL_CLASS_NAME)
079                        .replace(CLASS_OBJECT_REPLACE_GUARD, JvmAbi.CLASS_OBJECT_CLASS_NAME);
080                this.fqName = new FqName(fqName);
081            }
082            return fqName;
083        }
084    
085        @NotNull
086        public FqName getPackageFqName() {
087            int lastSlash = internalName.lastIndexOf("/");
088            if (lastSlash == -1) return FqName.ROOT;
089            return new FqName(internalName.substring(0, lastSlash).replace('/', '.'));
090        }
091    
092        @NotNull
093        public String getInternalName() {
094            return internalName;
095        }
096    
097        @Override
098        public String toString() {
099            return internalName;
100        }
101    
102        @Override
103        public boolean equals(Object o) {
104            if (this == o) return true;
105            if (o == null || getClass() != o.getClass()) return false;
106            return internalName.equals(((JvmClassName) o).internalName);
107        }
108    
109        @Override
110        public int hashCode() {
111            return internalName.hashCode();
112        }
113    }