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