All Downloads are FREE. Search and download functionalities are using the official Maven repository.

refdiff.core.diff.CstRootHelper Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
package refdiff.core.diff;

import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import refdiff.core.diff.similarity.SourceRepresentationBuilder;
import refdiff.core.io.SourceFile;
import refdiff.core.io.SourceFileSet;
import refdiff.core.cst.HasChildrenNodes;
import refdiff.core.cst.Location;
import refdiff.core.cst.Parameter;
import refdiff.core.cst.CstNode;
import refdiff.core.cst.CstNodeRelationship;
import refdiff.core.cst.CstNodeRelationshipType;
import refdiff.core.cst.CstRoot;
import refdiff.core.cst.TokenizedSource;

public class CstRootHelper {
	
	private final CstRoot cstRoot;
	private final Map idMap = new HashMap<>();
	private final Map> edges = new HashMap<>();
	private final Map> reverseEdges = new HashMap<>();
	private final Map depthMap = new HashMap<>();
	private final Map fileMap = new HashMap<>();
	private final SourceRepresentationBuilder srb;
	private final Map srMap = new HashMap<>();
	private final Map srBodyMap = new HashMap<>();
	private final Map srNameMap = new HashMap<>();
	private final Map> nameIndex = new HashMap<>();
	private final boolean isBefore;
	
	public CstRootHelper(CstRoot cstRoot, SourceFileSet sources, SourceRepresentationBuilder srb, boolean isBefore) throws IOException {
		this.cstRoot = cstRoot;
		this.srb = srb;
		this.isBefore = isBefore;
		
		cstRoot.forEachNode((node, depth) -> {
			idMap.put(node.getId(), node);
			depthMap.put(node, depth);
			nameIndex.computeIfAbsent(node.getLocalName(), k -> new ArrayList<>()).add(node);
		});
		
		for (CstNodeRelationship relationship : cstRoot.getRelationships()) {
			edges.compute(relationship.getN1(), (key, oldValue) -> {
				if (oldValue == null) {
					ArrayList list = new ArrayList<>();
					list.add(relationship);
					return list;
				} else {
					oldValue.add(relationship);
					return oldValue;
				}
			});
			reverseEdges.compute(relationship.getN2(), (key, oldValue) -> {
				if (oldValue == null) {
					ArrayList list = new ArrayList<>();
					list.add(relationship);
					return list;
				} else {
					oldValue.add(relationship);
					return oldValue;
				}
			});
		}
		
		for (SourceFile file : sources.getSourceFiles()) {
			fileMap.put(file.getPath(), sources.readContent(file));
		}
	}
	
	public int depth(CstNode node) {
		return depthMap.get(node);
	}
	
	public List findByLocalName(String localName) {
		return nameIndex.getOrDefault(localName, Collections.emptyList());
	}
	
	public Collection findRelationships(CstNodeRelationshipType type, CstNode node) {
		return this.findRelationships(edges, type, node, rel -> idMap.get(rel.getN2()));
	}
	
	public boolean hasRelationship(CstNodeRelationshipType type, Optional optN1, CstNode n2) {
		return hasRelationship(type, optN1, Optional.of(n2));
	}
	
	public boolean hasRelationship(CstNodeRelationshipType type, Optional optN1, Optional optN2) {
		if (optN1.isPresent() && optN2.isPresent()) {
			CstNode n1 = optN1.get();
			CstNode n2 = optN2.get();
			return edges.getOrDefault(n1.getId(), Collections.emptyList()).stream()
				.filter(rel -> rel.getType().equals(type) && idMap.get(rel.getN2()).equals(n2))
				.findFirst().isPresent();
		}
		return false;
	}
	
	public Collection findReverseRelationships(CstNodeRelationshipType type, CstNode node) {
		return this.findRelationships(reverseEdges, type, node, rel -> idMap.get(rel.getN1()));
	}
	
	private Collection findRelationships(Map> map, CstNodeRelationshipType type, CstNode node, Function mappingFn) {
		return map.getOrDefault(node.getId(), Collections.emptyList()).stream()
			.filter(rel -> rel.getType().equals(type))
			.map(mappingFn).collect(Collectors.toList());
	}
	
	public Optional findByNamePath(String... namePath) {
		return findByNamePath(cstRoot, namePath);
	}
	
	public static Optional findByNamePath(CstRoot cstRoot, String... namePath) {
		return findByNamePathRecursive(cstRoot.getNodes(), 0, namePath);
	}
	
	public static Optional findByFullName(HasChildrenNodes parent, String name) {
		for (CstNode node : parent.getNodes()) {
			if (fullName(node).equals(name)) {
				return Optional.of(node);
			}
		}
		return Optional.empty();
	}
	
	private static Optional findByNamePathRecursive(List list, int depth, String[] namePath) {
		if (depth >= namePath.length) {
			throw new IllegalArgumentException(String.format("depth should be less than namePath.length"));
		}
		String name = namePath[depth];
		for (CstNode node : list) {
			if (fullName(node).equals(name)) {
				if (depth == namePath.length - 1) {
					return Optional.of(node);
				} else {
					return findByNamePathRecursive(node.getNodes(), depth + 1, namePath);
				}
			}
		}
		return Optional.empty();
	}
	
	public static boolean sameName(CstNode n1, CstNode n2) {
		return !n1.getSimpleName().isEmpty() && n1.getSimpleName().equals(n2.getSimpleName());
	}
	
	public static boolean sameSignature(CstNode n1, CstNode n2) {
		return signature(n1).equals(signature(n2));
	}
	
	public static boolean sameNamespace(CstNode n1, CstNode n2) {
		return Objects.equals(n1.getNamespace(), n2.getNamespace());
	}
	
	public static String signature(CstNode n) {
		return n.getLocalName();
	}
	
	public static String fullName(CstNode n) {
		return n.getNamespace() != null ? n.getNamespace() + n.getLocalName() : n.getLocalName();
	}
	
	public static boolean anonymous(CstNode n) {
		return n.getSimpleName().isEmpty();
	}
	
	public static boolean leaf(CstNode n) {
		return n.getNodes().isEmpty();
	}
	
	public static boolean sameType(CstNode n1, CstNode n2) {
		return n1.getType().equals(n2.getType());
	}
	
	public static boolean childOf(CstNode n1, CstNode n2) {
		return n1.getParent().isPresent() && n1.getParent().get().equals(n2);
	}
	
	public void printRelationships(PrintStream out) {
		out.print("Relationships:\n");
		for (CstNodeRelationship rel : cstRoot.getRelationships()) {
			CstNode n1 = idMap.get(rel.getN1());
			CstNode n2 = idMap.get(rel.getN2());
			out.print(String.format("%s %s %s\n", n1.getLocalName(), rel.getType(), n2.getLocalName()));
		}
	}
	
	public void computeSourceRepresentation(CstNode node) {
		if (!srMap.containsKey(node)) {
			String sourceCode = fileMap.get(node.getLocation().getFile());
			List nodeTokens = retrieveTokens(sourceCode, node, false);
			srMap.put(node, srb.buildForNode(node, isBefore, nodeTokens));
			srNameMap.put(node, srb.buildForName(node, isBefore));
			
			if (node.getLocation().getBegin() != node.getLocation().getBodyBegin()) {
				List nodeBodyTokens = retrieveTokens(sourceCode, node, true);
				T body = srb.buildForFragment(nodeBodyTokens);
				List tokensToIgnore = new ArrayList<>();
				for (Parameter parameter : node.getParameters()) {
					tokensToIgnore.add(parameter.getName());
				}
				tokensToIgnore.addAll(getTokensToIgnoreInNodeBody(node));
				T normalizedBody = srb.minus(body, tokensToIgnore);
				srBodyMap.put(node, normalizedBody);
				
			} else {
				srBodyMap.put(node, srMap.get(node));
			}
		}
	}
	
	private List retrieveTokens(String sourceCode, CstNode node, boolean bodyOnly) {
		return retrieveTokens(cstRoot, sourceCode, node, bodyOnly);
	}
	
	public static List retrieveTokens(CstRoot cstRoot, String sourceCode, CstNode node, boolean bodyOnly) {
		Location location = node.getLocation();
		int nodeStart;
		int nodeEnd;
		if (bodyOnly) {
			nodeStart = location.getBodyBegin();
			nodeEnd = location.getBodyEnd();
		} else {
			nodeStart = location.getBegin();
			nodeEnd = location.getEnd();
		}
		TokenizedSource tokenizedSourceCode = cstRoot.getTokenizedSource().get(location.getFile());
		List tokens = new ArrayList<>();
		for (int[] tokenPositon : tokenizedSourceCode.getTokens()) {
			int tokenStart = tokenPositon[TokenizedSource.START];
			int tokenEnd = tokenPositon[TokenizedSource.END];
			if (tokenStart < nodeStart) {
				continue;
			}
			if (tokenStart >= nodeEnd) {
				break;
			}
			tokens.add(sourceCode.substring(tokenStart, tokenEnd));
		}
		return tokens;
	}
	
	private Collection getTokensToIgnoreInNodeBody(CstNode node) {
		return Arrays.asList("return");
	}
	
	public T sourceRep(CstNode n) {
		if (!srMap.containsKey(n)) {
			throw new RuntimeException("Source representation not computed");
		}
		return srMap.get(n);
	}
	
	public T bodySourceRep(CstNode n) {
		if (!srBodyMap.containsKey(n)) {
			throw new RuntimeException("Source representation not computed");
		}
		return srBodyMap.get(n);
	}
	
	public T nameSourceRep(CstNode n) {
		if (!srNameMap.containsKey(n)) {
			throw new RuntimeException("Source representation not computed");
		}
		return srNameMap.get(n);
	}
	
	public static List getNodePath(CstNode node) {
		LinkedList path = new LinkedList<>();
		computeNodePath(path, node);
		return path;
	}
	
	private static void computeNodePath(LinkedList path, CstNode node) {
		String nodeName;
		if (node.getNamespace() != null) {
			nodeName = node.getNamespace() + node.getLocalName();
		} else {
			nodeName = node.getLocalName();
		}
		path.addFirst(nodeName);
		if (node.getParent().isPresent()) {
			computeNodePath(path, node.getParent().get());
		}
	}

	public boolean isNameUnique(CstNode n2) {
		return findByLocalName(n2.getLocalName()).size() == 1;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy