/* * 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.workbench.findusages; import jetbrains.mps.smodel.adapter.ids.MetaIdFactory; import jetbrains.mps.smodel.adapter.ids.SConceptId; import jetbrains.mps.util.EqualUtil; import jetbrains.mps.util.io.ModelInputStream; import jetbrains.mps.util.io.ModelOutputStream; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.annotations.Immutable; 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.module.SModuleId; import java.io.IOException; /** * Entry in the usage index. We keep distinct instances to tell concept uses from node or model uses. * * Though concept instances ({@link ConceptInstance}) are now different from node usages, they come from the same indexing process, * and thus it would be ineffective to index twice to keep them separate. * * @author Artem Tikhomirov * @since 3.4 */ abstract class UsageEntry { static final byte TOKEN_CONCEPT_INSTANCE = 1; static final byte TOKEN_NODE_USE = 2; static final byte TOKEN_MODEL_USE = 4; // XXX SHALL BE ModelDataOutput instead (once I extract relevant part of MOS API into MDO) /*package*/ abstract void save(ModelOutputStream mos) throws IOException; /*package*/ static UsageEntry load(ModelInputStream mis) throws IOException { byte token = mis.readByte(); if (token != TOKEN_CONCEPT_INSTANCE && token != TOKEN_MODEL_USE && token != TOKEN_NODE_USE) { throw new IOException(String.format("Bad UsageEntry identifier: %x", token)); } switch(token) { case TOKEN_CONCEPT_INSTANCE : return new ConceptInstance(MetaIdFactory.conceptId(mis.readUUID(), mis.readLong())); case TOKEN_NODE_USE : return new NodeUse(mis.readNodeId()); case TOKEN_MODEL_USE : return new ModelUse(mis.readModuleID(), mis.readModelID()); } throw new IllegalStateException(); } @Immutable final static class ConceptInstance extends UsageEntry { private final SConceptId myConcept; public ConceptInstance(SConceptId concept) { myConcept = concept; } @Override public int hashCode() { return myConcept.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof ConceptInstance) { return myConcept.equals(((ConceptInstance) obj).myConcept); } return false; } @Override /*package*/ void save(ModelOutputStream mos) throws IOException { mos.writeByte(TOKEN_CONCEPT_INSTANCE); mos.writeUUID(myConcept.getLanguageId().getIdValue()); mos.writeLong(myConcept.getIdValue()); } } @Immutable final static class NodeUse extends UsageEntry { private final SNodeId myNode; public NodeUse(SNodeId node) { myNode = node; } @Override public int hashCode() { return myNode.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof NodeUse) { return myNode.equals(((NodeUse) obj).myNode); } return false; } @Override void save(ModelOutputStream mos) throws IOException { mos.writeByte(TOKEN_NODE_USE); mos.writeNodeId(myNode); } } @Immutable final static class ModelUse extends UsageEntry { private final SModuleId myModule; private final SModelId myModel; public ModelUse(@NotNull SModelReference modelRef) { myModel = modelRef.getModelId(); myModule = myModel.isGloballyUnique() ? null : modelRef.getModuleReference().getModuleId(); } /*package*/ModelUse(@Nullable SModuleId module, @NotNull SModelId model) { myModule = module; myModel = model; } @Override public int hashCode() { return myModel.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof ModelUse) { ModelUse o = (ModelUse) obj; return myModel.equals(o.myModel) && EqualUtil.equals(myModule, o.myModule); } return false; } @Override void save(ModelOutputStream mos) throws IOException { mos.writeByte(TOKEN_MODEL_USE); mos.writeModuleID(myModule); mos.writeModelID(myModel); } } }