-
Notifications
You must be signed in to change notification settings - Fork 30
/
PackageList.java
426 lines (398 loc) · 15.4 KB
/
PackageList.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
/*
* Copyright (c) 2007, 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
*/
package ee.jakarta.tck.authentication.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.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/**
* 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.
* <p/>
* 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).
* <p/>
* 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.
* <p/>
* 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 {
// 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);
System.out.println(
"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();
System.out
.println("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 {
System.out.println("\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();
System.out
.println("\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");
System.out.println(Arrays.asList(readList.getSubPackages("jakarta.ejb")));
System.out.println(readList.getSubPackagesFormatted("jakarta.ejb"));
}
} // end class PackageList