/* * 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.generator.impl.cache; import jetbrains.mps.util.Pair; import jetbrains.mps.util.io.ModelInputStream; import jetbrains.mps.util.io.ModelOutputStream; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.model.SNodeId; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.stream.Collectors; /** * Persistence-friendly snapshot of mapping labels. * Evgeny Gryaznov, Sep 30, 2010 */ public class MappingsMemento { /* mapping,input -> output */ private final Map<String, Map<SNodeId, Object>> myMappingNameAndInputNodeToOutputNodeMap = new HashMap<String, Map<SNodeId, Object>>(); /* input -> output */ private final Map<SNodeId, Object> myCopiedOutputNodeForInputNode = new HashMap<SNodeId, Object>(); private final List<Pair<String, SNodeId>> myConditionalRoots = new ArrayList<>(); // add functions public void addOutputNodeByInputNodeAndMappingName(SNodeId inputNode, String mappingName, Object value) { if (mappingName == null) return; Map<SNodeId, Object> currentMapping = myMappingNameAndInputNodeToOutputNodeMap.get(mappingName); if (currentMapping == null) { myMappingNameAndInputNodeToOutputNodeMap.put(mappingName, new HashMap<SNodeId, Object>()); currentMapping = myMappingNameAndInputNodeToOutputNodeMap.get(mappingName); } if (value instanceof SNodeId) { currentMapping.put(inputNode, value); } else if (value instanceof SNode) { currentMapping.put(inputNode, ((SNode) value).getNodeId()); } else if (value instanceof Collection) { @SuppressWarnings("unchecked") Collection<SNode> n0 = (Collection<SNode>) value; List<SNodeId> v = new ArrayList<SNodeId>(n0.size()); for (SNode n : n0) { v.add(n.getNodeId()); } currentMapping.put(inputNode, v); } } public void addOutputNodeByInputNode(SNodeId inputNode, SNodeId outputNode, boolean isUnique) { myCopiedOutputNodeForInputNode.put(inputNode, isUnique ? outputNode : Collections.singletonList(outputNode)); } public void addNewOutputNode(String mappingLabel, SNodeId outputNode) { myConditionalRoots.add(new Pair<>(mappingLabel, outputNode)); } // getters public Map<String, Map<SNodeId, Object>> getMappingNameAndInputNodeToOutputNodeMap() { return myMappingNameAndInputNodeToOutputNodeMap; } public Map<SNodeId, Object> getCopiedOutputNodeForInputNode() { return myCopiedOutputNodeForInputNode; } public Collection<SNodeId> getNewOutputNodes(String mappingLabel) { return myConditionalRoots.stream().filter(p -> p.o1.equals(mappingLabel)).map(p -> p.o2).collect(Collectors.toList()); } // serialization public void save(ModelOutputStream os) throws IOException { /* mapping,input -> output */ os.writeInt(myMappingNameAndInputNodeToOutputNodeMap.size()); for (Entry<String, Map<SNodeId, Object>> e : myMappingNameAndInputNodeToOutputNodeMap.entrySet()) { os.writeString(e.getKey()); Map<SNodeId, Object> innerMap = e.getValue(); os.writeInt(innerMap.size()); for (Entry<SNodeId, Object> v : innerMap.entrySet()) { os.writeNodeId(v.getKey()); Object value = v.getValue(); if (value instanceof SNodeId) { os.writeInt(1); os.writeNodeId((SNodeId) value); } else if (value instanceof List) { os.writeInt(((List) value).size()); for (SNodeId id : (List<SNodeId>) value) { os.writeNodeId(id); } } } } /* input -> output */ os.writeInt(myCopiedOutputNodeForInputNode.size()); for (Entry<SNodeId, Object> e : myCopiedOutputNodeForInputNode.entrySet()) { os.writeNodeId(e.getKey()); Object val = e.getValue(); if (val instanceof SNodeId) { os.writeNodeId((SNodeId) val); os.writeBoolean(true); } else { os.writeNodeId(((List<SNodeId>) val).get(0)); os.writeBoolean(false); } } /* check */ os.writeByte('!'); } public static MappingsMemento load(ModelInputStream is) throws IOException { MappingsMemento mappingsMemento = new MappingsMemento(); /* mapping,input -> output */ for (int size = is.readInt(); size > 0; size--) { String label = is.readString(); int mapSize = is.readInt(); Map<SNodeId, Object> innerMap = new HashMap<SNodeId, Object>(mapSize); for (; mapSize > 0; mapSize--) { SNodeId key = is.readNodeId(); int valSize = is.readInt(); if (valSize == 1) { innerMap.put(key, is.readNodeId()); } else { List<SNodeId> list = new ArrayList<SNodeId>(valSize); for (; valSize > 0; valSize--) { list.add(is.readNodeId()); } innerMap.put(key, list); } } mappingsMemento.myMappingNameAndInputNodeToOutputNodeMap.put(label, innerMap); } /* input -> output */ for (int size = is.readInt(); size > 0; size--) { SNodeId inputNode = is.readNodeId(); SNodeId outputNode = is.readNodeId(); boolean isUnique = is.readBoolean(); mappingsMemento.addOutputNodeByInputNode(inputNode, outputNode, isUnique); } /* check */ if (is.readByte() != '!') { throw new IOException("inconsistent cache"); } return mappingsMemento; } }