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.serialization;
018    
019    import kotlin.Function1;
020    import kotlin.KotlinPackage;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.kotlin.descriptors.ClassDescriptor;
023    import org.jetbrains.kotlin.descriptors.ClassOrPackageFragmentDescriptor;
024    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
025    import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
026    import org.jetbrains.kotlin.name.FqName;
027    import org.jetbrains.kotlin.name.Name;
028    
029    import java.util.List;
030    
031    import static org.jetbrains.kotlin.serialization.ProtoBuf.QualifiedNameTable.QualifiedName;
032    
033    public class StringTable {
034        private static final class FqNameProto {
035            public final QualifiedName.Builder fqName;
036    
037            public FqNameProto(@NotNull QualifiedName.Builder fqName) {
038                this.fqName = fqName;
039            }
040    
041            @Override
042            public int hashCode() {
043                int result = 13;
044                result = 31 * result + fqName.getParentQualifiedName();
045                result = 31 * result + fqName.getShortName();
046                result = 31 * result + fqName.getKind().hashCode();
047                return result;
048            }
049    
050            @Override
051            public boolean equals(Object obj) {
052                if (obj == null || getClass() != obj.getClass()) return false;
053    
054                QualifiedName.Builder other = ((FqNameProto) obj).fqName;
055                return fqName.getParentQualifiedName() == other.getParentQualifiedName()
056                       && fqName.getShortName() == other.getShortName()
057                       && fqName.getKind() == other.getKind();
058            }
059        }
060    
061        private final Interner<String> strings = new Interner<String>();
062        private final Interner<FqNameProto> qualifiedNames = new Interner<FqNameProto>();
063        private final SerializerExtension extension;
064    
065        public StringTable(@NotNull SerializerExtension extension) {
066            this.extension = extension;
067        }
068    
069        @NotNull
070        public List<String> getStrings() {
071            return strings.getAllInternedObjects();
072        }
073    
074        @NotNull
075        public List<QualifiedName.Builder> getFqNames() {
076            return KotlinPackage.map(qualifiedNames.getAllInternedObjects(), new Function1<FqNameProto, QualifiedName.Builder>() {
077                @Override
078                public QualifiedName.Builder invoke(FqNameProto proto) {
079                    return proto.fqName;
080                }
081            });
082        }
083    
084        public int getSimpleNameIndex(@NotNull Name name) {
085            return getStringIndex(name.asString());
086        }
087    
088        public int getStringIndex(@NotNull String string) {
089            return strings.intern(string);
090        }
091    
092        public int getFqNameIndex(@NotNull ClassOrPackageFragmentDescriptor descriptor) {
093            QualifiedName.Builder builder = QualifiedName.newBuilder();
094            if (descriptor instanceof ClassDescriptor) {
095                builder.setKind(QualifiedName.Kind.CLASS);
096            }
097    
098            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
099            int shortName;
100            if (containingDeclaration instanceof PackageFragmentDescriptor) {
101                shortName = getSimpleNameIndex(descriptor.getName());
102                PackageFragmentDescriptor fragment = (PackageFragmentDescriptor) containingDeclaration;
103                if (!fragment.getFqName().isRoot()) {
104                    builder.setParentQualifiedName(getFqNameIndex(fragment.getFqName()));
105                }
106            }
107            else if (containingDeclaration instanceof ClassDescriptor) {
108                shortName = getSimpleNameIndex(descriptor.getName());
109                ClassDescriptor outerClass = (ClassDescriptor) containingDeclaration;
110                builder.setParentQualifiedName(getFqNameIndex(outerClass));
111            }
112            else {
113                if (descriptor instanceof ClassDescriptor) {
114                    builder.setKind(QualifiedName.Kind.LOCAL);
115                    shortName = getStringIndex(extension.getLocalClassName((ClassDescriptor) descriptor));
116                }
117                else {
118                    throw new IllegalStateException("Package container should be a package: " + descriptor);
119                }
120            }
121    
122            builder.setShortName(shortName);
123    
124            return qualifiedNames.intern(new FqNameProto(builder));
125        }
126    
127        public int getFqNameIndex(@NotNull FqName fqName) {
128            int result = -1;
129            for (Name segment : fqName.pathSegments()) {
130                QualifiedName.Builder builder = QualifiedName.newBuilder();
131                builder.setShortName(getSimpleNameIndex(segment));
132                if (result != -1) {
133                    builder.setParentQualifiedName(result);
134                }
135                result = qualifiedNames.intern(new FqNameProto(builder));
136            }
137            return result;
138        }
139    }