/* * 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++); } } }