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 }