/* * Copyright (C) 2011 Red Hat, Inc. and/or its affiliates. * * 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 org.jboss.errai.codegen.util; import org.mvel2.util.ParseTools; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLDecoder; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * @author Mike Brock */ public class QuickDeps { private static final Set<String> RESERVED_KEYWORDS = new HashSet<String>() { { add("abstract"); add("assert"); add("boolean"); add("break"); add("byte"); add("case"); add("catch"); add("char"); add("class"); add("const"); add("continue"); add("default"); add("do"); add("double"); add("else"); add("enum"); add("extends"); add("final"); add("finally"); add("float"); add("for"); add("goto"); add("if"); add("implements"); add("import"); add("instanceof"); add("int"); add("interface"); add("long"); add("native"); add("new"); add("package"); add("private"); add("protected"); add("public"); add("return"); add("short"); add("static"); add("strictfp"); add("super"); add("switch"); add("synchronized"); add("this"); add("throw"); add("throws"); add("transient"); add("try"); add("void"); add("volatile"); add("while"); add("false"); add("null"); add("true"); } }; private static final Predicate DEFAULT_PREDICATE = new Predicate() { @Override public boolean processPackage(String packageName) { return true; } @Override public boolean processClass(String className) { return true; } }; public static Set<String> getQuickTypeDependencyList(final String javaSource, ClassLoader classLoader) { return getQuickTypeDependencyList(javaSource, classLoader, DEFAULT_PREDICATE); } public static Set<String> getQuickTypeDependencyList(final String javaSource, ClassLoader classLoader, Predicate predicate) { try { if (classLoader == null) { classLoader = Thread.currentThread().getContextClassLoader(); } final IdentifierTokenizer identifierTokenizer = new IdentifierTokenizer(javaSource); final Map<String, String> imports = new HashMap<String, String>(32); final Set<String> wildcardPackages = new HashSet<String>(32); wildcardPackages.add("java.lang"); final Set<String> usedTypes = new HashSet<String>(100); boolean firstClass = true; String clazzName = null; String packageName = ""; String token; while ((token = identifierTokenizer.nextToken()) != null) { if (RESERVED_KEYWORDS.contains(token)) { if ("import".equals(token)) { if ((token = identifierTokenizer.nextToken()).charAt(token.length() - 1) == '*') { wildcardPackages.add(token.substring(0, token.lastIndexOf('.'))); } else { imports.put(token, token); imports.put(token.substring(token.lastIndexOf('.') + 1), token); } } else if ("package".equals(token)) { wildcardPackages.add(token = identifierTokenizer.nextToken()); if (!predicate.processPackage(token)) { return Collections.emptySet(); } packageName = token.concat("."); } else if ("class".equals(token)) { if (firstClass) { firstClass = false; final String fqcn = packageName + (clazzName = identifierTokenizer.nextToken()); if (!predicate.processClass(fqcn)) { return Collections.emptySet(); } usedTypes.add(fqcn); } else { final String innerClassName = packageName .concat(clazzName) .concat("$") .concat(token = identifierTokenizer.nextToken()); usedTypes.add(innerClassName); imports.put(token, innerClassName); } } continue; } if (token.endsWith(".class")) { token = token.substring(0, token.length() - 6); } /** * This handles the case where there's whitespace or a comment after the union operator. */ else if (token.charAt(token.length() - 1) == '.') { token = token.substring(0, token.length() - 1); } if (imports.containsKey(token)) { usedTypes.add(imports.get(token)); } else { wildcardMatch(classLoader, imports, wildcardPackages, usedTypes, token); } } return usedTypes; } catch (IOException e) { throw new RuntimeException("error reading filesystem", e); } } private static void wildcardMatch(final ClassLoader classLoader, final Map<String, String> imports, final Set<String> wildcardPackages, final Set<String> usedTypes, final String name) throws IOException { for (final String pkg : wildcardPackages) { final String fqcn = pkg.concat(".").concat(name); final String slashified = fqcn.replace('.', '/'); final String source = slashified.concat(".java"); final String clazz = slashified.concat(".class"); URL url; if ((url = classLoader.getResource(source)) != null || (url = classLoader.getResource(clazz)) != null) { String urlFile = URLDecoder.decode(url.getFile(), "UTF-8"); int split; if ((split = urlFile.lastIndexOf('!')) != -1) { urlFile = urlFile.substring(split + 2); } final String path = URLDecoder.decode(new File(urlFile).getCanonicalFile().toURI().getPath(), "UTF-8"); System.out.println("urlFile: " + urlFile); System.out.println(" to: " + path); System.out.println(" source: " + source); System.out.println(" clazz: " + clazz); if (urlFile.endsWith(source) || urlFile.endsWith(clazz)) { if (split != -1 || path.equalsIgnoreCase(urlFile)) { imports.put(fqcn, fqcn); imports.put(name, fqcn); usedTypes.add(fqcn); } return; } } } } private static class IdentifierTokenizer { private final String expr; private int i; private int startToken; private boolean tokenCapture; private IdentifierTokenizer(final String expr) { this.expr = expr; } public String nextToken() { char c; for (; i < expr.length(); i++) { switch (c = expr.charAt(i)) { case '"': case '\'': if (i < expr.length()) { i = captureStringLiteral(c, expr, i, expr.length()) + 1; } break; case '/': if (tokenCapture) { tokenCapture = false; return expr.substring(startToken, i).trim(); } if (i < expr.length() && expr.charAt(i + 1) == '*') { i += 2; while (i < expr.length() && !(c == '*' && expr.charAt(i + 1) == '/')) { c = expr.charAt(++i); } i++; } else if (expr.charAt(i + 1) == '/') { while (i < expr.length() && c != '\n') { c = expr.charAt(++i); } } break; default: if (ParseTools.isIdentifierPart(c) || c == '.' || (c == '*' && expr.charAt(i - 1) == '.')) { if (!tokenCapture) { startToken = i; tokenCapture = true; } } else if (tokenCapture) { tokenCapture = false; return expr.substring(startToken, i); } } } return null; } } public static int captureStringLiteral(final char type, final String expr, int cursor, final int end) { while (++cursor < end && expr.charAt(cursor) != type) { if (expr.charAt(cursor) == '\\') cursor++; } if (cursor >= end || expr.charAt(cursor) != type) { throw new RuntimeException("unterminated string literal"); } return cursor; } public static interface Predicate { public boolean processPackage(String packageName); public boolean processClass(String className); } }