com.github.javaparser.printer.lexicalpreservation.RemovedGroup Maven / Gradle / Ivy
/*
* Copyright (C) 2007-2010 Júlio Vilmar Gesser.
* Copyright (C) 2011, 2013-2021 The JavaParser Team.
*
* This file is part of JavaParser.
*
* JavaParser can be used either under the terms of
* a) the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* b) the terms of the Apache License
*
* You should have received a copy of both licenses in LICENCE.LGPL and
* LICENCE.APACHE. Please refer to those files for details.
*
* JavaParser 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.github.javaparser.printer.lexicalpreservation;
import com.github.javaparser.JavaToken;
import com.github.javaparser.TokenRange;
import com.github.javaparser.TokenTypes;
import com.github.javaparser.ast.Node;
import com.github.javaparser.printer.concretesyntaxmodel.CsmToken;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* This class represents a group of {@link Removed} elements.
* The {@link Removed} elements are ideally consecutive for the methods in this class to work correctly.
*
* This class consists of methods that calculate information to better handle the difference application for the
* containing {@link Removed} elements.
*
* @see Iterable
*
* @author ThLeu
*/
final class RemovedGroup implements Iterable {
private final Integer firstElementIndex;
private final List removedList;
private boolean isProcessed = false;
private RemovedGroup(Integer firstElementIndex, List removedList) {
if (firstElementIndex == null) {
throw new IllegalArgumentException("firstElementIndex should not be null");
}
if (removedList == null || removedList.isEmpty()) {
throw new IllegalArgumentException("removedList should not be null or empty");
}
this.firstElementIndex = firstElementIndex;
this.removedList = removedList;
}
/**
* Factory method to create a RemovedGroup which consists of consecutive Removed elements
*
* @param firstElementIndex the difference index at which the RemovedGroup starts
* @param removedList list of the consecutive Removed elements
* @return a RemovedGroup object
* @throws IllegalArgumentException if the firstElementIndex is null or the removedList is empty or null
*/
public static RemovedGroup of(Integer firstElementIndex, List removedList) {
return new RemovedGroup(firstElementIndex, removedList);
}
/**
* Marks the RemovedGroup as processed which indicates that it should not be processed again
*/
final void processed() {
isProcessed = true;
}
/**
* Returns whether the RemovedGroup was already processed and should not be processed again
*
* @return wheter the RemovedGroup was already processed
*/
final boolean isProcessed() {
return isProcessed;
}
private List getIndicesBeingRemoved() {
return IntStream.range(firstElementIndex, firstElementIndex + removedList.size())
.boxed()
.collect(Collectors.toList());
}
/**
* Returns the difference index of the last element being removed with this RemovedGroup
*
* @return the last difference incex of this RemovedGroup
*/
final Integer getLastElementIndex() {
List indicesBeingRemoved = getIndicesBeingRemoved();
return indicesBeingRemoved.get(indicesBeingRemoved.size() - 1);
}
/**
* Returns the first element of this RemovedGroup
*
* @return the first element of this RemovedGroup
*/
final Removed getFirstElement() {
return removedList.get(0);
}
/**
* Returns the last element of this RemovedGroup
*
* @return the last element of this RemovedGroup
*/
final Removed getLastElement() {
return removedList.get(removedList.size() - 1);
}
/**
* Returns true if the RemovedGroup equates to a complete line
* This is the case if there are only spaces and tabs left on the line besides the Removed elements.
*
* Example:
*
* " [Removed] [EOL]" -> this would be a complete line, regardless of spaces or tabs before or after the [Removed] element
* " [Removed] void [EOL]" -> this would not be a complete line because of the "void"
* " public [Removed] [EOL]" -> this would not be a complete line because of the "public"
*
*
* @return true if the RemovedGroup equates to a complete line
*/
final boolean isACompleteLine() {
return hasOnlyWhitespace(getFirstElement(), hasOnlyWhitespaceInFrontFunction)
&& hasOnlyWhitespace(getLastElement(), hasOnlyWhitespaceBehindFunction);
}
private final Function hasOnlyWhitespaceJavaTokenInFrontFunction = begin -> hasOnlyWhiteSpaceForTokenFunction(begin, token -> token.getPreviousToken());
private final Function hasOnlyWhitespaceJavaTokenBehindFunction = end -> hasOnlyWhiteSpaceForTokenFunction(end, token -> token.getNextToken());
private final Function hasOnlyWhitespaceInFrontFunction = tokenRange -> hasOnlyWhitespaceJavaTokenInFrontFunction.apply(tokenRange.getBegin());
private final Function hasOnlyWhitespaceBehindFunction = tokenRange -> hasOnlyWhitespaceJavaTokenBehindFunction.apply(tokenRange.getEnd());
private boolean hasOnlyWhitespace(Removed startElement, Function hasOnlyWhitespaceFunction) {
boolean hasOnlyWhitespace = false;
if (startElement.isChild()) {
LexicalDifferenceCalculator.CsmChild csmChild = (LexicalDifferenceCalculator.CsmChild) startElement.getElement();
Node child = csmChild.getChild();
Optional tokenRange = child.getTokenRange();
if (tokenRange.isPresent()) {
hasOnlyWhitespace = hasOnlyWhitespaceFunction.apply(tokenRange.get());
}
} else if (startElement.isToken()) {
CsmToken token = (CsmToken) startElement.getElement();
if (TokenTypes.isEndOfLineToken(token.getTokenType())) {
hasOnlyWhitespace = true;
}
}
return hasOnlyWhitespace;
}
private boolean hasOnlyWhiteSpaceForTokenFunction(JavaToken token, Function> tokenFunction) {
Optional tokenResult = tokenFunction.apply(token);
if (tokenResult.isPresent()) {
if (TokenTypes.isWhitespaceButNotEndOfLine(tokenResult.get().getKind())) {
return hasOnlyWhiteSpaceForTokenFunction(tokenResult.get(), tokenFunction);
} else if (TokenTypes.isEndOfLineToken(tokenResult.get().getKind())) {
return true;
} else {
return false;
}
}
return true;
}
/**
* Returns the indentation in front of this RemovedGroup if possible.
* If there is something else than whitespace in front, Optional.empty() is returned.
*
* @return the indentation in front of this RemovedGroup or Optional.empty()
*/
final Optional getIndentation() {
Removed firstElement = getFirstElement();
int indentation = 0;
if (firstElement.isChild()) {
LexicalDifferenceCalculator.CsmChild csmChild = (LexicalDifferenceCalculator.CsmChild) firstElement.getElement();
Node child = csmChild.getChild();
Optional tokenRange = child.getTokenRange();
if (tokenRange.isPresent()) {
JavaToken begin = tokenRange.get().getBegin();
if (hasOnlyWhitespaceJavaTokenInFrontFunction.apply(begin)) {
Optional previousToken = begin.getPreviousToken();
while(previousToken.isPresent() && (TokenTypes.isWhitespaceButNotEndOfLine(previousToken.get().getKind()))) {
indentation++;
previousToken = previousToken.get().getPreviousToken();
}
if (previousToken.isPresent()) {
if (TokenTypes.isEndOfLineToken(previousToken.get().getKind())) {
return Optional.of(Integer.valueOf(indentation));
} else {
return Optional.empty();
}
} else {
return Optional.of(Integer.valueOf(indentation));
}
}
}
}
return Optional.empty();
}
@Override
public final Iterator iterator() {
return new Iterator() {
private int currentIndex = 0;
@Override
public boolean hasNext() {
return currentIndex < removedList.size() && removedList.get(currentIndex) != null;
}
@Override
public Removed next() {
return removedList.get(currentIndex++);
}
};
}
}