/*
* Copyright 2003-2016 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 jetbrains.mps.util.io;
import gnu.trove.TObjectIntHashMap;
import jetbrains.mps.project.ModuleId;
import jetbrains.mps.smodel.SModelId.ForeignSModelId;
import jetbrains.mps.smodel.SModelId.IntegerSModelId;
import jetbrains.mps.smodel.SModelId.RegularSModelId;
import jetbrains.mps.smodel.SNodeId.Regular;
import jetbrains.mps.smodel.adapter.ids.MetaIdHelper;
import jetbrains.mps.smodel.adapter.ids.SConceptId;
import jetbrains.mps.smodel.adapter.ids.SContainmentLinkId;
import jetbrains.mps.smodel.adapter.ids.SLanguageId;
import jetbrains.mps.smodel.adapter.ids.SPropertyId;
import jetbrains.mps.smodel.adapter.ids.SReferenceLinkId;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.language.SLanguage;
import org.jetbrains.mps.openapi.language.SProperty;
import org.jetbrains.mps.openapi.language.SReferenceLink;
import org.jetbrains.mps.openapi.model.SModelId;
import org.jetbrains.mps.openapi.model.SModelReference;
import org.jetbrains.mps.openapi.model.SNodeId;
import org.jetbrains.mps.openapi.model.SNodeReference;
import org.jetbrains.mps.openapi.module.SModuleId;
import org.jetbrains.mps.openapi.module.SModuleReference;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.UUID;
/**
* Evgeny Gryaznov, Sep 27, 2010
*/
public class ModelOutputStream extends DataOutputStream {
static final byte NULL = 0x70;
static final byte NODEID_STRING = 0x17;
static final byte NODEID_LONG = 0x18;
static final byte MODELID_STRING = 0x26;
static final byte MODELID_REGULAR = 0x28;
static final byte MODELID_FOREIGN = 0x27;
static final byte MODELID_INTEGER = 0x29;
static final byte NODEPTR = 0x44;
static final byte MODULEID_FOREIGN = 0x47;
static final byte MODULEID_REGULAR = 0x48;
static final byte MODELREF = 7;
static final byte MODELREF_INDEX = 9;
static final byte MODULEREF_MODULEID = 0x17;
static final byte MODULEREF_NAMEONLY = 0x18;
static final byte MODULEREF_INDEX = 0x19;
static final byte LANGUAGE = 0x30;
static final byte LANGUAGE_INDEX = 0x31;
static final byte CONCEPT = 0x32;
static final byte CONCEPT_INDEX = 0x33;
static final byte PROPERTY = 0x34;
static final byte PROPERTY_INDEX = 0x35;
static final byte ASSOCIATION = 0x36;
static final byte ASSOCIATION_INDEX = 0x37;
static final byte AGGREGATION = 0x38;
static final byte AGGREGATION_INDEX = 0x39;
private TObjectIntHashMap<String> stringToIndex = new TObjectIntHashMap<String>();
private TObjectIntHashMap<SModelReference> modelrefToIndex = new TObjectIntHashMap<SModelReference>();
private TObjectIntHashMap<SModuleReference> moduleRefToIndex = new TObjectIntHashMap<SModuleReference>();
private TObjectIntHashMap<SLanguageId> myLanguage2Index = new TObjectIntHashMap<SLanguageId>();
private TObjectIntHashMap<SConceptId> myConcept2Index = new TObjectIntHashMap<SConceptId>();
private TObjectIntHashMap<SPropertyId> myProperty2Index = new TObjectIntHashMap<SPropertyId>();
private TObjectIntHashMap<SReferenceLinkId> myAssociation2Index = new TObjectIntHashMap<SReferenceLinkId>();
private TObjectIntHashMap<SContainmentLinkId> myAggregation2Index = new TObjectIntHashMap<SContainmentLinkId>();
private int myStringIndex, myRefIndex, myModuleRefIndex = 0;
private int myLanguageIndex, myConceptIndex, myPropertyIndex, myAssociationIndex, myAggregationIndex;
public ModelOutputStream(OutputStream out) {
super(new BufferedOutputStream(out, 65536));
}
public void writeStrings(@Nullable Collection<String> c) throws IOException {
writeInt(c == null ? -1 : c.size());
if (c != null) {
for (String s : c) {
writeString(s);
}
}
}
public void writeString(@Nullable String s) throws IOException {
if (s == null) {
writeByte(NULL);
} else {
if (!stringToIndex.containsKey(s)) {
stringToIndex.put(s, myStringIndex++);
while (s.length() > 16384) {
String prefix = s.substring(0, 16384);
writeByte(42);
writeUTF(prefix);
s = s.substring(16384);
}
writeByte(0);
writeUTF(s);
} else {
writeByte(1);
writeInt(stringToIndex.get(s));
}
}
}
public void writeModuleReference(SModuleReference ref) throws IOException {
if (ref == null) {
writeByte(NULL);
} else {
if (!moduleRefToIndex.containsKey(ref)) {
moduleRefToIndex.put(ref, myModuleRefIndex++);
if (ref.getModuleId() != null) {
writeByte(MODULEREF_MODULEID);
writeModuleID(ref.getModuleId());
} else {
writeByte(MODULEREF_NAMEONLY);
}
writeString(ref.getModuleName());
} else {
writeByte(MODULEREF_INDEX);
writeInt(moduleRefToIndex.get(ref));
}
}
}
public void writeModuleID(SModuleId id) throws IOException {
if (id == null) {
writeByte(NULL);
} else if (id instanceof ModuleId.Regular) {
writeByte(MODULEID_REGULAR);
writeUUID(((ModuleId.Regular) id).getUUID());
} else if (id instanceof ModuleId.Foreign) {
writeByte(MODULEID_FOREIGN);
writeString(((ModuleId.Foreign) id).getName());
} else {
throw new IOException("unknown id");
}
}
public void writeModelReference(SModelReference ref) throws IOException {
if (ref == null) {
writeByte(NULL);
} else {
if (!modelrefToIndex.containsKey(ref)) {
modelrefToIndex.put(ref, myRefIndex++);
writeByte(MODELREF);
writeModelID(ref.getModelId());
writeString(ref.getModelName());
writeModuleReference(ref.getModuleReference());
} else {
writeByte(MODELREF_INDEX);
writeInt(modelrefToIndex.get(ref));
}
}
}
public void writeModelID(SModelId id) throws IOException {
if (id == null) {
writeByte(NULL);
} else if (id instanceof RegularSModelId) {
writeByte(MODELID_REGULAR);
writeUUID(((RegularSModelId) id).getId());
} else if (id instanceof ForeignSModelId) {
writeByte(MODELID_FOREIGN);
writeString(((ForeignSModelId) id).getId());
} else if (id instanceof IntegerSModelId) {
writeByte(MODELID_INTEGER);
writeInt(((IntegerSModelId) id).getValue());
} else {
writeByte(MODELID_STRING);
writeString(id.toString());
}
}
public void writeNodeId(SNodeId id) throws IOException {
if (id == null) {
writeByte(NULL);
} else if (id instanceof Regular) {
writeByte(NODEID_LONG);
writeLong(((Regular) id).getId());
} else {
writeByte(NODEID_STRING);
writeString(id.toString());
}
}
public void writeNodePointer(SNodeReference ptr) throws IOException {
if (ptr == null) {
writeByte(NULL);
} else {
writeByte(NODEPTR);
writeModelReference(ptr.getModelReference());
writeNodeId(ptr.getNodeId());
}
}
public void writeUUID(UUID uuid) throws IOException {
writeLong(uuid.getMostSignificantBits());
writeLong(uuid.getLeastSignificantBits());
}
public void writeLanguage(SLanguage lang) throws IOException {
if (lang == null) {
writeByte(NULL);
return;
}
final SLanguageId id = MetaIdHelper.getLanguage(lang);
if (myLanguage2Index.containsKey(id)) {
writeByte(LANGUAGE_INDEX);
writeShort(myLanguage2Index.get(id));
} else {
writeByte(LANGUAGE);
writeUUID(id.getIdValue());
writeString(lang.getQualifiedName());
myLanguage2Index.put(id, myLanguageIndex++);
}
}
public void writeConcept(SAbstractConcept concept) throws IOException {
if (concept == null) {
writeByte(NULL);
return;
}
final SConceptId id = MetaIdHelper.getConcept(concept);
assert id != null : "Can't get identity of concept " + concept;
if (myConcept2Index.containsKey(id)) {
writeByte(CONCEPT_INDEX);
writeShort(myConcept2Index.get(id));
} else {
writeByte(CONCEPT);
writeUUID(id.getLanguageId().getIdValue());
writeLong(id.getIdValue());
writeString(concept.getQualifiedName()); // FIXME MetaAdapterFactory shall be explicit about what concept name it takes
myConcept2Index.put(id, myConceptIndex++);
}
}
public void writeProperty(SProperty property) throws IOException {
if (property == null) {
writeByte(NULL);
return;
}
final SPropertyId id = MetaIdHelper.getProperty(property);
assert id != null : "Can't get identity of property " + property;
if (myProperty2Index.containsKey(id)) {
writeByte(PROPERTY_INDEX);
writeShort(myProperty2Index.get(id));
} else {
writeByte(PROPERTY);
writeConcept(property.getOwner());
writeLong(id.getIdValue());
writeString(property.getName());
myProperty2Index.put(id, myPropertyIndex++);
}
}
public void writeReferenceLink(SReferenceLink link) throws IOException {
if (link == null) {
writeByte(NULL);
return;
}
final SReferenceLinkId id = MetaIdHelper.getAssociation(link);
assert id != null : "Can't get identity of association " + link;
if (myAssociation2Index.containsKey(id)) {
writeByte(ASSOCIATION_INDEX);
writeShort(myAssociation2Index.get(id));
} else {
writeByte(ASSOCIATION);
writeConcept(link.getOwner());
writeLong(id.getIdValue());
writeString(link.getName());
myAssociation2Index.put(id, myAssociationIndex++);
}
}
public void writeContainmentLink(SContainmentLink link) throws IOException {
if (link == null) {
writeByte(NULL);
return;
}
final SContainmentLinkId id = MetaIdHelper.getAggregation(link);
assert id != null : "Can't get identity of aggregation " + link;
if (myAggregation2Index.containsKey(id)) {
writeByte(AGGREGATION_INDEX);
writeShort(myAggregation2Index.get(id));
} else {
writeByte(AGGREGATION);
writeConcept(link.getOwner());
writeLong(id.getIdValue());
writeString(link.getName());
myAggregation2Index.put(id, myAggregationIndex++);
}
}
}