/* * 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.smodel.persistence.def.v9; import jetbrains.mps.extapi.model.GeneratableSModel; import jetbrains.mps.generator.ModelDigestUtil; import jetbrains.mps.smodel.persistence.def.IHashProvider; import jetbrains.mps.smodel.persistence.def.XmlFastScanner; import jetbrains.mps.smodel.persistence.def.v9.IdEncoder.EncodingException; import org.jetbrains.mps.openapi.model.SNodeId; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class HashProvider9 extends IHashProvider { @Override public String getHash(String content) { return ModelDigestUtil.hashText(content); } @Override public Map<String, String> getRootHashes(String content) { Map<String, String> result = new HashMap<String, String>(); extractRootHashes(content, result); return result; } private static void extractRootHashes(String content, Map<String, String> rootHashes) { XmlFastScanner scanner = new XmlFastScanner(content.toCharArray()); int token, nodeStart = -1, nodeEnd = -1; String rootId = null; // { mandatory space} id {optional space} = {optional space} "{value}" final Matcher nodeIdMatcher = Pattern.compile("\\sid\\s*=\\s*\"([^\"]+)\"").matcher(""); final IdEncoder idEncoder = new IdEncoder(); boolean nodeElementFound = false; while ((token = scanner.next()) != XmlFastScanner.EOI) { if (token != XmlFastScanner.OPEN_TAG && token != XmlFastScanner.SIMPLE_TAG && token != XmlFastScanner.CLOSE_TAG) { continue; } if (scanner.tagDepth() != 1 || !"node".equals(scanner.getName())) { continue; } switch (token) { case XmlFastScanner.SIMPLE_TAG: nodeEnd = scanner.getOffset(); // fall-through case XmlFastScanner.OPEN_TAG: nodeEnd = -1; // just in case due to corrupt file (e.g. merge) closing tag comes first. nodeStart = scanner.getTokenOffset(); if (nodeIdMatcher.reset(scanner.token()).find()) { rootId = nodeIdMatcher.group(1); } else { rootId = null; } if (rootId != null && !nodeElementFound) { rootHashes.put(GeneratableSModel.HEADER, ModelDigestUtil.hashText(scanner.getText(0, nodeStart))); nodeElementFound = true; } break; case XmlFastScanner.CLOSE_TAG: nodeEnd = scanner.getOffset(); break; } if (rootId != null && nodeStart != -1 && nodeEnd != -1) { assert nodeStart <= nodeEnd; String s = scanner.getText(nodeStart, nodeEnd); SNodeId nodeId = null; try { nodeId = idEncoder.parseNodeId(rootId); } catch (EncodingException e) { throw new IllegalArgumentException(e); } // presentation of node id in persistence is different from what customers use (SNodeId.toString). // However, it's bad idea to have map<string,string> here, and shall get refactored. rootHashes.put(nodeId.toString(), ModelDigestUtil.hashText(s)); nodeStart = nodeEnd = -1; rootId = null; } } if (!nodeElementFound) { rootHashes.put(GeneratableSModel.HEADER, ModelDigestUtil.hashText(content)); } } }