/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.ant.bnd;
import aQute.bnd.differ.Baseline.BundleInfo;
import aQute.bnd.differ.Baseline.Info;
import aQute.bnd.differ.DiffPluginImpl;
import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.Jar;
import aQute.bnd.service.diff.Delta;
import aQute.bnd.service.diff.Diff;
import aQute.bnd.version.Version;
import aQute.lib.io.IO;
import aQute.service.reporter.Reporter;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
/**
* @author Raymond Augé
* @author Andrea Di Giorgi
*/
public abstract class Baseline {
public boolean execute() throws Exception {
boolean match = true;
_headerPrinted = false;
_printWriter = null;
if (_logFile.exists()) {
_logFile.delete();
}
File logDir = _logFile.getParentFile();
logDir.mkdirs();
BaselineProcessor baselineProcessor = new BaselineProcessor();
baselineProcessor.setProperties(_properties);
Jar newJar = new Jar(_newJarFile);
if (_newCompatJarFile != null) {
newJar.addAll(new Jar(_newCompatJarFile));
}
Jar oldJar = null;
if (_oldJarFile != null) {
if (!_oldJarFile.exists() || _oldJarFile.isDirectory() ||
!_oldJarFile.canRead()) {
baselineProcessor.warning(
"Baseline file %s is invalid. Check if it exists, is " +
"readable, and is not a directory.",
_oldJarFile);
}
else {
oldJar = new Jar(_oldJarFile);
}
}
else {
oldJar = baselineProcessor.getBaselineJar();
}
try {
if (oldJar == null) {
return match;
}
aQute.bnd.differ.Baseline baseline = new aQute.bnd.differ.Baseline(
baselineProcessor, new DiffPluginImpl());
Set<Info> infos = baseline.baseline(newJar, oldJar, null);
if (infos.isEmpty()) {
return match;
}
BundleInfo bundleInfo = baseline.getBundleInfo();
if (_forceCalculatedVersion) {
bundleInfo.suggestedVersion = calculateVersion(
bundleInfo.olderVersion, infos);
}
else if (hasPackageRemoved(infos)) {
bundleInfo.suggestedVersion = new Version(
bundleInfo.olderVersion.getMajor() + 1, 0, 0);
}
int compare = bundleInfo.suggestedVersion.compareTo(
bundleInfo.newerVersion.getWithoutQualifier());
if ((compare > 0) || (_forceCalculatedVersion && (compare != 0))) {
bundleInfo.mismatch = true;
}
if (bundleInfo.mismatch) {
match = false;
updateBundleVersion(
bundleInfo.newerVersion, bundleInfo.suggestedVersion);
}
Info[] infosArray = infos.toArray(new Info[infos.size()]);
Arrays.sort(
infosArray,
new Comparator<Info>() {
@Override
public int compare(Info info1, Info info2) {
return info1.packageName.compareTo(info2.packageName);
}
});
doHeader(bundleInfo);
for (Info info : infosArray) {
if (info.mismatch) {
match = false;
}
Diff packageDiff = info.packageDiff;
Delta delta = packageDiff.getDelta();
if (_forceVersionOneOnAddedPackages && (delta == Delta.ADDED) &&
bundleInfo.newerVersion.equals(info.newerVersion)) {
info.suggestedVersion = Version.ONE;
}
String warnings = "-";
Version newerVersion = info.newerVersion;
Version suggestedVersion = info.suggestedVersion;
if (suggestedVersion != null) {
if (newerVersion.compareTo(suggestedVersion) > 0) {
match = false;
warnings = "EXCESSIVE VERSION INCREASE";
}
else if (newerVersion.compareTo(suggestedVersion) < 0) {
warnings = "VERSION INCREASE REQUIRED";
}
}
if (delta == Delta.REMOVED) {
warnings = "PACKAGE REMOVED";
}
else if (delta == Delta.UNCHANGED) {
boolean newVersionSuggested = false;
if (suggestedVersion.compareTo(newerVersion) > 0) {
warnings = "VERSION INCREASE SUGGESTED";
newVersionSuggested = true;
}
else if (suggestedVersion.compareTo(newerVersion) < 0) {
warnings = "EXCESSIVE VERSION INCREASE";
newVersionSuggested = true;
}
if (!newVersionSuggested && !info.mismatch) {
continue;
}
}
boolean correctPackageInfo = generatePackageInfo(info, delta);
if (!correctPackageInfo) {
if (delta == Delta.ADDED) {
warnings = "PACKAGE ADDED, MISSING PACKAGEINFO";
}
else if (delta == Delta.REMOVED) {
warnings = "PACKAGE REMOVED, UNNECESSARY PACKAGEINFO";
}
}
if (((!_reportDiff || _reportOnlyDirtyPackages) &&
warnings.equals("-")) ||
(_reportOnlyDirtyPackages && correctPackageInfo &&
(delta == Delta.REMOVED))) {
continue;
}
doInfo(bundleInfo, info, warnings);
if (_reportDiff && (delta != Delta.REMOVED)) {
doPackageDiff(packageDiff);
}
}
}
finally {
log(baselineProcessor);
baselineProcessor.close();
newJar.close();
if (oldJar != null) {
oldJar.close();
}
if (_printWriter != null) {
_printWriter.close();
}
}
return match;
}
public Properties getProperties() {
return _properties;
}
public void setBndFile(File bndFile) {
_bndFile = bndFile;
}
public void setForceCalculatedVersion(boolean forceCalculatedVersion) {
_forceCalculatedVersion = forceCalculatedVersion;
}
public void setForcePackageInfo(boolean forcePackageInfo) {
_forcePackageInfo = forcePackageInfo;
}
public void setForceVersionOneOnAddedPackages(
boolean forceVersionOneOnAddedPackages) {
_forceVersionOneOnAddedPackages = forceVersionOneOnAddedPackages;
}
public void setLogFile(File logFile) {
_logFile = logFile;
}
public void setNewCompatJarFile(File newCompatJarFile) {
_newCompatJarFile = newCompatJarFile;
}
public void setNewJarFile(File newJarFile) {
_newJarFile = newJarFile;
}
public void setOldJarFile(File oldJarFile) {
_oldJarFile = oldJarFile;
}
public void setReportDiff(boolean reportDiff) {
_reportDiff = reportDiff;
}
public void setReportOnlyDirtyPackages(boolean reportOnlyDirtyPackages) {
_reportOnlyDirtyPackages = reportOnlyDirtyPackages;
}
public void setSourceDir(File sourceDir) {
_sourceDir = sourceDir;
}
protected Version calculateVersion(Version version, Set<Info> infos)
throws IOException {
Delta highestDelta = Delta.UNCHANGED;
Set<String> movedPackages = getMovedPackages();
for (Info info : infos) {
Delta delta = info.packageDiff.getDelta();
if ((delta == Delta.ADDED) || (delta == Delta.CHANGED)) {
delta = Delta.MICRO;
}
else if (delta == Delta.REMOVED) {
if (movedPackages.contains(info.packageName)) {
delta = Delta.MICRO;
}
else {
delta = Delta.MAJOR;
}
}
if (delta.compareTo(highestDelta) > 0) {
highestDelta = delta;
}
}
if (highestDelta == Delta.MAJOR) {
version = new Version(version.getMajor() + 1, 0, 0);
}
else if (highestDelta == Delta.MINOR) {
version = new Version(
version.getMajor(), version.getMinor() + 1, 0);
}
else {
version = new Version(
version.getMajor(), version.getMinor(), version.getMicro() + 1);
}
return version;
}
protected void doDiff(Diff diff, StringBuilder sb) {
String type = String.valueOf(diff.getType());
String output = String.format(
"%s%-3s %-10s %s", sb, getShortDelta(diff.getDelta()),
type.toLowerCase(), diff.getName());
log(output);
if (_printWriter != null) {
_printWriter.println(output);
}
sb.append("\t");
for (Diff curDiff : diff.getChildren()) {
if (curDiff.getDelta() == Delta.UNCHANGED) {
continue;
}
doDiff(curDiff, sb);
}
sb.deleteCharAt(sb.length() - 1);
}
protected void doHeader(BundleInfo bundleInfo) throws IOException {
if (!bundleInfo.mismatch) {
return;
}
String output = "[Baseline Report] Mode: ";
if (_reportDiff) {
output += "diff";
}
else {
output += "standard";
}
if (_logFile != null) {
output += " (persisted)";
}
log(output);
output =
"[Baseline Warning] Bundle Version Change Recommended: " +
bundleInfo.suggestedVersion;
log(output);
persistLog(output);
}
protected void doInfo(BundleInfo bundleInfo, Info info, String warnings)
throws IOException {
doPackagesHeader(bundleInfo);
reportLog(
String.valueOf(info.mismatch ? '*' : ' '), info.packageName,
String.valueOf(info.packageDiff.getDelta()),
String.valueOf(info.newerVersion),
String.valueOf(info.olderVersion),
String.valueOf(
(info.suggestedVersion == null) ? "-" : info.suggestedVersion),
warnings, String.valueOf(info.attributes));
}
protected void doPackageDiff(Diff diff) {
StringBuilder sb = new StringBuilder();
sb.append("\t");
for (Diff curDiff : diff.getChildren()) {
if (curDiff.getDelta() == Delta.UNCHANGED) {
continue;
}
doDiff(curDiff, sb);
}
}
protected void doPackagesHeader(BundleInfo bundleInfo) throws IOException {
if (_headerPrinted) {
return;
}
_headerPrinted = true;
reportLog(
" ", "PACKAGE_NAME", "DELTA", "CUR_VER", "BASE_VER", "REC_VER",
"WARNINGS", "ATTRIBUTES");
reportLog(
"=", "==================================================",
"==========", "==========", "==========", "==========",
"==========", "==========");
}
protected boolean generatePackageInfo(Info info, Delta delta)
throws Exception {
boolean correct = true;
File packageDir = new File(
_sourceDir, info.packageName.replace('.', File.separatorChar));
if (!_forcePackageInfo && !packageDir.exists()) {
return correct;
}
File packageInfoFile = new File(packageDir, "packageinfo");
if (delta == Delta.REMOVED) {
if (packageInfoFile.exists()) {
correct = false;
packageInfoFile.delete();
}
}
else {
if (!packageInfoFile.exists()) {
correct = false;
}
packageDir.mkdirs();
FileOutputStream fileOutputStream = new FileOutputStream(
packageInfoFile);
String content = "version " + info.suggestedVersion;
fileOutputStream.write(content.getBytes());
fileOutputStream.close();
}
return correct;
}
protected Set<String> getMovedPackages() throws IOException {
File movedPackagesFile = new File(
_bndFile.getParentFile(), "moved-packages.txt");
if (!movedPackagesFile.exists()) {
return Collections.emptySet();
}
Set<String> movedPackages = new HashSet<>();
try (BufferedReader bufferedReader = new BufferedReader(
new FileReader(movedPackagesFile))) {
String line = null;
while ((line = bufferedReader.readLine()) != null) {
movedPackages.add(line);
}
}
return movedPackages;
}
protected String getShortDelta(Delta delta) {
if (delta == Delta.ADDED) {
return "+";
}
else if (delta == Delta.CHANGED) {
return "~";
}
else if (delta == Delta.MAJOR) {
return ">";
}
else if (delta == Delta.MICRO) {
return "0xB5";
}
else if (delta == Delta.MINOR) {
return "<";
}
else if (delta == Delta.REMOVED) {
return "-";
}
String deltaString = delta.toString();
return String.valueOf(deltaString.charAt(0));
}
protected boolean hasPackageRemoved(Iterable<Info> infos)
throws IOException {
Set<String> movedPackages = getMovedPackages();
for (Info info : infos) {
if ((info.packageDiff.getDelta() == Delta.REMOVED) &&
!movedPackages.contains(info.packageName)) {
return true;
}
}
return false;
}
protected abstract void log(Reporter reporter);
protected abstract void log(String output);
protected void persistLog(String output) throws IOException {
if (_logFile == null) {
return;
}
if (_printWriter == null) {
_logFile.createNewFile();
_printWriter = new PrintWriter(_logFile);
}
_printWriter.println(output);
}
protected void reportLog(
String string1, String string2, String string3, String string4,
String string5, String string6, String string7, String string8)
throws IOException {
String output = String.format(
"%s %-50s %-10s %-10s %-10s %-10s %-10s", string1, string2, string3,
string4, string5, string6, string7);
log(output);
persistLog(output);
}
protected void updateBundleVersion(Version oldVersion, Version newVersion)
throws IOException {
if (_bndFile == null) {
return;
}
String content = IO.collect(_bndFile);
content = content.replace(
Constants.BUNDLE_VERSION + ": " + oldVersion,
Constants.BUNDLE_VERSION + ": " + newVersion);
IO.store(content, _bndFile);
}
private File _bndFile;
private boolean _forceCalculatedVersion;
private boolean _forcePackageInfo;
private boolean _forceVersionOneOnAddedPackages = true;
private boolean _headerPrinted;
private File _logFile;
private File _newCompatJarFile;
private File _newJarFile;
private File _oldJarFile;
private PrintWriter _printWriter;
private final Properties _properties = new Properties();
private boolean _reportDiff;
private boolean _reportOnlyDirtyPackages;
private File _sourceDir;
}