blob: 4ced12477ba47ad3b3e0d693f5131c091cc15ffb [file] [log] [blame]
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.stubs;
import com.intellij.openapi.diagnostic.LogUtil;
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
import com.intellij.util.containers.RecentStringInterner;
import com.intellij.util.io.AbstractStringEnumerator;
import com.intellij.util.io.DataInputOutputUtil;
import com.intellij.util.io.IOUtil;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
/**
* Author: dmitrylomov
*/
public class StubSerializationHelper {
private final AbstractStringEnumerator myNameStorage;
protected final TIntObjectHashMap<ObjectStubSerializer> myIdToSerializer = new TIntObjectHashMap<ObjectStubSerializer>();
protected final TObjectIntHashMap<ObjectStubSerializer> mySerializerToId = new TObjectIntHashMap<ObjectStubSerializer>();
public StubSerializationHelper(@NotNull AbstractStringEnumerator nameStorage) {
myNameStorage = nameStorage;
}
public void assignId(@NotNull final ObjectStubSerializer serializer) throws IOException {
final int id = persistentId(serializer);
final ObjectStubSerializer old = myIdToSerializer.put(id, serializer);
assert old == null : "ID: " + serializer.getExternalId() + " is not unique; Already registered serializer with this ID: " + old.getClass().getName();
final int oldId = mySerializerToId.put(serializer, id);
assert oldId == 0 : "Serializer " + serializer + " is already registered; Old ID:" + oldId;
}
private int persistentId(@NotNull final ObjectStubSerializer serializer) throws IOException {
return myNameStorage.enumerate(serializer.getExternalId());
}
private void doSerialize(@NotNull Stub rootStub, @NotNull StubOutputStream stream) throws IOException {
final ObjectStubSerializer serializer = StubSerializationUtil.getSerializer(rootStub);
DataInputOutputUtil.writeINT(stream, getClassId(serializer));
serializer.serialize(rootStub, stream);
final List<? extends Stub> children = rootStub.getChildrenStubs();
final int childrenSize = children.size();
DataInputOutputUtil.writeINT(stream, childrenSize);
for (int i = 0; i < childrenSize; ++i) {
doSerialize(children.get(i), stream);
}
}
public void serialize(@NotNull Stub rootStub, @NotNull OutputStream stream) throws IOException {
BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream();
FileLocalStringEnumerator storage = new FileLocalStringEnumerator(true);
StubOutputStream stubOutputStream = new StubOutputStream(out, storage);
doSerialize(rootStub, stubOutputStream);
DataOutputStream resultStream = new DataOutputStream(stream);
DataInputOutputUtil.writeINT(resultStream, storage.myStrings.size());
byte[] buffer = IOUtil.allocReadWriteUTFBuffer();
for(String s:storage.myStrings) {
IOUtil.writeUTFFast(buffer, resultStream, s);
}
resultStream.write(out.getInternalBuffer(), 0, out.size());
}
private int getClassId(final ObjectStubSerializer serializer) {
final int idValue = mySerializerToId.get(serializer);
assert idValue != 0: "No ID found for serializer " + LogUtil.objectAndClass(serializer);
return idValue;
}
private final RecentStringInterner myStringInterner = new RecentStringInterner();
@NotNull
public Stub deserialize(@NotNull InputStream stream) throws IOException, SerializerNotFoundException {
FileLocalStringEnumerator storage = new FileLocalStringEnumerator(false);
StubInputStream inputStream = new StubInputStream(stream, storage);
final int numberOfStrings = DataInputOutputUtil.readINT(inputStream);
byte[] buffer = IOUtil.allocReadWriteUTFBuffer();
storage.myStrings.ensureCapacity(numberOfStrings);
int i = 0;
while(i < numberOfStrings) {
String s = myStringInterner.get(IOUtil.readUTFFast(buffer, inputStream));
storage.myStrings.add(s);
++i;
}
return deserialize(inputStream, null);
}
String intern(String str) {
return myStringInterner.get(str);
}
@NotNull
private Stub deserialize(@NotNull StubInputStream stream, @Nullable Stub parentStub) throws IOException, SerializerNotFoundException {
final int id = DataInputOutputUtil.readINT(stream);
final ObjectStubSerializer serializer = getClassById(id);
if (serializer == null) {
throw new SerializerNotFoundException("No serializer registered for stub: ID=" + id + "; parent stub class=" + (parentStub != null? parentStub.getClass().getName() : "null"));
}
Stub stub = serializer.deserialize(stream, parentStub);
int childCount = DataInputOutputUtil.readINT(stream);
for (int i = 0; i < childCount; i++) {
deserialize(stream, stub);
}
return stub;
}
private ObjectStubSerializer getClassById(int id) {
return myIdToSerializer.get(id);
}
private static class FileLocalStringEnumerator implements AbstractStringEnumerator {
private final TObjectIntHashMap<String> myEnumerates;
private final ArrayList<String> myStrings = new ArrayList<String>();
FileLocalStringEnumerator(boolean forSavingStub) {
if (forSavingStub) myEnumerates = new TObjectIntHashMap<String>();
else myEnumerates = null;
}
@Override
public int enumerate(@Nullable String value) throws IOException {
if (value == null) return 0;
assert myEnumerates != null; // enumerate possible only when writing stub
int i = myEnumerates.get(value);
if (i == 0) {
myEnumerates.put(value, i = myStrings.size() + 1);
myStrings.add(value);
}
return i;
}
@Override
public String valueOf(int idx) throws IOException {
if (idx == 0) return null;
return myStrings.get(idx - 1);
}
@Override
public void markCorrupted() {
}
@Override
public void close() throws IOException {
}
@Override
public boolean isDirty() {
return false;
}
@Override
public void force() {
}
}
}