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