eu.cqse.check.framework.shallowparser.framework.ShallowEntityTraversalUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of teamscale-commons Show documentation
Show all versions of teamscale-commons Show documentation
Provides common DTOs for Teamscale
/*
* Copyright (c) CQSE GmbH
*
* 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
*
* http://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 eu.cqse.check.framework.shallowparser.framework;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import eu.cqse.check.framework.shallowparser.SubTypeNames;
/** Utility methods for working with shallow entities. */
public class ShallowEntityTraversalUtils {
/** Lists all entities. */
public static List listAllEntities(Collection entities) {
return listEntitiesOfTypes(entities, EnumSet.allOf(EShallowEntityType.class));
}
/**
* Lists all entities of the given type, includes matching child entities
* recursively.
*/
public static List listEntitiesOfType(Collection entities, EShallowEntityType type) {
return listEntitiesOfTypes(entities, EnumSet.of(type));
}
/**
* Lists all entities of the given types, includes matching child entities
* recursively.
*/
public static List listEntitiesOfTypes(Collection entities,
Set types) {
return new CollectingVisitorBase() {
@Override
protected boolean collect(ShallowEntity entity) {
return types.contains(entity.getType());
}
}.apply(entities);
}
/** Lists all entities that are selected by the given predicate. */
public static List selectEntities(Collection entities,
Predicate predicate) {
return new CollectingVisitorBase() {
@Override
protected boolean collect(ShallowEntity entity) {
return predicate.test(entity);
}
}.apply(entities);
}
/**
* Returns the first incomplete entity found (or null). Unclosed entities
* correspond to parsing errors.
*/
private static ShallowEntity findIncompleteEntity(ShallowEntity entity) {
if (!entity.isCompleted()) {
return entity;
}
return findIncompleteEntity(entity.getChildren());
}
/**
* Returns the first incomplete entity found (or null). Unclosed entities
* correspond to parsing errors.
*/
public static ShallowEntity findIncompleteEntity(List entities) {
for (ShallowEntity entity : entities) {
ShallowEntity incomplete = findIncompleteEntity(entity);
if (incomplete != null) {
return incomplete;
}
}
return null;
}
/**
* Traverses the given collection of entities and returns a flat list of
* entities.
*/
public static List getAllEntities(Collection entities) {
return new CollectingVisitorBase() {
@Override
protected boolean collect(ShallowEntity entity) {
return true;
}
}.apply(entities);
}
/**
* Lists all entities of the given type, but does not traverse into the
* entities, i.e. entities contained in the returned ones are not returned.
*/
public static List listEntitiesOfTypeNonRecursive(Collection entities,
EShallowEntityType type) {
List methods = new ArrayList<>();
ShallowEntity.traverse(entities, new ShallowEntityVisitorBase() {
/** {@inheritDoc} */
@Override
public boolean visit(ShallowEntity entity) {
if (entity.getType() == type) {
methods.add(entity);
// do not recurse into methods
return false;
}
return true;
}
});
return methods;
}
/**
* Returns all methods found in the given entities, but does not traverse into
* the entities, i.e. methods contained in other methods are not returned.
*/
public static List listMethodsNonRecursive(List entities) {
return listEntitiesOfTypeNonRecursive(entities, EShallowEntityType.METHOD);
}
/**
* Recursively traverses the given shallow entities and returns all entities
* that match the given predicate.
*/
public static List listMatchingEntitiesRecursive(List entities,
Predicate matchPredicate) {
return new CollectingVisitorBase() {
@Override
protected boolean collect(ShallowEntity entity) {
return matchPredicate.test(entity);
}
}.apply(entities);
}
/** Empty default implementation of {@link IShallowEntityVisitor}. */
public static abstract class ShallowEntityVisitorBase implements IShallowEntityVisitor {
/** {@inheritDoc} */
@Override
public boolean visit(ShallowEntity entity) {
return true;
}
/** {@inheritDoc} */
@Override
public void endVisit(ShallowEntity entity) {
// nothing
}
}
/**
* Base class for visitors that collect shallow entities, includes matching
* child entities recursively.
*/
public static abstract class CollectingVisitorBase extends ShallowEntityVisitorBase {
/** The collected entities. */
private final List entities = new ArrayList<>();
/** {@inheritDoc} */
@Override
public boolean visit(ShallowEntity entity) {
if (collect(entity)) {
entities.add(entity);
}
return true;
}
/** Template method that returns true if the entity should be collected. */
protected abstract boolean collect(ShallowEntity entity);
/**
* Applies this collecting visitor depth-first to the given entities and returns
* the collected result.
*/
public List apply(Collection entities) {
ShallowEntity.traverse(entities, this);
return this.entities;
}
}
/** Utility method for traversing multiple entities. */
public static void traverseWithKey(List entities, IShallowEntityKeyVisitor visitor) {
traverseWithKey(entities, visitor, new int[0]);
}
/**
* Utility method for traversing multiple entities.
*
* @param parentKey
* Specifies the unique key of the entity lists parent.
*/
private static void traverseWithKey(List entities, IShallowEntityKeyVisitor visitor,
int[] parentKey) {
int length = parentKey.length;
for (int i = 0; i < entities.size(); i++) {
ShallowEntity entity = entities.get(i);
int[] key = Arrays.copyOf(parentKey, length + 1);
key[length] = i;
if (visitor.visit(key, entity)) {
traverseWithKey(entity.getChildren(), visitor, key);
}
}
}
/** Empty default implementation of {@link IShallowEntityVisitor}. */
public interface IShallowEntityKeyVisitor {
/**
* Denotes that visiting the entity begins.
*
* @param key
* Key, which uniquely identifies the entity for the traversed list
* of {@link ShallowEntity}s.
* @return true if the children of this entity are to be visited as well.
*/
boolean visit(int[] key, ShallowEntity entity);
}
/**
* Returns a {@link ShallowEntity} from the given list of entities, which is
* identified by the given key.
*
* @see ShallowEntityTraversalUtils#traverseWithKey(List,
* IShallowEntityKeyVisitor)
*/
public static ShallowEntity getEntityFromListWithKey(List entities, int[] key) {
if (key.length == 0 || key[0] >= entities.size()) {
return null;
}
ShallowEntity entity = entities.get(key[0]);
if (key.length == 1) {
return entity;
}
return getEntityFromListWithKey(entity.getChildren(), Arrays.copyOfRange(key, 1, key.length));
}
/**
* Returns the next {@link ShallowEntity} after the given entity. Returns
* null
if there is no subsequent entity.
*/
public static ShallowEntity getSubsequentEntity(ShallowEntity entity) {
boolean found = false;
while (entity != null && entity.getParent() != null) {
List neighboringEntities = entity.getParent().getChildren();
for (ShallowEntity neighboringEntity : neighboringEntities) {
if (found) {
return neighboringEntity;
} else if (neighboringEntity.equals(entity)) {
found = true;
}
}
entity = entity.getParent();
found = false;
}
return null;
}
/**
* Returns the previous {@link ShallowEntity} before the given entity. Returns
* null
if there is no previous entity.
*/
public static ShallowEntity getPreviousEntity(ShallowEntity entity) {
ShallowEntity previousEntity = null;
while (entity != null && entity.getParent() != null) {
List neighboringEntities = entity.getParent().getChildren();
for (ShallowEntity neighboringEntity : neighboringEntities) {
if (neighboringEntity.equals(entity)) {
return previousEntity;
}
previousEntity = neighboringEntity;
}
entity = entity.getParent();
}
return null;
}
/**
* Returns the outermost entity containing the given (one-based) line number.
*/
public static Optional findEntityForLineNonRec(int line, List entities) {
return findEntityForLine(line, false, entities, false);
}
/**
* Returns the innermost entity containing the given (one-based) line number.
*/
public static Optional findEntityForLine(int line, List entities) {
return findEntityForLine(line, true, entities, false);
}
/**
* Returns the innermost entity containing the given (one-based) line number.
*
* @param returnSuccessor
* Returns the next entity after the given line if the exact line has
* no matching entity
*/
public static Optional findEntityForLine(int line, List entities,
boolean returnSuccessor) {
return findEntityForLine(line, true, entities, returnSuccessor);
}
/**
* Returns the entity containing the given (one-based) line number.
*
* @param returnSuccessor
* Returns the next entity after the given line if the exact line has
* no matching entity
*/
private static Optional findEntityForLine(int line, boolean recursive, List entities,
boolean returnSuccessor) {
// we know that entities are sorted according to line number, so we can
// binary search them
@SuppressWarnings("serial")
ShallowEntity comparisonEntity = new ShallowEntity(null, null, null, null, 0) {
@Override
public int getStartLine() {
return line;
}
};
int index = Collections.binarySearch(entities, comparisonEntity,
Comparator.comparingInt(ShallowEntity::getStartLine));
if (index < 0) {
// we want insertion point +1 in this case
index = -index - 2;
}
if (index >= 0 && index < entities.size() && entities.get(index).getStartLine() <= line
&& line <= entities.get(index).getEndLine()) {
ShallowEntity match = entities.get(index);
if (recursive && match.hasChildren()) {
Optional betterMatch = findEntityForLine(line, match.getChildren(), returnSuccessor);
if (betterMatch.isPresent()) {
return betterMatch;
}
}
return Optional.of(match);
}
if (returnSuccessor && index + 1 < entities.size()) {
ShallowEntity match = entities.get(index + 1);
return Optional.of(match);
}
return Optional.empty();
}
/**
* Traverses the parent hierarchy of the given startEntity upwards. Returns the
* first parent that matches the given predicate or Optional.empty if no entity
* matches the predicate.
*/
public static Optional findParentEntity(ShallowEntity startEntity,
Predicate shallowEntityPredicate) {
ShallowEntity current = startEntity;
while (current.getParent() != null) {
current = current.getParent();
if (shallowEntityPredicate.test(current)) {
return Optional.of(current);
}
}
return Optional.empty();
}
/**
* Traverses the parent hierarchy of the given startEntity upwards. Returns the
* entity itself or the first parent that matches the given predicate or
* Optional.empty if no entity matches the predicate.
*/
public static Optional findEntityOrParentEntity(ShallowEntity startEntity,
Predicate shallowEntityPredicate) {
ShallowEntity current = startEntity;
while (current != null && !shallowEntityPredicate.test(current)) {
current = current.getParent();
}
return Optional.ofNullable(current);
}
/**
* Traverses the parent hierarchy of the given startEntity upwards. Returns all
* the shallow entities that match the given predicate, or an empty list if no
* entity matches the predicate.
*/
public static List findMatchingParentEntities(ShallowEntity startEntity,
Predicate shallowEntityPredicate) {
List parentEntities = new ArrayList<>();
ShallowEntity current = startEntity;
while (current.getParent() != null) {
current = current.getParent();
if (shallowEntityPredicate.test(current)) {
parentEntities.add(current);
}
}
return parentEntities;
}
/**
* Traverses the parent hierarchy of the given startEntity upwards. Returns the
* first parent that matches the given subtype (see {@link SubTypeNames}) or
* Optional.empty if no entity matches the subtype.
*/
public static Optional findParentEntityWithSubType(ShallowEntity startEntity, String subtype) {
return findParentEntity(startEntity, entity -> entity.getSubtype().equals(subtype));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy