| /* |
| * Copyright 2008 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.tools.apichecker; |
| |
| import com.google.gwt.core.ext.TreeLogger; |
| import com.google.gwt.core.ext.UnableToCompleteException; |
| import com.google.gwt.core.ext.typeinfo.JPackage; |
| import com.google.gwt.core.ext.typeinfo.NotFoundException; |
| import com.google.gwt.core.ext.typeinfo.TypeOracle; |
| import com.google.gwt.dev.javac.CompilationUnit; |
| import com.google.gwt.dev.javac.JdtCompiler; |
| import com.google.gwt.dev.javac.TypeOracleMediator; |
| import com.google.gwt.dev.javac.impl.FileCompilationUnit; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.net.MalformedURLException; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.Vector; |
| |
| /** |
| * Encapsulates an API. |
| * |
| */ |
| public class ApiContainer { |
| |
| private HashMap<String, ApiPackage> apiPackages = new HashMap<String, ApiPackage>(); |
| private HashMap<String, String> excludedFiles = null; |
| private TreeLogger logger = null; |
| private String name = null; |
| private int numFilesCount = 0; |
| private Collection<File> sourceTrees = null; |
| private TypeOracle typeOracle = null; |
| |
| public ApiContainer(String fileName, String suffix, TreeLogger logger) |
| throws IllegalArgumentException, MalformedURLException, |
| FileNotFoundException, IOException, NotFoundException, |
| UnableToCompleteException { |
| this.logger = logger; |
| if (fileName == null) { |
| throw new IllegalArgumentException("fileName is null"); |
| } |
| FileInputStream fis = new FileInputStream(fileName); |
| Properties config = new Properties(); |
| config.load(fis); |
| String apiName = config.getProperty("name" + suffix); |
| String allSourceFiles = config.getProperty("sourceFiles" + suffix); |
| String allExcludedFiles = config.getProperty("excludedFiles" + suffix); |
| |
| if (allExcludedFiles == null) { |
| allExcludedFiles = ""; |
| } |
| if (apiName == null || allSourceFiles == null) { |
| throw new IllegalArgumentException( |
| "in apiContainer constructor, either name (" + apiName |
| + ") or sourceFiles (" + allSourceFiles + ") is null"); |
| } |
| logger.log(TreeLogger.DEBUG, "read from config file " + fileName |
| + ", name = " + apiName + ", allSourceFiles = " + allSourceFiles |
| + ", allExcludedFiles = " + allExcludedFiles, null); |
| |
| String sourceFilesArray[] = allSourceFiles.split(":"); |
| Collection<File> fileCollection = new Vector<File>(); |
| for (String tempStr : sourceFilesArray) { |
| tempStr = tempStr.trim(); |
| fileCollection.add(new File(tempStr)); |
| } |
| this.sourceTrees = fileCollection; |
| if (allExcludedFiles.equals("")) { |
| this.excludedFiles = generateCanonicalHashmap(new String[0]); |
| } else { |
| String excludedFilesArray[] = allExcludedFiles.split(":"); |
| this.excludedFiles = generateCanonicalHashmap(excludedFilesArray); |
| } |
| this.name = apiName; |
| createTypeOracleFromSources(); |
| initializeApiPackages(); |
| } |
| |
| // constructor is used while testing |
| ApiContainer(String name, TreeLogger logger, TypeOracle typeOracle) { |
| this.name = name; |
| this.logger = logger; |
| this.typeOracle = typeOracle; |
| initializeApiPackages(); |
| } |
| |
| public ApiPackage getApiPackage(String packageName) { |
| return apiPackages.get(packageName); |
| } |
| |
| public HashSet<String> getApiPackageNames() { |
| return new HashSet<String>(apiPackages.keySet()); |
| } |
| |
| public TreeLogger getLogger() { |
| return logger; |
| } |
| |
| private void addCompilationUnitsInPath(Set<CompilationUnit> units, |
| File sourcePathEntry) throws NotFoundException, IOException, |
| UnableToCompleteException { |
| File[] files = sourcePathEntry.listFiles(); |
| if (files == null) { |
| // No files found. |
| return; |
| } |
| |
| for (int i = 0; i < files.length; i++) { |
| final File file = files[i]; |
| // Ignore files like .svn and .cvs |
| if (file.getName().startsWith(".") || file.getName().equals("CVS")) { |
| continue; |
| } |
| if (isExcludedFile(file.getCanonicalPath())) { |
| // do not process the subtree |
| logger.log(TreeLogger.DEBUG, "not traversing " |
| + file.toURL().toString(), null); |
| continue; |
| } |
| if (file.isFile()) { |
| String pkgName = null; |
| if (file.getName().endsWith("java")) { |
| pkgName = extractPackageNameFromFile(file); |
| logger.log(TreeLogger.DEBUG, "pkgName = " + pkgName + ", file = " |
| + file.toString(), null); |
| } |
| if (isValidPackage(pkgName, sourcePathEntry.toURL().toString())) { |
| // Add if it's a source file and the package and fileNames are okay |
| CompilationUnit unit = new FileCompilationUnit(file, pkgName); |
| units.add(unit); |
| numFilesCount++; |
| } else { |
| logger.log(TreeLogger.SPAM, " not adding file " + file.toURL(), null); |
| } |
| } else { |
| // Recurse into subDirs |
| addCompilationUnitsInPath(units, file); |
| } |
| } |
| } |
| |
| private void createTypeOracleFromSources() throws NotFoundException, |
| IOException, UnableToCompleteException { |
| |
| numFilesCount = 0; |
| TypeOracleMediator mediator = new TypeOracleMediator(); |
| Set<CompilationUnit> units = new HashSet<CompilationUnit>(); |
| for (Iterator<File> i = sourceTrees.iterator(); i.hasNext();) { |
| addCompilationUnitsInPath(units, i.next()); |
| } |
| JdtCompiler.compile(units); |
| mediator.refresh(logger, units); |
| typeOracle = mediator.getTypeOracle(); |
| logger.log(TreeLogger.INFO, "API " + name |
| + ", Finished with building typeOracle, added " + numFilesCount |
| + " files", null); |
| } |
| |
| private String extractPackageNameFromFile(File file) { |
| if (!file.exists()) { |
| return null; |
| } |
| String fileName = null; |
| try { |
| fileName = file.toURL().toString(); |
| FileReader fr = new FileReader(file); |
| BufferedReader br = new BufferedReader(fr); |
| String str = null; |
| while ((str = br.readLine()) != null) { |
| if (str.indexOf("package") != 0) { |
| continue; |
| } |
| String parts[] = str.split("[\b\t\n\r ]+"); |
| if ((parts.length == 2) && parts[0].equals("package")) { |
| return parts[1].substring(0, parts[1].length() - 1); // the ; char |
| } |
| } |
| return null; |
| } catch (Exception ex) { |
| logger.log(TreeLogger.ERROR, |
| "error in parsing and obtaining the packageName from file " |
| + fileName + "error's message " + ex.getMessage(), ex); |
| return null; |
| } |
| } |
| |
| /** |
| * Convert a set into a HashMap for faster lookups. |
| */ |
| private HashMap<String, String> generateCanonicalHashmap(String strArray[]) |
| throws IOException { |
| HashMap<String, String> tempMap = new HashMap<String, String>(); |
| if (strArray == null) { |
| return tempMap; |
| } |
| for (String str : strArray) { |
| str = str.trim(); |
| File tempFile = new File(str); |
| str = tempFile.getCanonicalPath(); |
| tempMap.put(str, str); |
| } |
| return tempMap; |
| } |
| |
| /** |
| * Purge non API packages. |
| */ |
| private void initializeApiPackages() { |
| HashSet<JPackage> allPackages = new HashSet<JPackage>( |
| Arrays.asList(typeOracle.getPackages())); |
| Iterator<JPackage> packagesIterator = allPackages.iterator(); |
| HashSet<String> packagesNotAdded = new HashSet<String>(); |
| while (packagesIterator.hasNext()) { |
| JPackage packageObject = packagesIterator.next(); |
| if (ApiPackage.isApiPackage(packageObject)) { |
| ApiPackage apiPackageObj = new ApiPackage(packageObject, this); |
| apiPackages.put(apiPackageObj.getName(), apiPackageObj); |
| } else { |
| packagesNotAdded.add(packageObject.toString()); |
| } |
| } |
| if (packagesNotAdded.size() > 0) { |
| logger.log(TreeLogger.DEBUG, "API " + name + ": not added " |
| + packagesNotAdded.size() + " packages: " + packagesNotAdded, null); |
| } |
| if (apiPackages.size() > 0) { |
| logger.log(TreeLogger.INFO, "API " + name + apiPackages.size() |
| + " Api packages: " + apiPackages.keySet(), null); |
| } |
| } |
| |
| private boolean isExcludedFile(String fileName) { |
| String pattern = "file:"; |
| if (fileName.indexOf(pattern) == 0) { |
| fileName = fileName.substring(pattern.length()); |
| } |
| return (excludedFiles.get(fileName) != null); |
| } |
| |
| private boolean isValidPackage(String packageName, String filePath) { |
| logger.log(TreeLogger.SPAM, "packageName = " + packageName |
| + ", filePath = " + filePath, null); |
| if (packageName == null) { |
| return false; |
| } |
| int lastSlashPosition = filePath.lastIndexOf("/"); |
| if (lastSlashPosition == -1) { |
| return false; |
| } |
| String dirPath = filePath.substring(0, lastSlashPosition); |
| String packageNameAsPath = packageName.replace('.', '/'); |
| logger.log(TreeLogger.SPAM, "packageNameAsPath " + packageNameAsPath |
| + ", dirPath = " + dirPath, null); |
| return dirPath.endsWith(packageNameAsPath); |
| } |
| } |