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.java;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.jet.lang.resolve.name.FqName;
021    import org.jetbrains.jet.lang.resolve.name.Name;
022    
023    import java.util.ArrayList;
024    import java.util.Arrays;
025    import java.util.List;
026    
027    public class JvmClassName {
028        @NotNull
029        public static JvmClassName byInternalName(@NotNull String internalName) {
030            return new JvmClassName(internalName);
031        }
032    
033        /**
034         * WARNING: fq name cannot be uniquely mapped to JVM class name.
035         */
036        @NotNull
037        public static JvmClassName byFqNameWithoutInnerClasses(@NotNull FqName fqName) {
038            JvmClassName r = new JvmClassName(fqName.asString().replace('.', '/'));
039            r.fqName = fqName;
040            return r;
041        }
042    
043        @NotNull
044        public static JvmClassName byFqNameWithoutInnerClasses(@NotNull String fqName) {
045            return byFqNameWithoutInnerClasses(new FqName(fqName));
046        }
047    
048        private final static String CLASS_OBJECT_REPLACE_GUARD = "<class_object>";
049        private final static String TRAIT_IMPL_REPLACE_GUARD = "<trait_impl>";
050    
051        // Internal name:  kotlin/Map$Entry
052        // FqName:         kotlin.Map.Entry
053    
054        private final String internalName;
055        private FqName fqName;
056    
057        private JvmClassName(@NotNull String internalName) {
058            this.internalName = internalName;
059        }
060    
061        /**
062         * WARNING: internal name cannot be converted to FQ name for a class which contains dollars in the name
063         */
064        @NotNull
065        public FqName getFqNameForClassNameWithoutDollars() {
066            if (fqName == null) {
067                String fqName = internalName
068                        .replace(JvmAbi.CLASS_OBJECT_CLASS_NAME, CLASS_OBJECT_REPLACE_GUARD)
069                        .replace(JvmAbi.TRAIT_IMPL_CLASS_NAME, TRAIT_IMPL_REPLACE_GUARD)
070                        .replace('$', '.')
071                        .replace('/', '.')
072                        .replace(TRAIT_IMPL_REPLACE_GUARD, JvmAbi.TRAIT_IMPL_CLASS_NAME)
073                        .replace(CLASS_OBJECT_REPLACE_GUARD, JvmAbi.CLASS_OBJECT_CLASS_NAME);
074                this.fqName = new FqName(fqName);
075            }
076            return fqName;
077        }
078    
079        @NotNull
080        public String getInternalName() {
081            return internalName;
082        }
083    
084        @NotNull
085        public FqName getPackageFqName() {
086            int packageNameEnd = internalName.lastIndexOf("/");
087            if (packageNameEnd == -1) {
088                return FqName.ROOT;
089            }
090            return FqName.fromSegments(Arrays.asList(internalName.substring(0, packageNameEnd).split("/")));
091        }
092    
093        @NotNull
094        public FqName getHeuristicClassFqName() {
095            String name = internalName.substring(internalName.lastIndexOf("/") + 1);
096            char[] chars = name.toCharArray();
097            //treat all 'stand-alone' dollars as dots, except for last and first char of class name
098            for (int i = 1; i < chars.length - 1; ++i) {
099                if (name.charAt(i) == '$' && name.charAt(i - 1) != '$' && name.charAt(i + 1) != '$') {
100                    chars[i] = '.';
101                }
102            }
103            return new FqName(new String(chars));
104        }
105    
106        @Override
107        public String toString() {
108            return internalName;
109        }
110    
111        @Override
112        public boolean equals(Object o) {
113            if (this == o) return true;
114            if (o == null || getClass() != o.getClass()) return false;
115            return internalName.equals(((JvmClassName) o).internalName);
116        }
117    
118        @Override
119        public int hashCode() {
120            return internalName.hashCode();
121        }
122    }