ee.jakarta.tck.jsonp.signaturetest.PackageList Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
/*
* $Id$
*/
package ee.jakarta.tck.jsonp.signaturetest;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Logger;
/**
* This class represents a package list file. A package list file is used in
* conjunction with a set of signature files to execute API signature tests.
* Users specify which set of package APIs are verified. Each package's
* signature is verified independently. As such all valid sub-packages must be
* excluded while a package's signature is being verified. This allows API check
* to determine incompatible additional packages included in a distribution.
*
* This class builds a package list file when signatures are recorded and
* provides an API to provide valid sub-package lists when signatures are played
* back (verified).
*
* In record mode, this class reads the existing package list file, if one
* exists, and removes the package (as well as sub-packages) that are currently
* being recorded. All package names read from the existing package list file
* are held in a tree set which sorts the package names and keeps duplicate
* package names from ocurring. The user can then instruct this class to write
* out the package list file. At this point this class reads the currently
* recorded signature file and extracts each package names and adds it to the
* tree set. After this step the previous package list file is saved as a backup
* and the new package list file is written to disk.
*
* In playback mode, this class reads the contents of the package list file and
* stores each package name in a tree set. Users can then invoke the
* getSubPackages method to retrieve the valid sub-packages for any given
* package. This is done by simply examining the package names in the tree set
* and returning any package name that starts with the parent package name and a
* trailing period character.
*/
class PackageList {
private static final Logger LOGGER = Logger.getLogger(PackageList.class.getName());
// Any line in the packageFile starting with this character is a comment
private static final char COMMENT_CHAR = '#';
private static final String BACKUP_EXT = ".bak";
// File containing the list of packages and sub-packages
private File packageFile;
// Signature file where the package signatures were recorded
private File sigFile;
// Name of the package being recorded
private String additionalPackageName;
// Name of packages and sub-packages in the
private Set packageNames = new TreeSet();
/**
* Creates an instance of the PackageList class. The PackageList instance
* reads the specified package file and populates it's internal state with the
* package names found in this file. Users should use this c'tor when playing
* back signature files. Users can init the PackageList instance then use the
* "String[] getSubPackages(String packageName)" method to get the list of
* valid sub-packages for every package who's signature is being verified.
*
* @param packageFileName
* The name of the file that contains the package list. This file
* contains the names of all the packages that exist across all the
* signature files that makeup this deliverable. This file is used to
* generate a list of valid sub-packages that must be exclued when
* testing theor parent package's signature.
*
* @throws Exception
* when the packageFileName does not exist.
*/
public PackageList(String packageFileName) throws Exception {
packageFile = new File(packageFileName);
if (packageFile.exists() && packageFile.isFile()) {
extractExistingPackageNames();
} else {
throw new FileNotFoundException(packageFileName);
}
}
/**
* Creates an instance of the PackageList class. The PackageList instance
* reads the contents of the packageFileName and stores it in it's internal
* state. Next, any packages whos name starts with the specified packageName
* are removed from the internal package list. This is done because this is
* the package being recorded and we need to remove any previously recorded
* package names in case any sub-packages have been removed since the last
* time the signatures were recorded. Users should use this c'tor when they
* are recording signature files never during playback.
*
* @param packageName
* The name of the package whos signatures are being recorded (along
* with sub-packages).
* @param sigFileName
* The name of the file that contains the recored signatures.
* @param packageFileName
* The name of the file that contains the package list. This file
* contains the names of all the packages that exist across all the
* signature files that makeup this deliverable. This file is used to
* generate a list of valid sub-packages that must be exclued when
* testing their parent package's signature.
*
* @throws Exception
* when an error occurs reading the packageFileName or the
* sigFileName does not exist.
*/
public PackageList(String packageName, String sigFileName,
String packageFileName) throws Exception {
this.additionalPackageName = packageName;
sigFile = new File(sigFileName);
if (!sigFile.exists() || !sigFile.isFile()) {
throw new FileNotFoundException(sigFileName);
}
packageFile = new File(packageFileName);
if (packageFile.exists() && packageFile.isFile()) {
extractExistingPackageNames();
removeExistingPackage();
}
}
/**
* Read the package names stored in the package list file. Each package name
* found in the package list file is added to the internal tree set.
*
* @throws Exception
* if there is an error opening or reading the package list file.
*/
private void extractExistingPackageNames() throws Exception {
BufferedReader in = new BufferedReader(new FileReader(packageFile));
String line;
String trimLine;
try {
while ((line = in.readLine()) != null) {
trimLine = line.trim();
if (isComment(trimLine) || "".equals(trimLine)) {
continue;
}
packageNames.add(trimLine);
}
} finally {
try {
in.close();
} catch (Exception e) {
}
}
}
/**
* Returns true if the specified string starts with a comment character as
* denoted by the COMMENT_CHAR constant.
*
* @param line
* Determins of this line is a comment line
*
* @return boolean True if the specified line is a comment line else false.
*/
private boolean isComment(String line) {
if (line == null) {
return false;
}
String theLine = line.trim();
if (theLine.length() > 0) {
return (theLine.charAt(0) == COMMENT_CHAR);
}
return false;
}
/**
* Removes package names from the package list file. The packages that are
* removed are the ones currently being recorded. The packages being recorded
* is denoted by this.additionalPackageName. This includes any sub-packages of
* the additionalPackageName. This step is necessary in the cases where a
* sub-package has been removed from a parent package in between signature
* recordings.
*/
private void removeExistingPackage() {
String delPackage = this.additionalPackageName;
String packageName;
List delPkgs = new ArrayList();
// iterate over package set and find package names to remove
for (Iterator i = packageNames.iterator(); i.hasNext();) {
packageName = (String) i.next();
if (packageName.startsWith(delPackage)) {
delPkgs.add(packageName);
}
}
// actually remove the package names from the set
for (int i = 0; i < delPkgs.size(); i++) {
packageName = (String) (delPkgs.get(i));
packageNames.remove(packageName);
LOGGER.info(
"PackageList.removeExistingPackage() \"" + packageName + "\"");
}
}
/**
* Write the package list out to the package list file. This is done by
* reading all the package names in the specified signature file and adding
* them to the internal tree set. Then the old package list file is removed
* and the new package list file is written out.
*
* @throws Exception
* if there is a problem removing the existing package file or
* writting the new package list file.
*/
public void writePkgListFile() throws Exception {
readPkgsFromSigFile();
removePkgFile();
writePkgFile();
}
/**
* Extract the package name from the specified string. The specified string
* should have the form: "package jakarta.ejb;"
*
* @param packageLine
* The string containing the package name.
*
* @return String The extracted package name.
*
* @throws Exception
* if the specified string does not conform to the expected format.
*/
private String parsePackageName(String packageLine) throws Exception {
// sig test framework doesn't have the concept of package entries
// as the ApiCheck signature format does.
// Instead, we need to parse an entry similar to this:
// CLSS public jakarta.some.package.SomeClass
return packageLine.substring(packageLine.lastIndexOf(' ') + 1,
packageLine.lastIndexOf('.'));
}
/**
* Reads the package names from the signature file. Each package name that is
* read is added to this classes internal tree set.
*
* @throws Exception
* if there is an error opening or reading the signature file.
*/
private void readPkgsFromSigFile() throws Exception {
BufferedReader in = new BufferedReader(new FileReader(sigFile));
String line;
String trimLine;
try {
while ((line = in.readLine()) != null) {
trimLine = line.trim();
if (trimLine.startsWith("CLSS")) {
packageNames.add(parsePackageName(trimLine));
}
}
} finally {
try {
in.close();
} catch (Exception e) {
}
}
}
/**
* Removes the existing package list file. The package list file is actually
* moved to a backup file if it exists. The old backup is lost.
*
* @throws Exception
* if there is an error moving the current package list file to a
* backup file.
*/
private void removePkgFile() throws Exception {
File backupPkgFile = new File(packageFile.getPath() + BACKUP_EXT);
if (backupPkgFile.exists() && backupPkgFile.isFile()) {
backupPkgFile.delete();
}
if (packageFile.isFile() && packageFile.exists()) {
File copyPackageFile = new File(packageFile.getPath());
copyPackageFile.renameTo(backupPkgFile);
}
}
/**
* Write a simple header to the package list file to explain what the file is.
*
* @param out
* The BufferedWriter to dump the header to.
*
* @throws Exception
* if there is any errors writing the header to the specified
* BufferedWriter.
*/
private void writeHeader(BufferedWriter out) throws Exception {
out.write(COMMENT_CHAR);
out.write(COMMENT_CHAR);
out.newLine();
out.write(COMMENT_CHAR + " This file contains a list of all the packages");
out.newLine();
out.write(COMMENT_CHAR + " contained in the signature files for this");
out.newLine();
out.write(
COMMENT_CHAR + " deliverable. This file is used to exclude valid");
out.newLine();
out.write(COMMENT_CHAR + " sub-packages from being verified when their");
out.newLine();
out.write(COMMENT_CHAR + " parent package's signature is checked.");
out.newLine();
out.write(COMMENT_CHAR);
out.write(COMMENT_CHAR);
out.newLine();
out.newLine();
}
/**
* Write the list of package names out to a package list file.
*
* @throws Exception
* if there is an error creating and writting the package list file.
*/
private void writePkgFile() throws Exception {
BufferedWriter out = null;
try {
out = new BufferedWriter(new FileWriter(packageFile));
writeHeader(out);
for (Iterator i = packageNames.iterator(); i.hasNext();) {
String packageName = (String) i.next();
out.write(packageName);
out.newLine();
LOGGER.info("PackageList.writePkgFile() \"" + packageName + "\"");
}
} finally {
if (out != null) {
out.close();
}
}
}
/**
* Returns the list of sub-packages that exist in the specified package name.
*
* @param pkgName
* The name of the package we want the sub-package list for.
*
* @return String[] The sub-packages that live under the specified parent
* package.
*/
public String[] getSubPackages(String pkgName) {
List result = new ArrayList();
String subPackageName = pkgName + ".";
for (Iterator i = packageNames.iterator(); i.hasNext();) {
String packageName = (String) i.next();
if (packageName.startsWith(subPackageName)) {
result.add(packageName);
}
}
return (String[]) (result.toArray(new String[result.size()]));
}
/**
* Returns the list of sub-packages that exist in the specified package name.
* The returned string matches the API check format of specifying multiple
* packages with a single string. Each package name is separated with the "+"
* character.
*
* @param pkgName
* The name of the package we want the sub-package list for.
*
* @return String The sub-packages that live under the specified parent
* package.
*/
public String getSubPackagesFormatted(String pkgName) {
StringBuffer formattedResult = new StringBuffer();
String[] result = getSubPackages(pkgName);
for (int i = 0; i < result.length; i++) {
formattedResult.append(result[i]);
if (i < (result.length - 1)) {
formattedResult.append("+");
}
}
return formattedResult.toString();
}
/*
* Test Driver
*/
public static void main(String[] args) throws Exception {
LOGGER.info("\n\n*** Creating package list file ***\n\n");
PackageList list = new PackageList("jakarta.ejb",
"/home/ryano/cts-tools-master/tools/api-check/test/jakarta.ejb.sig_2.1",
"/home/ryano/cts-tools-master/tools/api-check/test/pkg-list.txt");
list.writePkgListFile();
LOGGER.info("\n\n*** Reading sub-packages from package list file ***\n\n");
PackageList readList = new PackageList(
"/home/ryano/cts-tools-master/tools/api-check/test/pkg-list.txt");
LOGGER.info(readList.getSubPackages("jakarta.ejb").toString());
LOGGER.info(readList.getSubPackagesFormatted("jakarta.ejb").toString());
}
} // end class PackageList
© 2015 - 2024 Weber Informatics LLC | Privacy Policy