/* * Copyright 2015 The Closure Compiler Authors. * * 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 com.google.javascript.jscomp; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.javascript.jscomp.parsing.parser.util.format.SimpleFormat; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * A utility class for generating and parsing id mappings held by {@link ReplaceIdGenerators}. */ public final class IdMappingUtil { @VisibleForTesting static final char NEW_LINE = '\n'; private static final Splitter LINE_SPLITTER = Splitter.on(NEW_LINE).omitEmptyStrings(); // Prevent instantiation. private IdMappingUtil() {} /** * @return The serialize map of generators and their ids and their * replacements. */ static String generateSerializedIdMappings(Map<String, Map<String, String>> idGeneratorMaps) { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, Map<String, String>> replacements : idGeneratorMaps.entrySet()) { if (!replacements.getValue().isEmpty()) { sb.append('[') .append(replacements.getKey()) .append(']') .append(NEW_LINE) .append(NEW_LINE); for (Map.Entry<String, String> replacement : replacements.getValue().entrySet()) { sb.append(replacement.getKey()) .append(':') .append(replacement.getValue()) .append(NEW_LINE); } sb.append(NEW_LINE); } } return sb.toString(); } /** * The expected format looks like this: * * <p>[generatorName1] * someId1:someFile:theLine:theColumn * ... * * <p>[[generatorName2] * someId2:someFile:theLine:theColumn] * ... * * <p>The returned data is grouped by generator name (the map key). The inner map provides * mappings from id to content (file, line and column info). In a glimpse, the structure is * {@code Map<generator name, BiMap<id, value>>}. * * <p>@throws IllegalArgumentException malformed input where there it 1) has duplicate generator * name, or 2) the line has no ':' for id and its content. */ public static Map<String, BiMap<String, String>> parseSerializedIdMappings(String idMappings) { if (Strings.isNullOrEmpty(idMappings)) { return Collections.emptyMap(); } Map<String, BiMap<String, String>> resultMap = new HashMap<>(); BiMap<String, String> currentSectionMap = null; int lineIndex = 0; for (String line : LINE_SPLITTER.split(idMappings)) { lineIndex++; if (line.isEmpty()) { continue; } if (line.charAt(0) == '[') { String currentSection = line.substring(1, line.length() - 1); currentSectionMap = resultMap.get(currentSection); if (currentSectionMap == null) { currentSectionMap = HashBiMap.create(); resultMap.put(currentSection, currentSectionMap); } else { throw new IllegalArgumentException( SimpleFormat.format("Cannot parse id map: %s\n Line: $s, lineIndex: %s", idMappings, line, lineIndex)); } } else { int split = line.indexOf(':'); if (split != -1) { String name = line.substring(0, split); String location = line.substring(split + 1, line.length()); currentSectionMap.put(name, location); } else { throw new IllegalArgumentException( SimpleFormat.format("Cannot parse id map: %s\n Line: $s, lineIndex: %s", idMappings, line, lineIndex)); } } } return resultMap; } }