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