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

Java.src.org.antlr.v4.runtime.misc.RuleDependencyChecker Maven / Gradle / Ivy

There is a newer version: 4.9.0
Show newest version
/*
 * Copyright (c) 2012 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD-3-Clause license that
 * can be found in the LICENSE.txt file in the project root.
 */
package org.antlr.v4.runtime.misc;

import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.RuleDependencies;
import org.antlr.v4.runtime.RuleDependency;
import org.antlr.v4.runtime.RuleVersion;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Sam Harwell
 */
public class RuleDependencyChecker {
	private static final Logger LOGGER = Logger.getLogger(RuleDependencyChecker.class.getName());

	private static final Set> checkedTypes = new HashSet>();

	public static void checkDependencies(Class dependentClass) {
		if (isChecked(dependentClass)) {
			return;
		}

		List> typesToCheck = new ArrayList>();
		typesToCheck.add(dependentClass);
		Collections.addAll(typesToCheck, dependentClass.getDeclaredClasses());
		for (final Class clazz : typesToCheck) {
			if (isChecked(clazz)) {
				continue;
			}

			List> dependencies = getDependencies(clazz);
			if (dependencies.isEmpty()) {
				continue;
			}

			checkDependencies(dependencies, dependencies.get(0).getItem1().recognizer());
		}
	}

	private static boolean isChecked(Class clazz) {
		synchronized (checkedTypes) {
			return checkedTypes.contains(clazz);
		}
	}

	private static void markChecked(Class clazz) {
		synchronized (checkedTypes) {
			checkedTypes.add(clazz);
		}
	}

	private static void checkDependencies(List> dependencies, Class> recognizerClass) {
		String[] ruleNames = getRuleNames(recognizerClass);
		int[] ruleVersions = getRuleVersions(recognizerClass, ruleNames);

		StringBuilder incompatible = new StringBuilder();
		for (Tuple2 dependency : dependencies) {
			if (!recognizerClass.isAssignableFrom(dependency.getItem1().recognizer())) {
				continue;
			}

			if (dependency.getItem1().rule() < 0 || dependency.getItem1().rule() >= ruleVersions.length) {
				incompatible.append(String.format("Element %s dependent on unknown rule %d@%d in %s\n",
												  dependency.getItem2().toString(),
												  dependency.getItem1().rule(),
												  dependency.getItem1().version(),
												  dependency.getItem1().recognizer().getSimpleName()));
			}
			else if (ruleVersions[dependency.getItem1().rule()] != dependency.getItem1().version()) {
				incompatible.append(String.format("Element %s dependent on rule %s@%d (found @%d) in %s\n",
												  dependency.getItem2().toString(),
												  ruleNames[dependency.getItem1().rule()],
												  dependency.getItem1().version(),
												  ruleVersions[dependency.getItem1().rule()],
												  dependency.getItem1().recognizer().getSimpleName()));
			}
		}

		if (incompatible.length() != 0) {
			throw new IllegalStateException(incompatible.toString());
		}

		markChecked(recognizerClass);
	}

	private static int[] getRuleVersions(Class> recognizerClass, String[] ruleNames) {
		int[] versions = new int[ruleNames.length];

		Field[] fields = recognizerClass.getFields();
		for (Field field : fields) {
			boolean isStatic = (field.getModifiers() & Modifier.STATIC) != 0;
			boolean isInteger = field.getType() == Integer.TYPE;
			if (isStatic && isInteger && field.getName().startsWith("RULE_")) {
				try {
					String name = field.getName().substring("RULE_".length());
					if (name.isEmpty() || !Character.isLowerCase(name.charAt(0))) {
						continue;
					}

					int index = field.getInt(null);
					if (index < 0 || index >= versions.length) {
						Object[] params = { index, field.getName(), recognizerClass.getSimpleName() };
						LOGGER.log(Level.WARNING, "Rule index {0} for rule ''{1}'' out of bounds for recognizer {2}.", params);
						continue;
					}

					Method ruleMethod = getRuleMethod(recognizerClass, name);
					if (ruleMethod == null) {
						Object[] params = { name, recognizerClass.getSimpleName() };
						LOGGER.log(Level.WARNING, "Could not find rule method for rule ''{0}'' in recognizer {1}.", params);
						continue;
					}

					RuleVersion ruleVersion = ruleMethod.getAnnotation(RuleVersion.class);
					int version = ruleVersion != null ? ruleVersion.value() : 0;
					versions[index] = version;
				} catch (IllegalArgumentException ex) {
					LOGGER.log(Level.WARNING, null, ex);
				} catch (IllegalAccessException ex) {
					LOGGER.log(Level.WARNING, null, ex);
				}
			}
		}

		return versions;
	}

	private static Method getRuleMethod(Class> recognizerClass, String name) {
		Method[] declaredMethods = recognizerClass.getMethods();
		for (Method method : declaredMethods) {
			if (method.getName().equals(name) && method.isAnnotationPresent(RuleVersion.class)) {
				return method;
			}
		}

		return null;
	}

	private static String[] getRuleNames(Class> recognizerClass) {
		try {
			Field ruleNames = recognizerClass.getField("ruleNames");
			return (String[])ruleNames.get(null);
		} catch (NoSuchFieldException ex) {
			LOGGER.log(Level.WARNING, null, ex);
		} catch (SecurityException ex) {
			LOGGER.log(Level.WARNING, null, ex);
		} catch (IllegalArgumentException ex) {
			LOGGER.log(Level.WARNING, null, ex);
		} catch (IllegalAccessException ex) {
			LOGGER.log(Level.WARNING, null, ex);
		}

		return new String[0];
	}

	public static List> getDependencies(Class clazz) {
		List> result = new ArrayList>();
		List supportedTarget = Arrays.asList(RuleDependency.class.getAnnotation(Target.class).value());
		for (ElementType target : supportedTarget) {
			switch (target) {
			case TYPE:
				if (!clazz.isAnnotation()) {
					getElementDependencies(clazz, result);
				}
				break;
			case ANNOTATION_TYPE:
				if (!clazz.isAnnotation()) {
					getElementDependencies(clazz, result);
				}
				break;
			case CONSTRUCTOR:
				for (Constructor ctor : clazz.getDeclaredConstructors()) {
					getElementDependencies(ctor, result);
				}
				break;
			case FIELD:
				for (Field field : clazz.getDeclaredFields()) {
					getElementDependencies(field, result);
				}
				break;
			case LOCAL_VARIABLE:
				System.err.println("Runtime rule dependency checking is not supported for local variables.");
				break;
			case METHOD:
				for (Method method : clazz.getDeclaredMethods()) {
					getElementDependencies(method, result);
				}
				break;
			case PACKAGE:
				// package is not a subset of class, so nothing to do here
				break;
			case PARAMETER:
				System.err.println("Runtime rule dependency checking is not supported for parameters.");
				break;
			}
		}

		return result;
	}

	private static void getElementDependencies(AnnotatedElement annotatedElement, List> result) {
		RuleDependency dependency = annotatedElement.getAnnotation(RuleDependency.class);
		if (dependency != null) {
			result.add(Tuple.create(dependency, annotatedElement));
		}

		RuleDependencies dependencies = annotatedElement.getAnnotation(RuleDependencies.class);
		if (dependencies != null) {
			for (RuleDependency d : dependencies.value()) {
				if (d != null) {
					result.add(Tuple.create(d, annotatedElement));
				}
			}
		}
	}

	private RuleDependencyChecker() {
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy