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