com.mycila.maven.plugin.license.header.HeaderParser Maven / Gradle / Ivy
Show all versions of license-maven-plugin Show documentation
/*
* Copyright (C) 2008-2024 Mycila ([email protected])
*
* 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
*
* https://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.mycila.maven.plugin.license.header;
import com.mycila.maven.plugin.license.util.FileContent;
import com.mycila.maven.plugin.license.util.StringUtils;
/**
* The HeaderParser
class is used to get header information about the current header defined in the given
* file. The achieve this it will use the HeaderDefinition
associated to the type of the given file.
*
* Important: is considered a license header a header which contains the word copyright (case insensitive)
* within a section of the file which match the given HeaderDefinition
associated to this
* HeaderParser
.
*
* @see com.mycila.maven.plugin.license.header.HeaderDefinition
*/
public final class HeaderParser {
private final int beginPosition;
private final int endPosition;
private final boolean existingHeader;
private final FileContent fileContent;
private final String[] keywords;
private HeaderDefinition headerDefinition;
private String line;
/**
* Creates a HeaderParser
object linked to the given file content and the associated header definition
* based on the file type.
*
* @param fileContent The file content.
* @param headerDefinition The associated header definition to use.
* @param keywords The keywords used for finding header.
* @throws IllegalArgumentException If the file content is null or if the header definition is null.
*/
public HeaderParser(FileContent fileContent, HeaderDefinition headerDefinition, String[] keywords) {
if (fileContent == null) {
throw new IllegalArgumentException("Cannot create a header parser for null file content");
}
if (headerDefinition == null) {
throw new IllegalArgumentException("Cannot work on file header if the header definition is null");
}
this.keywords = keywords.clone();
this.headerDefinition = headerDefinition;
this.fileContent = fileContent;
beginPosition = findBeginPosition();
existingHeader = hasHeader();
endPosition = existingHeader ? findEndPosition() : -1;
}
/**
* Returns the index position in the content where the header effectively starts.
*
* @return The index in the content.
*/
public int getBeginPosition() {
return beginPosition;
}
/**
* Returns the index position in the content where the header effectively ends.
*
* @return The index in the content.
*/
public int getEndPosition() {
return endPosition;
}
/**
* Tells if the given file already contains a license header.
*
* @return true if a license header has been detect or false.
*/
public boolean gotAnyHeader() {
return existingHeader;
}
/**
* Returns the file content.
*
* @return The content.
*/
public FileContent getFileContent() {
return fileContent;
}
/**
* Returns the header definition associated to this header parser (itself bounded to a file).
*
* @return The associated header definition.
*/
public HeaderDefinition getHeaderDefinition() {
return headerDefinition;
}
private int findBeginPosition() {
int beginPos = 0;
line = fileContent.nextLine();
if (headerDefinition.getSkipLinePattern() == null) {
return beginPos;
}
// the format expect to find lines to be skipped
while (line != null && !headerDefinition.isSkipLine(line)) {
beginPos = fileContent.getPosition();
line = fileContent.nextLine();
}
// at least we have found the line to skip or we are the end of the file
// this time we are going to skip next lines if they match the skip pattern
while (line != null && headerDefinition.isSkipLine(line)) {
beginPos = fileContent.getPosition();
line = fileContent.nextLine();
}
if (line == null) {
// After skipping everything we are at the end of the file
// Header has to be at the file beginning
beginPos = 0;
fileContent.reset();
line = fileContent.nextLine();
}
return beginPos;
}
private boolean hasHeader() {
// skip blank lines
while (line != null && "".equals(line.trim())) {
line = fileContent.nextLine();
}
// check if there is already a header
boolean gotHeader = false;
if (headerDefinition.isFirstHeaderLine(line)) {
StringBuilder inPlaceHeader = new StringBuilder();
inPlaceHeader.append(line.toLowerCase());
line = fileContent.nextLine();
// skip blank lines before header text
if (headerDefinition.allowBlankLines()) {
while (line != null && "".equals(line.trim())) {
line = fileContent.nextLine();
}
}
// first header detected line & potential blank lines have been detected
// following lines should be header lines
if (line == null) {
// we detected previously a one line comment block that matches the header detection
// it is not an header it is a comment
return false;
} else {
inPlaceHeader.append(line.toLowerCase());
}
String before = StringUtils.rtrim(headerDefinition.getBeforeEachLine());
if ("".equals(before) && !headerDefinition.isMultiLine()) {
before = headerDefinition.getBeforeEachLine();
}
boolean foundEnd = false;
if (headerDefinition.isMultiLine() && headerDefinition.isLastHeaderLine(line)) {
foundEnd = true;
} else {
while ((line = fileContent.nextLine()) != null && line.startsWith(before)) {
inPlaceHeader.append(line.toLowerCase());
if (headerDefinition.isMultiLine() && headerDefinition.isLastHeaderLine(line)) {
foundEnd = true;
break;
}
}
}
// skip blank lines after header text
if (headerDefinition.isMultiLine() && headerDefinition.allowBlankLines() && !foundEnd) {
do {
line = fileContent.nextLine();
} while (line != null && "".equals(line.trim()));
fileContent.rewind();
} else if (!headerDefinition.isMultiLine() && !foundEnd) {
fileContent.rewind();
}
if (!headerDefinition.isMultiLine()) {
// keep track of the position for headers where the end line is the same as the before each line
int pos = fileContent.getPosition();
// check if the line is the end line
while (line != null
&& !headerDefinition.isLastHeaderLine(line)
&& (headerDefinition.allowBlankLines() || !"".equals(line.trim()))
&& line.startsWith(before)) {
line = fileContent.nextLine();
}
if (line == null) {
fileContent.resetTo(pos);
}
} else if (line != null) {
// we could end up there if we still have some lines, but not matching "before".
// This can be the last line in a multi line header
int pos = fileContent.getPosition();
line = fileContent.nextLine();
if (line == null || !headerDefinition.isLastHeaderLine(line)) {
fileContent.resetTo(pos);
}
}
gotHeader = true;
for (String keyword : keywords) {
if (inPlaceHeader.indexOf(keyword.toLowerCase()) == -1) {
gotHeader = false;
break;
}
}
}
return gotHeader;
}
private int findEndPosition() {
// we check if there is a header, if the next line is the blank line of the header
int end = fileContent.getPosition();
line = fileContent.nextLine();
if (beginPosition == 0) {
while (line != null && "".equals(line.trim())) {
end = fileContent.getPosition();
line = fileContent.nextLine();
}
}
if (headerDefinition.getEndLine().endsWith("EOL") && line != null && "".equals(line.trim())) {
end = fileContent.getPosition();
}
return end;
}
}