/* * Copyright 2014 Google Inc. * * 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.gwt.resources.converter; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.TreeLogger.Type; import com.google.gwt.resources.css.ast.Context; import com.google.gwt.resources.css.ast.CssDef; import com.google.gwt.resources.css.ast.CssEval; import com.google.gwt.resources.css.ast.CssUrl; import com.google.gwt.resources.css.ast.CssVisitor; import com.google.gwt.thirdparty.guava.common.base.Strings; import com.google.gwt.thirdparty.guava.common.collect.BiMap; import com.google.gwt.thirdparty.guava.common.collect.HashBiMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.regex.Pattern; /** * GSS requires that constants are defined in upper case. This visitor will collect all existing * constants, create the GSS compatible name of each constant and returns a mapping of all * original names with the new generated name. * <p/> * This visitor lists also all constant nodes. */ public class DefCollectorVisitor extends CssVisitor { private static final Pattern INVALID_CHAR = Pattern.compile("[^A-Z0-9_]"); private int renamingCounter = 0; private final BiMap<String, String> defMapping; private final List<CssDef> constantNodes; private final boolean lenient; private final TreeLogger treeLogger; public DefCollectorVisitor(boolean lenient, TreeLogger treeLogger) { this.lenient = lenient; this.treeLogger = treeLogger; defMapping = HashBiMap.create(); constantNodes = new LinkedList<CssDef>(); } public Map<String, String> getDefMapping() { return defMapping; } public List<CssDef> getConstantNodes() { return constantNodes; } @Override public boolean visit(CssEval x, Context ctx) { return handleDefNode(x); } @Override public boolean visit(CssDef x, Context ctx) { return handleDefNode(x); } @Override public boolean visit(CssUrl x, Context ctx) { return handleDefNode(x); } private boolean handleDefNode(CssDef def) { constantNodes.add(def); if (!defMapping.containsKey(def.getKey())) { String upperCaseName = toUpperCase(def.getKey()); String validName = INVALID_CHAR.matcher(upperCaseName).replaceAll("_"); if (!validName.equals(upperCaseName)) { treeLogger.log(Type.WARN, "Invalid characters detected in [" + upperCaseName + "]. They have " + "been replaced [" + validName + "]"); } if (defMapping.containsValue(validName)) { if (lenient) { treeLogger.log(Type.WARN, "Two constants have the same name [" + validName + "] " + "after conversion. The second constant will be renamed automatically."); validName = renameConstant(validName); } else { throw new Css2GssConversionException( "Two constants have the same name [" + validName + "] after conversion."); } } defMapping.forcePut(def.getKey(), validName); } return false; } private String renameConstant(String originalName) { String newName; do { newName = originalName + "__RENAMED__" + renamingCounter; renamingCounter++; } while (defMapping.containsValue(newName)); return newName; } private String toUpperCase(String camelCase) { assert !Strings.isNullOrEmpty(camelCase) : "camelCase cannot be null or empty"; if (isUpperCase(camelCase)) { return camelCase; } StringBuilder output = new StringBuilder().append(Character.toUpperCase(camelCase.charAt(0))); for (int i = 1; i < camelCase.length(); i++) { char c = camelCase.charAt(i); if (Character.isUpperCase(c)) { output.append('_').append(c); } else { output.append(Character.toUpperCase(c)); } } return output.toString(); } private boolean isUpperCase(String camelCase) { for (int i = 0; i < camelCase.length(); i++) { if (Character.isLowerCase(camelCase.charAt(i))) { return false; } } return true; } }