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 kotlin.jvm.functions.Function2;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.load.kotlin.TypeMappingConfiguration;
023    import org.jetbrains.kotlin.name.ClassId;
024    import org.jetbrains.kotlin.name.FqName;
025    
026    import java.util.regex.Pattern;
027    
028    public class JvmClassName {
029        @NotNull
030        public static JvmClassName byInternalName(@NotNull String internalName) {
031            return new JvmClassName(internalName);
032        }
033    
034        @NotNull
035        public static JvmClassName byClassId(@NotNull ClassId classId) {
036            return byClassId(classId, null);
037        }
038    
039        @NotNull
040        public static JvmClassName byClassId(@NotNull ClassId classId, @Nullable TypeMappingConfiguration<?> typeMappingConfiguration) {
041            FqName packageFqName = classId.getPackageFqName();
042    
043            String[] relativeClassNameSegments = classId.getRelativeClassName().asString().split(Pattern.quote("."));
044            String relativeClassName;
045    
046            if (relativeClassNameSegments.length == 1) {
047                relativeClassName = relativeClassNameSegments[0];
048            }
049            else if (relativeClassNameSegments.length > 1 && typeMappingConfiguration != null) {
050                Function2<String, String, String> innerClassNameFactory = typeMappingConfiguration.getInnerClassNameFactory();
051                relativeClassName = innerClassNameFactory.invoke(relativeClassNameSegments[0], relativeClassNameSegments[1]);
052                for (int i = 2; i < relativeClassNameSegments.length; ++i) {
053                    relativeClassName = innerClassNameFactory.invoke(relativeClassName, relativeClassNameSegments[i]);
054                }
055            }
056            else {
057                // Default behavior if we don't have an inner class name factory
058                relativeClassName = classId.getRelativeClassName().asString().replace('.', '$');
059            }
060    
061            return packageFqName.isRoot()
062                   ? new JvmClassName(relativeClassName)
063                   : new JvmClassName(packageFqName.asString().replace('.', '/') + "/" + relativeClassName);
064        }
065    
066        /**
067         * WARNING: fq name cannot be uniquely mapped to JVM class name.
068         */
069        @NotNull
070        public static JvmClassName byFqNameWithoutInnerClasses(@NotNull FqName fqName) {
071            JvmClassName r = new JvmClassName(fqName.asString().replace('.', '/'));
072            r.fqName = fqName;
073            return r;
074        }
075    
076        @NotNull
077        public static JvmClassName byFqNameWithoutInnerClasses(@NotNull String fqName) {
078            return byFqNameWithoutInnerClasses(new FqName(fqName));
079        }
080    
081        // Internal name:  kotlin/Map$Entry
082        // FqName:         kotlin.Map.Entry
083    
084        private final String internalName;
085        private FqName fqName;
086    
087        private JvmClassName(@NotNull String internalName) {
088            this.internalName = internalName;
089        }
090    
091        /**
092         * WARNING: internal name cannot be converted to FQ name for a class which contains dollars in the name
093         */
094        @NotNull
095        public FqName getFqNameForClassNameWithoutDollars() {
096            if (fqName == null) {
097                this.fqName = new FqName(internalName.replace('$', '.').replace('/', '.'));
098            }
099            return fqName;
100        }
101    
102        @NotNull
103        public FqName getPackageFqName() {
104            int lastSlash = internalName.lastIndexOf("/");
105            if (lastSlash == -1) return FqName.ROOT;
106            return new FqName(internalName.substring(0, lastSlash).replace('/', '.'));
107        }
108    
109        @NotNull
110        public String getInternalName() {
111            return internalName;
112        }
113    
114        @Override
115        public String toString() {
116            return internalName;
117        }
118    
119        @Override
120        public boolean equals(Object o) {
121            if (this == o) return true;
122            if (o == null || getClass() != o.getClass()) return false;
123            return internalName.equals(((JvmClassName) o).internalName);
124        }
125    
126        @Override
127        public int hashCode() {
128            return internalName.hashCode();
129        }
130    }