Aggregation.java
/*
* YuiCompressor Maven plugin
*
* Copyright 2012-2025 Hazendaz.
*
* Licensed under the GNU Lesser General Public License (LGPL),
* version 2.1 or later (the "License").
* You may not use this file except in compliance with the License.
* You may read the licence in the 'lgpl.txt' file in the root folder of
* project or obtain a copy at
*
* https://www.gnu.org/licenses/lgpl-2.1.html
*
* 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 net.alchim31.maven.yuicompressor;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.codehaus.plexus.build.BuildContext;
import org.codehaus.plexus.util.DirectoryScanner;
import org.codehaus.plexus.util.IOUtil;
/**
* The Class Aggregation.
*/
public class Aggregation {
/** The input dir. */
private File inputDir;
/** The output. */
private File output;
/** The includes. */
private String[] includes;
/** The excludes. */
private String[] excludes;
/** The remove included. */
private boolean removeIncluded;
/** The insert new line. */
private boolean insertNewLine;
/** The insert file header. */
private boolean insertFileHeader;
/** The fix last semicolon. */
private boolean fixLastSemicolon;
/** The auto exclude wildcards. */
private boolean autoExcludeWildcards;
/**
* Gets the output.
*
* @return the output
*/
public File getOutput() {
return output;
}
/**
* Sets the output.
*
* @param output
* the new output
*/
public void setOutput(File output) {
this.output = output;
}
/**
* Sets the includes.
*
* @param includes
* the new includes
*/
public void setIncludes(String[] includes) {
this.includes = includes;
}
/**
* Sets the insert new line.
*
* @param insertNewLine
* the new insert new line
*/
public void setInsertNewLine(boolean insertNewLine) {
this.insertNewLine = insertNewLine;
}
/**
* Sets the auto exclude wildcards.
*
* @param autoExcludeWildcards
* the new auto exclude wildcards
*/
public void setAutoExcludeWildcards(boolean autoExcludeWildcards) {
this.autoExcludeWildcards = autoExcludeWildcards;
}
/**
* Run.
*
* @param previouslyIncludedFiles
* the previously included files
* @param buildContext
* the build context
*
* @return the list
*
* @throws IOException
* the IO exception
*/
public List<File> run(Collection<File> previouslyIncludedFiles, BuildContext buildContext) throws IOException {
return this.run(previouslyIncludedFiles, buildContext, null);
}
/**
* Run.
*
* @param previouslyIncludedFiles
* the previously included files
* @param buildContext
* the build context
* @param incrementalFiles
* the incremental files
*
* @return the list
*
* @throws IOException
* the IO exception
*/
public List<File> run(Collection<File> previouslyIncludedFiles, BuildContext buildContext,
Set<String> incrementalFiles) throws IOException {
defineInputDir();
List<File> files;
if (autoExcludeWildcards) {
files = getIncludedFiles(previouslyIncludedFiles, buildContext, incrementalFiles);
} else {
files = getIncludedFiles(null, buildContext, incrementalFiles);
}
if (!files.isEmpty()) {
output = output.getCanonicalFile();
output.getParentFile().mkdirs();
try (OutputStream out = buildContext.newFileOutputStream(output)) {
for (File file : files) {
if (file.getCanonicalPath().equals(output.getCanonicalPath())) {
continue;
}
try (InputStream in = Files.newInputStream(file.toPath())) {
if (insertFileHeader) {
out.write(createFileHeader(file).getBytes(StandardCharsets.UTF_8));
}
IOUtil.copy(in, out);
if (fixLastSemicolon) {
out.write(';');
}
if (insertNewLine) {
out.write('\n');
}
}
if (removeIncluded) {
if (file.exists()) {
Files.delete(file.toPath());
}
buildContext.refresh(file);
}
}
}
}
return files;
}
/**
* Creates the file header.
*
* @param file
* the file
*
* @return the string
*/
private String createFileHeader(File file) {
StringBuilder header = new StringBuilder();
header.append("/*");
header.append(file.getName());
header.append("*/");
if (insertNewLine) {
header.append('\n');
}
return header.toString();
}
/**
* Define input dir.
*
* @throws IOException
* the exception
*/
private void defineInputDir() throws IOException {
if (inputDir == null) {
inputDir = output.getParentFile();
}
inputDir = inputDir.getCanonicalFile();
if (!inputDir.isDirectory()) {
throw new IllegalStateException("input directory not found: " + inputDir);
}
}
/**
* Gets the included files.
*
* @param previouslyIncludedFiles
* the previously included files
* @param buildContext
* the build context
* @param incrementalFiles
* the incremental files
*
* @return the included files
*
* @throws IOException
* the IO exception
*/
private List<File> getIncludedFiles(Collection<File> previouslyIncludedFiles, BuildContext buildContext,
Set<String> incrementalFiles) throws IOException {
List<File> filesToAggregate = new ArrayList<>();
if (includes != null) {
for (String include : includes) {
addInto(include, filesToAggregate, previouslyIncludedFiles);
}
}
// If build is incremental with no delta, then don't include for aggregation
if (!buildContext.isIncremental()) {
return filesToAggregate;
}
if (incrementalFiles != null) {
boolean aggregateMustBeUpdated = false;
for (File file : filesToAggregate) {
if (incrementalFiles.contains(file.getCanonicalPath())) {
aggregateMustBeUpdated = true;
break;
}
}
if (aggregateMustBeUpdated) {
return filesToAggregate;
}
}
return new ArrayList<>();
}
/**
* Adds the into.
*
* @param include
* the include
* @param includedFiles
* the included files
* @param previouslyIncludedFiles
* the previously included files
*/
private void addInto(String include, List<File> includedFiles, Collection<File> previouslyIncludedFiles) {
if (include.indexOf('*') > -1) {
DirectoryScanner scanner = newScanner();
scanner.setIncludes(new String[] { include });
scanner.scan();
String[] rpaths = scanner.getIncludedFiles();
Arrays.sort(rpaths);
for (String rpath : rpaths) {
File file = scanner.getBasedir().toPath().resolve(rpath).toFile();
if (!includedFiles.contains(file)
&& (previouslyIncludedFiles == null || !previouslyIncludedFiles.contains(file))) {
includedFiles.add(file);
}
}
} else {
File file = Path.of(include).toFile();
if (!file.isAbsolute()) {
file = inputDir.toPath().resolve(include).toFile();
}
if (!includedFiles.contains(file)) {
includedFiles.add(file);
}
}
}
/**
* New scanner.
*
* @return the directory scanner
*/
private DirectoryScanner newScanner() {
DirectoryScanner scanner = new DirectoryScanner();
scanner.setBasedir(inputDir);
if (excludes != null && excludes.length != 0) {
scanner.setExcludes(excludes);
}
scanner.addDefaultExcludes();
return scanner;
}
}