Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/sun/security/util/Resources/NewResourcesNames.java
38853 views
/*1* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.BufferedReader;24import java.io.File;25import java.io.FileInputStream;26import java.io.FileOutputStream;27import java.io.InputStreamReader;28import java.io.PrintWriter;29import java.util.HashMap;30import java.util.HashSet;31import java.util.ListResourceBundle;32import java.util.Map;33import java.util.Set;3435/**36* Prepares new key names for Resources.java.37* 6987827: security/util/Resources.java needs improvement38*39* Run inside jdk/src/share/classes:40*41* java NewResourcesNames $(42* for a in $(find com/sun/security sun/security javax/security -type f); do43* egrep -q '(ResourcesMgr.getString|rb.getString)' $a && echo $a44* done)45*46* Before running this tool, run the following two commands to make sure there47* are only these 2 types of calls into the resources:48* for a in `find com/sun/security sun/security javax/security -type f`; do49* cat $a | perl -ne 'print if /\bResourcesMgr\b/'; done |50* grep -v ResourcesMgr.getString51* for a in `find com/sun/security sun/security -type f`; do52* cat $a | perl -ne 'print if /\brb\b/'; done |53* grep -v rb.getString54*/55class NewResourcesNames {5657// Max length of normalized names58static int MAXLEN = 127;5960static String[] resources = {61"sun/security/tools/jarsigner/Resources.java",62"sun/security/tools/keytool/Resources.java",63"sun/security/tools/policytool/Resources.java",64"sun/security/util/Resources.java",65"sun/security/util/AuthResources.java",66};6768public static void main(String[] args) throws Exception {6970// Load all names inside resources files71Map<String,String> allnames = loadResources();7273// Modify the callers. There are two patterns:74// 1. ResourcesMgr.getString("75// used by most JAAS codes76// 2. rb.getString("77// used by tools78Set<String> allfound = new HashSet<String>();79for (String arg: args) {80allfound.addAll(rewriteFile(arg, "ResourcesMgr.getString(\""));81allfound.addAll(rewriteFile(arg, "rb.getString(\""));82}8384// Special case 1: KeyTool's enum definition of commands and options85allfound.addAll(keyToolEnums());8687// Special case 2: PolicyFile called this 4 times88allfound.addAll(rewriteFile("sun/security/provider/PolicyFile.java",89"ResourcesMgr.getString(POLICY+\""));9091// During the calls above, you can read sth like:92//93// Working on com/sun/security/auth/PolicyParser.java94// GOOD match is 1795//96// This means a " exists right after getString(. Sometimes you see97//98// Working on sun/security/tools/keytool/Main.java99// BAD!! pmatch != match: 212 != 209100// Working on sun/security/provider/PolicyFile.java101// BAD!! pmatch != match: 14 != 10102//103// which is mismatch. There are only two such special cases list above.104// For KeyTool, there are 3 calls for showing help. For PolicyTool, 3105// for name prefixed with POLICY. They are covered in the two special106// cases above.107108// Names used but not defined. This is mostly error, except for109// special case 2 above. So it's OK to see 3 entries red here110if (!allnames.keySet().containsAll(allfound)) {111err("FATAL: Undefined names");112for (String name: allfound) {113if (!allnames.keySet().contains(name)) {114err(" " + name);115}116}117}118119// Names defined but not used. Mostly this is old entries not removed.120// When soemone remove a line of code, he dares not remove the entry121// in case it's also used somewhere else.122if (!allfound.containsAll(allnames.keySet())) {123System.err.println("WARNING: Unused names");124for (String name: allnames.keySet()) {125if (!allfound.contains(name)) {126System.err.println(allnames.get(name));127System.err.println(" " + normalize(name));128System.err.println(" [" + name + "]");129}130}131}132}133134135/**136* Loads the three resources files. Saves names into a Map.137*/138private static Map<String,String> loadResources() throws Exception {139140// Name vs Resource141Map<String,String> allnames = new HashMap<String,String>();142143for (String f: resources) {144String clazz =145f.replace('/', '.').substring(0, f.length()-5);146147Set<String> expected = loadClass(clazz);148Set<String> found = rewriteFile(f, "{\"");149150// This is to check that word parsing is identical to Java thinks151if (!expected.equals(found)) {152throw new Exception("Expected and found do not match");153}154155for (String name: found) {156allnames.put(name, f);157}158}159return allnames;160}161162/**163* Special case treat for enums description in KeyTool164*/165private static Set<String> keyToolEnums() throws Exception {166167Set<String> names = new HashSet<String>();168169String file = "sun/security/tools/keytool/Main.java";170System.err.println("Working on " + file);171File origFile = new File(file);172File tmpFile = new File(file + ".tmp");173origFile.renameTo(tmpFile);174tmpFile.deleteOnExit();175176BufferedReader br = new BufferedReader(177new InputStreamReader(new FileInputStream(tmpFile)));178PrintWriter out = new PrintWriter(new FileOutputStream(origFile));179180int stage = 0; // 1. commands, 2. options, 3. finished181int match = 0;182183while (true) {184String s = br.readLine();185if (s == null) {186break;187}188if (s.indexOf("enum Command") >= 0) stage = 1;189else if (s.indexOf("enum Option") >= 0) stage = 2;190else if (s.indexOf("private static final String JKS") >= 0) stage = 3;191192if (stage == 1 || stage == 2) {193if (s.indexOf("(\"") >= 0) {194match++;195int p1, p2;196if (stage == 1) {197p1 = s.indexOf("\"");198p2 = s.indexOf("\"", p1+1);199} else {200p2 = s.lastIndexOf("\"");201p1 = s.lastIndexOf("\"", p2-1);202}203String name = s.substring(p1+1, p2);204names.add(name);205out.println(s.substring(0, p1+1) +206normalize(name) +207s.substring(p2));208} else {209out.println(s);210}211} else {212out.println(s);213}214}215br.close();216out.close();217System.err.println(" GOOD match is " + match);218return names;219}220221/**222* Loads a resources using JRE and returns the names223*/224private static Set<String> loadClass(String clazz) throws Exception {225ListResourceBundle lrb =226(ListResourceBundle)Class.forName(clazz).newInstance();227Set<String> keys = lrb.keySet();228Map<String,String> newold = new HashMap<String,String>();229boolean dup = false;230// Check if normalize() creates dup entries. This is crucial.231for (String k: keys) {232String key = normalize(k);233if (newold.containsKey(key)) {234err("Dup found for " + key + ":");235err("["+newold.get(key)+"]");236err("["+k+"]");237dup = true;238}239newold.put(key, k);240}241if (dup) throw new Exception();242return keys;243}244245/**246* Rewrites a file using a pattern. The name string should be right after247* the pattern. Note: pattern ignores whitespaces. Returns names found.248*/249private static Set<String> rewriteFile(String file, String pattern)250throws Exception {251252System.err.println("Working on " + file);253Set<String> names = new HashSet<String>();254255int plen = pattern.length();256int match = 0;257258// The bare XXX.getString is also matched. Sometimes getString is259// called but does not use literal strings. This is harder to solve.260261int pmatch = 0;262int pheadlen = plen - 2;263String phead = pattern.substring(0, plen-2);264265// The non-whitespace chars read since, used to check for pattern266StringBuilder history = new StringBuilder();267int hlen = 0;268269File origFile = new File(file);270File tmpFile = new File(file + ".tmp");271origFile.renameTo(tmpFile);272tmpFile.deleteOnExit();273274FileInputStream fis = new FileInputStream(tmpFile);275FileOutputStream fos = new FileOutputStream(origFile);276277while (true) {278int ch = fis.read();279if (ch < 0) break;280if (!Character.isWhitespace(ch)) {281history.append((char)ch);282hlen++;283if (pheadlen > 0 && hlen >= pheadlen &&284history.substring(hlen-pheadlen, hlen).equals(phead)) {285pmatch++;286}287}288289if (hlen >= plen &&290history.substring(hlen-plen, hlen).equals(pattern)) {291match++;292history = new StringBuilder();293hlen = 0;294295fos.write(ch);296297// Save a name298StringBuilder sb = new StringBuilder();299// Save things after the second ". Maybe it's an end, maybe300// it's just literal string concatenation.301StringBuilder tail = new StringBuilder();302303boolean in = true; // inside name string304while (true) {305int n = fis.read();306if (in) {307if (n == '\\') {308int second = fis.read();309switch (second) {310case 'n': sb.append('\n'); break;311case 'r': sb.append('\r'); break;312case 't': sb.append('\t'); break;313case '"': sb.append('"'); break;314default: throw new Exception(String.format(315"I don't know this escape: %s%c",316sb.toString(), second));317}318} else if (n == '"') {319in = false;320// Maybe string concat? say bytes until clear321tail = new StringBuilder();322tail.append('"');323} else {324sb.append((char)n);325}326} else {327tail.append((char)n);328if (n == '"') { // string concat, in again329in = true;330} else if (n == ',' || n == ')') { // real end331break;332} else if (Character.isWhitespace(n) || n == '+') {333// string concat334} else {335throw new Exception("Not a correct concat");336}337}338}339String s = sb.toString();340names.add(s);341fos.write(normalize(s).getBytes());342fos.write(tail.toString().getBytes());343} else {344fos.write(ch);345}346}347348// Check pheadlen > 0. Don't want to mess with rewrite for resources349if (pheadlen > 0 && pmatch != match) {350err(" BAD!! pmatch != match: " + pmatch + " != " + match);351} else {352System.err.println(" GOOD match is " + match);353}354355fis.close();356fos.close();357return names;358}359360/**361* Normalize a string. Rules:362*363* 1. If all spacebar return "nSPACE", n is count364* 2. If consisting at least one alphanumeric:365* a. All alphanumeric remain366* b. All others in a row goes to a single ".", even if at head or tail367* 3. Otherwise:368* a. "****\n\n" to "STARNN", special case369* b. the English name if first char in *,.\n():'"370*371* Current observations show there's no dup, Hurray! Otherwise, add more372* special cases.373*/374private static String normalize(String s) throws Exception {375boolean needDot = false;376377// All spacebar case378int n = 0;379for (char c: s.toCharArray()) {380if (c == ' ') n++;381else n = -10000;382}383if (n == 1) return "SPACE";384else if (n > 1) return "" + n + "SPACE";385386StringBuilder sb = new StringBuilder();387int dotpos = -1;388for (int i=0; i<s.length(); i++) {389char c = s.charAt(i);390if (Character.isLetter(c) || Character.isDigit(c) ||391c == '{' || c == '}') {392if (needDot) {393// Rememeber the last dot, we want shorter form nice394if (sb.length() <= MAXLEN) dotpos = sb.length();395// "." only added when an alphanumeric is seen. This makes396// sure sb is empty when there's no alphanumerics at all397sb.append(".");398}399sb.append(c);400needDot = false;401} else {402needDot = true;403}404}405406// No alphanemeric?407if (sb.length() == 0) {408if (s.contains("*") && s.contains("\n")) {409return "STARNN";410}411for (char c: s.toCharArray()) {412switch (c) {413case '*': return "STAR";414case ',': return "COMMA";415case '.': return "PERIOD";416case '\n': return "NEWLINE";417case '(': return "LPARAM";418case ')': return "RPARAM";419case ':': return "COLON";420case '\'': case '"': return "QUOTE";421}422}423throw new Exception("Unnamed char: [" + s + "]");424}425426// tail "." only added when there are alphanumerics427if (needDot) sb.append('.');428String res = sb.toString();429if (res.length() > MAXLEN) {430if (dotpos < 0) throw new Exception("No dot all over? " + s);431return res.substring(0, dotpos);432} else {433return res;434}435}436437private static void err(String string) {438System.out.println("\u001b[1;37;41m" + string + "\u001b[m");439}440}441442443