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