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

spoon.experimental.SpoonifierVisitor Maven / Gradle / Ivy

Go to download

Spoon is a tool for meta-programming, analysis and transformation of Java programs.

There is a newer version: 11.1.1-beta-14
Show newest version
/*
 * 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.experimental;

import org.apache.commons.lang3.StringEscapeUtils;
import spoon.reflect.code.CtBinaryOperator;
import spoon.reflect.code.CtComment;
import spoon.reflect.code.CtLabelledFlowBreak;
import spoon.reflect.code.CtLiteral;
import spoon.reflect.code.CtOperatorAssignment;
import spoon.reflect.code.CtStatement;
import spoon.reflect.code.CtUnaryOperator;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtModifiable;
import spoon.reflect.declaration.CtNamedElement;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.path.CtRole;
import spoon.reflect.reference.CtArrayTypeReference;
import spoon.reflect.reference.CtIntersectionTypeReference;
import spoon.reflect.reference.CtPackageReference;
import spoon.reflect.reference.CtReference;
import spoon.reflect.reference.CtTypeParameterReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.reference.CtWildcardReference;
import spoon.reflect.visitor.CtInheritanceScanner;
import spoon.reflect.visitor.CtScanner;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Visitor that generates factory calls to recreate the AST visited.
 *
 */
public class SpoonifierVisitor extends CtScanner {
//public class SpoonifierVisitor extends CtInheritanceScanner {
	StringBuilder result = new StringBuilder();
	Map variableCount = new HashMap<>();
	Deque parentName = new ArrayDeque<>();
	Deque> roleContainer = new ArrayDeque<>();

	PropertyScanner propertyScanner = new PropertyScanner();

	public boolean printTabs;
	int tabs = 0;

	/**
	 * Default constructor for SpoonifierVisitor.
	 * Print with tabulations.
	 */
	public SpoonifierVisitor() {
		this(true);
	}

	/**
	 * @param printTabs if set to true, tabulations will be printed to match the structure of the AST constructed.
	 */
	public SpoonifierVisitor(boolean printTabs) {
		this.printTabs = printTabs;
	}

	/**
	 * @return the generated code.
	 *
	 * Note that this code assume a variable Factory factory is already aessible in the scope.
	 */
	public String getResult() {
		return result.toString();
	}

	private boolean isLeafTypeReference(CtElement element) {
		if (!(element instanceof CtTypeReference)) {
			return false;
		}
		if (element instanceof CtArrayTypeReference
				|| element instanceof CtWildcardReference
				|| element instanceof CtTypeParameterReference
				|| element instanceof CtIntersectionTypeReference) {
			return false;
		}
		CtTypeReference reference = (CtTypeReference) element;
		return reference.getDeclaringType() == null
				&& reference.getActualTypeArguments().isEmpty()
				&& reference.getAnnotations().isEmpty()
				&& reference.getComments().isEmpty();
	}


	public void enter(CtElement element) {
		if (element instanceof CtPackageReference
				&& isLeafTypeReference(element.getParent())) {
			return;
		}
		tabs++;

		String elementClass = element.getClass().getSimpleName();
		if (elementClass.endsWith("Impl")) {
			elementClass = elementClass.replace("Impl", "");
		}

		String variableName = null;
		if (isLeafTypeReference(element)) {
			CtTypeReference typeRef = (CtTypeReference) element;
			if (typeRef.isPrimitive()) {
				switch (typeRef.getSimpleName()) {
					case "int":
						variableName = "factory.Type().INTEGER_PRIMITIVE";
						break;
					case "char":
						variableName = "factory.Type().CHARACTER_PRIMITIVE";
						break;
					default:
						variableName = "factory.Type()." + typeRef.getSimpleName().toUpperCase() + "_PRIMITIVE";
				}
			} else if (typeRef.getSimpleName().equals("")) {
				variableName = "factory.Type().NULL_TYPE";
			} else if (typeRef.getPackage().isImplicit()) {
				variableName =  "factory.Type().createSimplyQualifiedReference(\"" + typeRef.getQualifiedName() + "\")";
			} else {
				variableName =  "factory.Type().createReference(\"" + typeRef.getQualifiedName() + "\")";
			}
		} else {
			variableName = getVariableName(elementClass);
			result.append(printTabs() + elementClass + " " + variableName + " = factory.create" + elementClass.replaceFirst("Ct", "") + "();");
			result.append("\n");

			if (element.isImplicit()) {
				result.append(printTabs() + variableName + ".setImplicit(true);\n");
			}
			propertyScanner.variableName = variableName;
			element.accept(propertyScanner);
		}

		if (element.isParentInitialized() && !parentName.isEmpty()) {
			CtRole elementRoleInParent = element.getRoleInParent();

			CtElement parent = element.getParent();
			Object o  = parent.getValueByRole(elementRoleInParent);
			if (o instanceof Map) {
				handleContainer(element, parent, elementRoleInParent, variableName, "Map");
			} else if (o instanceof List) {
				handleContainer(element, parent, elementRoleInParent, variableName, "List");
			} else if (o instanceof Set) {
				handleContainer(element, parent, elementRoleInParent, variableName, "Set");
			} else {
				result.append(printTabs() + parentName.getFirst() + ".setValueByRole(CtRole." + elementRoleInParent.name() + ", " + variableName + ");\n");
			}
		}

		parentName.push(variableName);
		roleContainer.push(new HashMap<>());
	}

	private String getVariableName(String className) {
		if (!variableCount.containsKey(className)) {
			variableCount.put(className, 0);
		}
		int count = variableCount.get(className);
		String variableName = className.substring(0, 1).toLowerCase() + className.substring(1) + count;
		variableCount.put(className, count + 1);
		return variableName;
	}

	private void handleContainer(CtElement element, CtElement parent, CtRole elementRoleInParent, String variableName, String container) {
		String concreteClass = null;

		switch (container) {
			case "Map":
				concreteClass = "HashMap";
				break;
			case "List":
				concreteClass = "ArrayList";
				break;
			case "Set":
				concreteClass = "HashSet";
				break;
		}

		String containerName;
		if (!roleContainer.getFirst().containsKey(elementRoleInParent)) {
			containerName = parentName.getFirst() + elementRoleInParent.toString().substring(0, 1).toUpperCase() + elementRoleInParent.toString().substring(1) + "s";
			roleContainer.getFirst().put(elementRoleInParent, containerName);
			result.append(printTabs() + container + " " + containerName + " = new " + concreteClass + "();\n");
		} else {
			containerName = roleContainer.getFirst().get(elementRoleInParent);
		}

		if (container.equals("Map")) {
			//This is going to be dirty.
			//In case where different keys point toward the same value,
			//some useless variable will be created.
			List keys = new ArrayList<>();
			Map m = parent.getValueByRole(elementRoleInParent);
			for (Object e : m.entrySet()) {
				Map.Entry entry = (Map.Entry) e;
				if (entry.getValue().equals(element)) {
					keys.add((String) entry.getKey());
				}
			}
			for (String key: keys) {
				result.append(printTabs() + containerName + ".put(\"" + key + "\", " + variableName + ");\n");
			}

		} else {
			result.append(printTabs() + containerName + ".add(" + variableName + ");\n");
		}
	}

	public void exit(CtElement element) {
		if (element instanceof CtPackageReference
				&& isLeafTypeReference(element.getParent())) {
			return;
		}
		if (!roleContainer.getFirst().isEmpty()) {
			for (CtRole role: roleContainer.getFirst().keySet()) {
				String variableName = roleContainer.getFirst().get(role);
				result.append(printTabs())
						.append(parentName.getFirst())
						.append(".setValueByRole(CtRole.")
						.append(role.name())
						.append(", ")
						.append(variableName)
						.append(");\n");
			}
		}
		parentName.pop();
		roleContainer.pop();
		tabs--;
	}

	private String printTabs() {
		String res = "";
		if (printTabs) {
			for (int i = 0; i < tabs; i++) {
				res += "\t";
			}
		}
		return res;
	}

	class PropertyScanner extends CtInheritanceScanner {
		public String variableName;

		/**
		 * visitLiteral
		 * @param element
		 */
		@Override
		public void visitCtLiteral(CtLiteral element) {
			if (element.getType().isPrimitive()) {
				result.append(printTabs())
							.append(variableName)
							.append(".setValue((")
							.append(element.getType().getSimpleName())
							.append(") ")
							.append(element)
							.append(");\n");
				if (element.getBase() != null) {
					result.append(printTabs())
							.append(variableName)
							.append(".setBase(LiteralBase.")
							.append(element.getBase().name())
							.append(");\n");
				}
			} else if (element.getType().getQualifiedName().equals("java.lang.String")) {
				result.append(printTabs())
							.append(variableName)
							.append(".setValue(\"")
							.append(StringEscapeUtils.escapeJava((String) element.getValue()))
							.append("\");\n");
			}
			super.visitCtLiteral(element);
		}

		@Override
		public void visitCtBinaryOperator(CtBinaryOperator element) {
			result.append(printTabs() + variableName + ".setKind(BinaryOperatorKind." + element.getKind().name() + ");\n");
			super.visitCtBinaryOperator(element);
		}

		@Override
		public void visitCtUnaryOperator(CtUnaryOperator element) {
			result.append(printTabs() + variableName + ".setKind(UnaryOperatorKind." + element.getKind().name() + ");\n");
			super.visitCtUnaryOperator(element);
		}

		@Override
		public void visitCtOperatorAssignment(CtOperatorAssignment element) {
			result.append(printTabs() + variableName + ".setKind(BinaryOperatorKind." + element.getKind().name() + ");\n");
			super.visitCtOperatorAssignment(element);
		}

		@Override
		public void visitCtComment(CtComment element) {
			result.append(printTabs() + variableName + ".setCommentType(CtComment.CommentType." + element.getCommentType().name() + ");\n");
			result.append(printTabs() + variableName + ".setContent(\"" + StringEscapeUtils.escapeJava(element.getContent()) + "\");\n");
			super.visitCtComment(element);
		}

		@Override
		public void visitCtParameter(CtParameter element) {
			if (element.isVarArgs()) {
				result.append(printTabs() + variableName + ".setVarArgs(true);\n");
			}
			super.visitCtParameter(element);
		}

		@Override
		public void visitCtMethod(CtMethod element) {
			if (element.isDefaultMethod()) {
				result.append(printTabs() + variableName + ".setDefaultMethod(true);\n");
			}
			super.visitCtMethod(element);
		}

		@Override
		public void scanCtReference(CtReference element) {
			result.append(printTabs() + variableName + ".setSimpleName(\"" + element.getSimpleName() + "\");\n");
		}

		@Override
		public void scanCtNamedElement(CtNamedElement element) {
			result.append(printTabs() + variableName + ".setSimpleName(\"" + element.getSimpleName() + "\");\n");
		}

		@Override
		public void scanCtModifiable(CtModifiable element) {
			if (!element.getModifiers().isEmpty()) {
				result.append(printTabs() + "Set " + variableName + "Modifiers = new HashSet<>();\n");
				for (ModifierKind mod : element.getModifiers()) {
					result.append(printTabs() + variableName + "Modifiers.add(ModifierKind." + mod.name() + ");\n");
				}
				result.append(printTabs() + variableName + ".setModifiers(" + variableName + "Modifiers);\n");
			}
		}

		@Override
		public void scanCtStatement(CtStatement element) {
			if (element.getLabel() != null) {
				result.append(printTabs() + variableName + ".setLabel(\"" + element.getLabel() + "\");\n");
			}
		}

		@Override
		public void scanCtLabelledFlowBreak(CtLabelledFlowBreak element) {
			if (element.getTargetLabel() != null) {
				result.append(printTabs() + variableName + ".setTargetLabel(\"" + element.getTargetLabel() + "\");\n");
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy