All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
spoon.pattern.internal.matcher.TobeMatched Maven / Gradle / Ivy
Go to download
Spoon is a tool for meta-programming, analysis and transformation of Java programs.
/*
* SPDX-License-Identifier: (MIT OR CECILL-C)
*
* Copyright (C) 2006-2023 INRIA and contributors
*
* Spoon is available either under the terms of the MIT License (see LICENSE-MIT.txt) or the Cecill-C License (see LICENSE-CECILL-C.txt). You as the user are entitled to choose the terms under which to adopt Spoon.
*/
package spoon.pattern.internal.matcher;
import static java.util.stream.Collectors.toUnmodifiableList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import org.jspecify.annotations.Nullable;
import spoon.SpoonException;
import spoon.reflect.meta.ContainerKind;
import spoon.support.util.ImmutableMap;
/**
* Describes what next has to be matched.
* It consists of current `parameters` represented by {@link ImmutableMap}
* and by a to be matched target elements.
* See children of {@link TobeMatched} for supported collections of target elements.
*/
public class TobeMatched {
//TODO remove parameters. Send them individually into matching methods and return MatchResult
private final ImmutableMap parameters;
//Use list for everything because Spoon uses Sets with predictable iteration order
private final List> targets;
private final boolean ordered;
/**
* @param parameters to be matched parameters
* @param containerKind the type of container in `target` value
* @param target the to be matched target data. List, Set, Map or single value
* @return new instance of {@link TobeMatched}, which contains `parameters` and `target` mapped using containerKind
*/
public static TobeMatched create(ImmutableMap parameters, ContainerKind containerKind, Object target) {
switch (containerKind) {
case LIST:
return new TobeMatched(parameters, (List) target, true);
case SET:
return new TobeMatched(parameters, (Set) target, false);
case MAP:
return new TobeMatched(parameters, (Map) target);
case SINGLE:
return new TobeMatched(parameters, target);
}
throw new SpoonException("Unexpected RoleHandler containerKind: " + containerKind);
}
private TobeMatched(ImmutableMap parameters, Object target) {
//It is correct to put whole container as single value in cases when ParameterNode matches agains whole attribute value
this.parameters = parameters;
//make a copy of origin collection, because it might be modified during matching process (by a refactoring algorithm)
this.targets = Collections.singletonList(target);
this.ordered = true;
}
/**
* @param parameters current parameters
* @param targets List or Set of to be matched targets
* @param ordered defines the way how targets are matched. If true then first target is matched with first ValueResolver.
* If false then all targets are tried with first ValueResolver.
*/
private TobeMatched(ImmutableMap parameters, Collection> targets, boolean ordered) {
this.parameters = parameters;
//make a copy of origin collection, because it might be modified during matching process (by a refactoring algorithm)
this.targets = targets == null ? Collections.emptyList() : copyToListWithoutNullValues(targets);
this.ordered = ordered;
}
private TobeMatched(ImmutableMap parameters, Map targets) {
this.parameters = parameters;
//make a copy of origin collection, because it might be modified during matching process (by a refactoring algorithm)
this.targets = targets == null ? Collections.emptyList() : copyToListWithoutNullValues(targets.entrySet());
this.ordered = false;
}
private TobeMatched(ImmutableMap parameters, List> targets, boolean ordered, int tobeRemovedIndex) {
this.parameters = parameters;
this.targets = new ArrayList<>(targets);
if (tobeRemovedIndex >= 0) {
this.targets.remove(tobeRemovedIndex);
}
this.ordered = ordered;
}
/**
* Converts the given collection of objects into a unmodifiable list without null values.
* @param the type of the objects in the collection.
* @param collection the collection of objects to be converted.
* @return the converted unmodifiable list without null values.
*/
private List copyToListWithoutNullValues(Collection collection) {
return collection.stream().filter(Objects::nonNull).collect(toUnmodifiableList());
}
/**
* @return parameters of last successful match.
*/
public ImmutableMap getParameters() {
return parameters;
}
/**
* @return {@link List} of to be matched targets, which
* A) have to be matched by current matching step
* B) remained after matching of all template nodes
*/
public List> getTargets() {
return targets;
}
/**
* @param tobeMatchedTargets {@link TobeMatched} with targets, which didn't matched yet. These which has to be matched next.
* @return matched targets. It means these targets, which are not contained in `notMatchedTargets`
*/
public List> getMatchedTargets(TobeMatched tobeMatchedTargets) {
int nrOfMatches = getTargets().size() - tobeMatchedTargets.getTargets().size();
if (nrOfMatches >= 0) {
if (nrOfMatches == 0) {
return Collections.emptyList();
}
List matched = new ArrayList<>(nrOfMatches);
for (Object target : getTargets()) {
if (containsSame(tobeMatchedTargets.getTargets(), target)) {
//this origin target is still available in this to be matched targets
continue;
}
//this origin target is NOT available in this to be matched targets. It means it matched
matched.add(target);
}
if (matched.size() == nrOfMatches) {
return matched;
}
}
throw new SpoonException("Invalid input `originTobeMatched`");
}
private boolean containsSame(List> items, Object object) {
for (Object item : items) {
if (item == object) {
return true;
}
}
return false;
}
/**
* @return true if there is anything to match.
*/
public boolean hasTargets() {
return !targets.isEmpty();
}
/**
* Makes a copy of this match context with the same targets, but with new `parameters`
* @param newParams to be used parameters
* @return copy of {@link TobeMatched} with new parameters
*/
public TobeMatched copyAndSetParams(ImmutableMap newParams) {
if (parameters == newParams) {
return this;
}
return new TobeMatched(newParams, targets, ordered, -1);
}
/**
* Calls matcher algorithm to match target item
* @param matcher a matching algorithm
* @return {@link TobeMatched} with List of remaining (to be matched) targets or null if there is no match
*/
public @Nullable TobeMatched matchNext(BiFunction matcher) {
if (targets.isEmpty()) {
//no target -> no match
return null;
}
if (ordered) {
//handle ordered list of targets - match with first target
ImmutableMap parameters = matcher.apply(targets.get(0), getParameters());
if (parameters != null) {
//return remaining match
return removeTarget(parameters, 0);
}
return null;
} else {
//handle un-ordered list of targets - match with all targets, stop at first matching
int idxOfMatch = 0;
while (idxOfMatch < targets.size()) {
ImmutableMap parameters = matcher.apply(targets.get(idxOfMatch), getParameters());
if (parameters != null) {
return removeTarget(parameters, idxOfMatch);
}
//try to match next target
idxOfMatch++;
}
return null;
}
}
/**
* @param remainingMatch the {@link TobeMatched} whose parameters has to be returned
* @return parameters from `remainingMatch`, if it exists. Else returns null
*/
public static @Nullable ImmutableMap getMatchedParameters(TobeMatched remainingMatch) {
return remainingMatch == null ? null : remainingMatch.getParameters();
}
/**
* @param idxOfTobeRemovedTarget index of to be removed target
* @return new {@link TobeMatched} without the target on the index `idxOfTobeRemovedTarget`
*/
public TobeMatched removeTarget(int idxOfTobeRemovedTarget) {
return removeTarget(parameters, idxOfTobeRemovedTarget);
}
public TobeMatched removeTarget(ImmutableMap parameters, int idxOfTobeRemovedTarget) {
return new TobeMatched(parameters, targets, ordered, idxOfTobeRemovedTarget);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Parameters:\n----------------\n")
.append(parameters)
.append("\nTobe matched target elements\n-----------------------\n");
for (int i = 0; i < targets.size(); i++) {
sb.append('\n').append(i + 1).append('/').append(targets.size()).append(": ").append(targets.get(i));
}
return sb.toString();
}
}