diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 8dd0fd2..24938af 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -12,5 +12,6 @@
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 13a8247..ce1c62c 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -4,14 +4,12 @@
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 6bd5763..654613f 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,7 +1,10 @@
-
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index f60e927..0bbff88 100644
--- a/README.md
+++ b/README.md
@@ -7,15 +7,20 @@ Compile time validation for queries written in HQL, JPQL, and [Panache][].
[Panache]: https://quarkus.io/guides/hibernate-orm-panache
[Hibernate logo]: http://static.jboss.org/hibernate/images/hibernate_logo_whitebkg_200px.png
+## Requirements
+
+This project now requires at least JDK 11, but JDK 15 or above
+is preferred.
+
## Building
Type `./gradlew` from this project directory.
This produces an artifact with the Maven coordinates
-`org.hibernate:query-validator:1.0-SNAPSHOT` in your local
+`org.hibernate:query-validator:2.0-SNAPSHOT` in your local
Maven repository.
-It also creates a far jar `query-validator-1.0-SNAPSHOT-all.jar`
+It also creates a far jar `query-validator-2.0-SNAPSHOT-all.jar`
in the `build/libs` directory of this project.
## Usage
@@ -27,9 +32,9 @@ basic JPA metadata annotations like `@Entity`, `@ManyToOne`,
mapping information like table and column names if that's what
you prefer.
-1. Put `query-validator-1.0-SNAPSHOT-all.jar` in the
+1. Put `query-validator-2.0-SNAPSHOT-all.jar` in the
compile-time classpath of your project. (Or depend on
- `org.hibernate:query-validator:1.0-SNAPSHOT`.)
+ `org.hibernate:query-validator:2.0-SNAPSHOT`.)
2. Annotate a package or toplevel class with `@CheckHQL`.
#### Usage with plain Hibernate or JPA
@@ -107,7 +112,7 @@ Just compile your code with `javac`, or even with ECJ
(`java -jar ecj-4.6.1.jar`), with the query validator `jar` in
the classpath:
- -classpath query-validator-1.0-SNAPSHOT-all.jar
+ -classpath query-validator-2.0-SNAPSHOT-all.jar
#### Gradle
@@ -115,8 +120,8 @@ Annoyingly, Gradle requires that the dependency on the query
validator be declared *twice*:
dependencies {
- implementation 'org.hibernate:query-validator:1.0-SNAPSHOT'
- annotationProcessor 'org.hibernate:query-validator:1.0-SNAPSHOT'
+ implementation 'org.hibernate:query-validator:2.0-SNAPSHOT'
+ annotationProcessor 'org.hibernate:query-validator:2.0-SNAPSHOT'
}
#### Maven
@@ -128,7 +133,7 @@ the dependency to the query validator.
org.hibernate
query-validator
- 1.0-SNAPSHOT
+ 2.0-SNAPSHOT
true
@@ -161,7 +166,7 @@ manually.
processing**.
2. Then go to **Java Compiler > Annotation Processing >
Factory Path** and click **Add External JARs...** and
- add `build/libs/query-validator-1.0-SNAPSHOT-all.jar`
+ add `build/libs/query-validator-2.0-SNAPSHOT-all.jar`
from this project directory.
Your project properties should look like this:
@@ -185,22 +190,16 @@ If the query validator doesn't run, please ensure that:
The query validator was developed and tested with:
-- JDK 1.8.0
-- Hibernate 5.4.10.Final
-- ECJ 4.6.1
-- Eclipse IDE 2019-03 to 2020-03 (JDT Core 3.17.0 to 3.18.300)
+- JDK 15, JDK 17, JDK 20
+- Hibernate 5.6.15.Final
+- ECJ 3.33.0
+- Eclipse IDE with JDT Core 3.33.0
Other versions of `javac`, ECJ, and Hibernate may or may not
work. The query validator depends on internal compiler APIs in
`javac` and ECJ, and is therefore sensitive to changes in the
compilers.
-_NOTE: this version of the query validator does not work on
-JDK 9 and above. The [jdk10][] branch in git is a preview of
-the changes required to make it work on JDK 9-12._
-
-[jdk10]: https://github.com/hibernate/query-validator/tree/jdk10
-
## Caveats
Please be aware of the following issues.
@@ -211,13 +210,6 @@ Queries are interpreted according to Hibernate's flavor of JPQL
(i.e. HQL), which is a superset of the query language defined by
the JPA specification.
-One important example of how the languages are different is the
-handling of function names. In the JPA spec, function names like
-`SUBSTRING`, `SQRT`, and `COALESCE` are *reserved words*. In HQL,
-they're just regular identifiers, and you may even write a HQL
-query that directly calls a user-defined or non-portable SQL
-function.
-
#### Function arguments are not checked
Hibernate's query translator never typechecks function arguments
diff --git a/build.gradle b/build.gradle
index b6515cf..2464d61 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,8 @@
-import org.gradle.internal.jvm.Jvm
-
plugins {
id 'java'
id 'groovy'
- id 'maven'
id 'maven-publish'
- id "com.github.johnrengelman.shadow" version "5.0.0"
+ id "com.github.johnrengelman.shadow" version "8.1.1"
}
defaultTasks 'assemble', 'publishToMavenLocal', 'shadowJar', 'test'
@@ -18,35 +15,38 @@ repositories {
}
dependencies {
- compile ('org.hibernate:hibernate-core:5.4.10.Final') {
+ implementation ('org.hibernate:hibernate-core:5.6.15.Final') {
transitive = false
}
//explicit the Hibernate dependencies we need:
- compile 'antlr:antlr:2.7.7'
- compile 'javax.persistence:javax.persistence-api:2.2'
- compile 'javax.transaction:javax.transaction-api:1.3'
- compile 'net.bytebuddy:byte-buddy:1.10.2'
- compile 'org.jboss.logging:jboss-logging:3.3.2.Final'
-
- testRuntime ('io.quarkus:quarkus-hibernate-orm-panache:1.4.1.Final') {
+ implementation 'antlr:antlr:2.7.7'
+ implementation 'javax.persistence:javax.persistence-api:2.2'
+ implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0'
+ implementation 'javax.transaction:javax.transaction-api:1.3'
+ implementation 'net.bytebuddy:byte-buddy:1.14.5'
+ implementation 'org.jboss.logging:jboss-logging:3.5.0.Final'
+
+ testRuntimeOnly ('io.quarkus:quarkus-hibernate-orm-panache:3.1.0.Final') {
transitive = false
}
- testRuntime ('io.quarkus:quarkus-panache-common:1.4.1.Final') {
+ testRuntimeOnly ('io.quarkus:quarkus-panache-common:3.1.0.Final') {
transitive = false
}
- compile 'org.codehaus.groovy:groovy:2.5.6:indy'
+ implementation 'org.apache.groovy:groovy:4.0.12'
+
+ implementation 'org.eclipse.jdt:ecj:3.33.0'
- compile 'org.eclipse.jdt.core.compiler:ecj:4.6.1'
- compile files(Jvm.current().toolsJar)
+// testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.3'
+ testImplementation 'junit:junit:4.13.2'
- testCompile 'junit:junit:4.12'
+ implementation 'javax.xml.bind:jaxb-api:2.3.1'
}
group = 'org.hibernate'
-version = '1.0-SNAPSHOT'
+version = '2.0-SNAPSHOT'
description = 'query-validator'
-sourceCompatibility = '1.8'
+sourceCompatibility = '8'
sourceSets {
main {
@@ -58,8 +58,8 @@ sourceSets {
shadowJar {
dependencies {
- exclude(dependency('org.eclipse.jdt.core.compiler:ecj'))
- exclude "tools.jar"
+ exclude(dependency('org.eclipse.jdt:ecj'))
+// exclude "tools.jar"
}
relocate ('org.hibernate', 'org.hibernate.query.validator.hibernate') {
exclude 'org.hibernate.query.validator.*'
@@ -81,14 +81,17 @@ shadowJar {
relocate 'groovyjarjarasm', 'org.hibernate.query.validator.asm'
relocate 'groovyjarjarcommonscli', 'org.hibernate.query.validator.cli'
relocate 'groovyjarjarpicocli', 'org.hibernate.query.validator.picocli'
- classifier = 'all'
+ archiveClassifier.set('all')
+}
+
+jar {
+ duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
publishing {
publications {
- maven(MavenPublication) {
- from(components.java)
- artifact shadowJar
+ shadow(MavenPublication) { publication ->
+ project.shadow.component(publication)
}
}
}
@@ -103,7 +106,7 @@ tasks.withType(JavaCompile) {
}
task copyDependencies(type: Copy) {
- from configurations.testRuntime
+ from configurations.testRuntimeClasspath
into 'test-runtime-libs'
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ea13fdf..fae0804 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.3.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/src/main/java/org/hibernate/query/validator/ECJProcessor.java b/src/main/java/org/hibernate/query/validator/ECJProcessor.java
index 29c5763..115b3de 100644
--- a/src/main/java/org/hibernate/query/validator/ECJProcessor.java
+++ b/src/main/java/org/hibernate/query/validator/ECJProcessor.java
@@ -55,6 +55,8 @@
* for ECJ.
*
* @see CheckHQL
+ *
+ * @author Gavin King
*/
//@SupportedAnnotationTypes(CHECK_HQL)
public class ECJProcessor extends AbstractProcessor {
@@ -81,9 +83,9 @@ private void checkHQL(CompilationUnitDeclaration unit, Compiler compiler) {
TypeElement typeElement = elements.getTypeElement(qualifiedName(type.binding));
TypeElement panacheEntity = PanacheUtils.isPanache(typeElement, processingEnv.getTypeUtils(), elements);
type.traverse(new ASTVisitor() {
- Set setParameterLabels = new HashSet<>();
- Set setParameterNames = new HashSet<>();
- Set setOrderBy = new HashSet<>();
+ final Set setParameterLabels = new HashSet<>();
+ final Set setParameterNames = new HashSet<>();
+ final Set setOrderBy = new HashSet<>();
boolean immediatelyCalled;
@Override
@@ -92,6 +94,7 @@ public boolean visit(MessageSend messageSend, BlockScope scope) {
switch (name) {
case "getResultList":
case "getSingleResult":
+ case "getSingleResultOrNull":
immediatelyCalled = true;
break;
case "count":
@@ -118,6 +121,8 @@ public boolean visit(MessageSend messageSend, BlockScope scope) {
}
break;
case "createQuery":
+ case "createSelectionQuery":
+ case "createMutationQuery":
for (Expression argument : messageSend.arguments) {
if (argument instanceof StringLiteral) {
check((StringLiteral) argument, true);
diff --git a/src/main/java/org/hibernate/query/validator/ECJSessionFactory.java b/src/main/java/org/hibernate/query/validator/ECJSessionFactory.java
index 5bad38a..e2aa321 100644
--- a/src/main/java/org/hibernate/query/validator/ECJSessionFactory.java
+++ b/src/main/java/org/hibernate/query/validator/ECJSessionFactory.java
@@ -17,6 +17,9 @@
import static org.hibernate.internal.util.StringHelper.*;
import static org.hibernate.query.validator.HQLProcessor.jpa;
+/**
+ * @author Gavin King
+ */
public abstract class ECJSessionFactory extends MockSessionFactory {
private static final Mocker entityPersister = Mocker.variadic(EntityPersister.class);
diff --git a/src/main/java/org/hibernate/query/validator/EclipseProcessor.groovy b/src/main/java/org/hibernate/query/validator/EclipseProcessor.groovy
index 269bdd1..c9b5975 100644
--- a/src/main/java/org/hibernate/query/validator/EclipseProcessor.groovy
+++ b/src/main/java/org/hibernate/query/validator/EclipseProcessor.groovy
@@ -4,6 +4,7 @@ import antlr.RecognitionException
import org.hibernate.QueryException
import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
import javax.lang.model.element.TypeElement
@@ -20,10 +21,19 @@ import static org.hibernate.query.validator.Validation.validate
* for Eclipse.
*
* @see CheckHQL
+ *
+ * @author Gavin King
*/
//@SupportedAnnotationTypes(CHECK_HQL)
class EclipseProcessor extends AbstractProcessor {
+ private ProcessingEnvironment processingEnv
+
+ synchronized void init(ProcessingEnvironment processingEnv) {
+ this.processingEnv = processingEnv
+ super.init(processingEnv)
+ }
+
static Mocker sessionFactory = Mocker.variadic(EclipseSessionFactory.class)
@Override
@@ -52,15 +62,15 @@ class EclipseProcessor extends AbstractProcessor {
def value = pair.value
if (value instanceof Object[]) {
for (literal in (Object[]) value) {
- if (literal.class.simpleName == "StringConstant") {
+ if (literal.getClass().simpleName == "StringConstant") {
names.add(literal.stringValue())
}
}
}
- else if (value.class.simpleName == "StringConstant") {
+ else if (value.getClass().simpleName == "StringConstant") {
names.add(value.stringValue())
}
- else if (value.class.simpleName == "BinaryTypeBinding") {
+ else if (value.getClass().simpleName == "BinaryTypeBinding") {
String name = qualifiedTypeName(value)
def dialect
try {
@@ -170,13 +180,14 @@ class EclipseProcessor extends AbstractProcessor {
}
private void validateStatement(type, panacheEntity, statement) {
- if (statement != null) switch (statement.class.simpleName) {
+ if (statement != null) switch (statement.getClass().simpleName) {
case "MessageSend":
boolean ic = immediatelyCalled
def name = simpleMethodName(statement)
switch (name) {
case "getResultList":
case "getSingleResult":
+ case "getSingleResultOrNull":
immediatelyCalled = true
break
case "count":
@@ -187,14 +198,14 @@ class EclipseProcessor extends AbstractProcessor {
case "list":
case "find":
// Disabled until we find how to support this type-safe in Javac
-// if (statement.receiver.class.simpleName == "SingleNameReference") {
+// if (statement.receiver.getClass().simpleName == "SingleNameReference") {
// def ref = statement.receiver;
// String target = charToString(ref.token);
// def queryArg = firstArgument(statement);
// if (queryArg != null) {
// checkPanacheQuery(queryArg, target, name, charToString(queryArg.source()), statement.arguments);
// }
- if (statement.receiver.class.simpleName == "ThisReference" && panacheEntity != null) {
+ if (statement.receiver.getClass().simpleName == "ThisReference" && panacheEntity != null) {
String target = panacheEntity.getSimpleName().toString();
def queryArg = firstArgument(statement);
if (queryArg != null) {
@@ -203,16 +214,18 @@ class EclipseProcessor extends AbstractProcessor {
}
break;
case "createQuery":
+ case "createSelectionQuery":
+ case "createMutationQuery":
statement.arguments.each { arg ->
- if (arg.class.simpleName == "StringLiteral"
- || arg.class.simpleName == "ExtendedStringLiteral") {
+ if (arg.getClass().simpleName == "StringLiteral"
+ || arg.getClass().simpleName == "ExtendedStringLiteral") {
validateArgument(arg, true)
}
}
break
case "setParameter":
def arg = statement.arguments.first()
- switch (arg.class.simpleName) {
+ switch (arg.getClass().simpleName) {
case "IntLiteral":
setParameterLabels.add(parseInt(new String((char[])arg.source())))
break
@@ -312,8 +325,8 @@ class EclipseProcessor extends AbstractProcessor {
def firstArgument(messageSend) {
for (argument in messageSend.arguments) {
- if (argument.class.simpleName == "StringLiteral" ||
- argument.class.simpleName == "ExtendedStringLiteral") {
+ if (argument.getClass().simpleName == "StringLiteral" ||
+ argument.getClass().simpleName == "ExtendedStringLiteral") {
return argument;
}
}
@@ -370,7 +383,7 @@ class EclipseProcessor extends AbstractProcessor {
}
}
boolean isParametersCall(firstArg) {
- if (firstArg.class.simpleName == "MessageSend") {
+ if (firstArg.getClass().simpleName == "MessageSend") {
def invocation = firstArg;
String fieldName = charToString(invocation.selector);
if (fieldName.equals("and") && isParametersCall(invocation.receiver)) {
@@ -381,7 +394,7 @@ class EclipseProcessor extends AbstractProcessor {
}
}
else if (fieldName.equals("with")
- && invocation.receiver.class.simpleName == "SingleNameReference") {
+ && invocation.receiver.getClass().simpleName == "SingleNameReference") {
def receiver = invocation.receiver;
String target = charToString(receiver.token);
if (target.equals("Parameters")) {
@@ -397,7 +410,7 @@ class EclipseProcessor extends AbstractProcessor {
}
boolean isSortCall(firstArg) {
- if (firstArg.class.simpleName == "MessageSend") {
+ if (firstArg.getClass().simpleName == "MessageSend") {
def invocation = firstArg;
String fieldName = charToString(invocation.selector);
if ((fieldName.equals("and")
@@ -406,7 +419,7 @@ class EclipseProcessor extends AbstractProcessor {
|| fieldName.equals("direction"))
&& isSortCall(invocation.receiver)) {
for (e in invocation.arguments) {
- if (e.class.simpleName == "StringLiteral") {
+ if (e.getClass().simpleName == "StringLiteral") {
setOrderBy.add(charToString(e.source()));
}
}
@@ -415,12 +428,12 @@ class EclipseProcessor extends AbstractProcessor {
else if ((fieldName.equals("by")
|| fieldName.equals("descending")
|| fieldName.equals("ascending"))
- && invocation.receiver.class.simpleName == "SingleNameReference") {
+ && invocation.receiver.getClass().simpleName == "SingleNameReference") {
def receiver = invocation.receiver;
String target = charToString(receiver.token);
if (target.equals("Sort")) {
for (e in invocation.arguments) {
- if (e.class.simpleName == "StringLiteral") {
+ if (e.getClass().simpleName == "StringLiteral") {
setOrderBy.add(charToString(e.source()));
}
}
diff --git a/src/main/java/org/hibernate/query/validator/EclipseSessionFactory.groovy b/src/main/java/org/hibernate/query/validator/EclipseSessionFactory.groovy
index bdc59a7..1ddbf58 100644
--- a/src/main/java/org/hibernate/query/validator/EclipseSessionFactory.groovy
+++ b/src/main/java/org/hibernate/query/validator/EclipseSessionFactory.groovy
@@ -11,6 +11,9 @@ import static java.util.Arrays.stream
import static org.hibernate.internal.util.StringHelper.*
import static org.hibernate.query.validator.HQLProcessor.jpa
+/**
+ * @author Gavin King
+ */
abstract class EclipseSessionFactory extends MockSessionFactory {
private static final Mocker entityPersister = Mocker.variadic(EntityPersister.class)
@@ -357,7 +360,7 @@ abstract class EclipseSessionFactory extends MockSessionFactory {
}
private static String propertyName(def symbol) {
- if (symbol.class.simpleName == "MethodBinding") {
+ if (symbol.getClass().simpleName == "MethodBinding") {
String name = simpleMethodName(symbol)
if (name.startsWith("get")) {
name = name.substring(3)
@@ -367,7 +370,7 @@ abstract class EclipseSessionFactory extends MockSessionFactory {
}
return Introspector.decapitalize(name)
}
- else if (symbol.class.simpleName == "FieldBinding") {
+ else if (symbol.getClass().simpleName == "FieldBinding") {
return simpleVariableName(symbol)
}
else {
@@ -379,14 +382,14 @@ abstract class EclipseSessionFactory extends MockSessionFactory {
if (isStatic(member) || isTransient(member)) {
return false
}
- else if (member.class.simpleName == "FieldBinding") {
- return accessType == AccessType.FIELD ||
- hasAnnotation(member, jpa("Access"))
+ else if (member.getClass().simpleName == "FieldBinding") {
+ return accessType == AccessType.FIELD
+ || hasAnnotation(member, jpa("Access"))
}
- else if (member.class.simpleName == "MethodBinding") {
- return isGetterMethod(member) &&
- (accessType == AccessType.PROPERTY ||
- hasAnnotation(member, jpa("Access")))
+ else if (member.getClass().simpleName == "MethodBinding") {
+ return isGetterMethod(member)
+ && (accessType == AccessType.PROPERTY
+ || hasAnnotation(member, jpa("Access")))
}
else {
return false
@@ -399,15 +402,15 @@ abstract class EclipseSessionFactory extends MockSessionFactory {
}
String methodName = simpleMethodName(method)
def returnType = method.returnType
- return methodName.startsWith("get") && returnType.id != 6 ||
- methodName.startsWith("is") && returnType.id == 5
+ return methodName.startsWith("get") && returnType.id != 6
+ || methodName.startsWith("is") && returnType.id == 5
}
private static def getMemberType(binding) {
- if (binding.class.simpleName == "MethodBinding") {
+ if (binding.getClass().simpleName == "MethodBinding") {
return binding.returnType
}
- else if (binding.class.simpleName == "FieldBinding") {
+ else if (binding.getClass().simpleName == "FieldBinding") {
return binding.type
}
else {
@@ -678,8 +681,8 @@ abstract class EclipseSessionFactory extends MockSessionFactory {
}
private static boolean missing(type) {
- return type.class.simpleName == "MissingTypeBinding" ||
- type.class.simpleName == "ProblemReferenceBinding"
+ return type.getClass().simpleName == "MissingTypeBinding"
+ || type.getClass().simpleName == "ProblemReferenceBinding"
}
}
diff --git a/src/main/java/org/hibernate/query/validator/ErrorReporter.java b/src/main/java/org/hibernate/query/validator/ErrorReporter.java
new file mode 100644
index 0000000..291d8d9
--- /dev/null
+++ b/src/main/java/org/hibernate/query/validator/ErrorReporter.java
@@ -0,0 +1,74 @@
+package org.hibernate.query.validator;
+
+import antlr.RecognitionException;
+import com.sun.tools.javac.model.JavacElements;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.util.Pair;
+import org.hibernate.QueryException;
+
+import javax.lang.model.element.Element;
+import javax.tools.JavaFileObject;
+
+import static com.sun.tools.javac.resources.CompilerProperties.Warnings.ProcMessager;
+
+/**
+ * @author Gavin King
+ */
+class ErrorReporter implements Validation.Handler {
+
+ private static final String KEY = "proc.messager";
+
+ private final Log log;
+ private final JCTree.JCLiteral literal;
+
+ ErrorReporter(JavacProcessor processor, JCTree.JCLiteral literal, Element element) {
+ this.literal = literal;
+
+ Context context = processor.getContext();
+ log = Log.instance(context);
+ Pair pair =
+ JavacElements.instance(context).getTreeAndTopLevel(element, null, null);
+ JavaFileObject sourcefile = pair == null ? null : pair.snd.sourcefile;
+ if (sourcefile != null) {
+ log.useSource(sourcefile);
+ }
+ }
+
+ @Override
+ public int getErrorCount() {
+ return 0;
+ }
+
+ @Override
+ public void throwQueryException() throws QueryException {
+ }
+
+ @Override
+ public void error(int start, int end, String message) {
+ log.error(literal.pos + start, KEY, message);
+ }
+
+ @Override
+ public void warn(int start, int end, String message) {
+
+ log.warning(literal.pos + start, ProcMessager(message));
+ }
+
+ @Override
+ public void reportError(RecognitionException e) {
+ log.error(literal.pos + e.column, KEY, e.getMessage());
+ }
+
+ @Override
+ public void reportError(String text) {
+ log.error(literal.pos, KEY, text);
+ }
+
+ @Override
+ public void reportWarning(String text) {
+ log.warning(literal.pos, ProcMessager(text));
+ }
+
+}
diff --git a/src/main/java/org/hibernate/query/validator/HQLProcessor.java b/src/main/java/org/hibernate/query/validator/HQLProcessor.java
index 1cdab1c..c535f3b 100644
--- a/src/main/java/org/hibernate/query/validator/HQLProcessor.java
+++ b/src/main/java/org/hibernate/query/validator/HQLProcessor.java
@@ -12,6 +12,9 @@
import java.io.StringWriter;
import java.util.Set;
+/**
+ * @author Gavin King
+ */
@SupportedAnnotationTypes("*")
public class HQLProcessor extends AbstractProcessor {
diff --git a/src/main/java/org/hibernate/query/validator/JavacChecker.java b/src/main/java/org/hibernate/query/validator/JavacChecker.java
new file mode 100644
index 0000000..7a5df02
--- /dev/null
+++ b/src/main/java/org/hibernate/query/validator/JavacChecker.java
@@ -0,0 +1,345 @@
+package org.hibernate.query.validator;
+
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.code.Attribute;
+import com.sun.tools.javac.model.JavacElements;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.TreeScanner;
+import org.hibernate.dialect.Dialect;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+import javax.tools.Diagnostic;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.hibernate.query.validator.HQLProcessor.CHECK_HQL;
+import static org.hibernate.query.validator.HQLProcessor.jpa;
+import static org.hibernate.query.validator.Validation.validate;
+
+/**
+ * @author Gavin King
+ */
+public class JavacChecker {
+ private final JavacProcessor javacProcessor;
+
+ public JavacChecker(JavacProcessor javacProcessor) {
+ this.javacProcessor = javacProcessor;
+ }
+
+ void checkHQL(Element element) {
+ Elements elementUtils = javacProcessor.getProcessingEnv().getElementUtils();
+ if (isCheckable(element) || isCheckable(element.getEnclosingElement())) {
+ List whitelist = getWhitelist(element);
+ JCTree tree = ((JavacElements) elementUtils).getTree(element);
+ TypeElement panacheEntity = PanacheUtils.isPanache(element, javacProcessor.getProcessingEnv().getTypeUtils(), elementUtils);
+ if (tree != null) {
+ tree.accept(new TreeScanner() {
+ final Set setParameterLabels = new HashSet<>();
+ final Set setParameterNames = new HashSet<>();
+ final Set setOrderBy = new HashSet<>();
+ boolean immediatelyCalled;
+
+ private void check(JCTree.JCLiteral jcLiteral, String hql,
+ boolean inCreateQueryMethod) {
+ ErrorReporter handler = new ErrorReporter(javacProcessor, jcLiteral, element);
+ validate(hql, inCreateQueryMethod && immediatelyCalled,
+ setParameterLabels, setParameterNames, handler,
+ JavacProcessor.sessionFactory.make(whitelist, handler, javacProcessor.getProcessingEnv()));
+ }
+
+ private void checkPanacheQuery(JCTree.JCLiteral jcLiteral, String targetType, String methodName, String panacheQl,
+ com.sun.tools.javac.util.List args) {
+ ErrorReporter handler = new ErrorReporter(javacProcessor, jcLiteral, element);
+ collectPanacheArguments(args);
+ int[] offset = new int[1];
+ String hql = PanacheUtils.panacheQlToHql(handler, targetType, methodName,
+ panacheQl, offset, setParameterLabels, setOrderBy);
+ if (hql == null)
+ return;
+ validate(hql, true,
+ setParameterLabels, setParameterNames, handler,
+ JavacProcessor.sessionFactory.make(whitelist, handler, javacProcessor.getProcessingEnv()), offset[0]);
+ }
+
+ private void collectPanacheArguments(com.sun.tools.javac.util.List args) {
+ // first arg is pql
+ // second arg can be Sort, Object..., Map or Parameters
+ setParameterLabels.clear();
+ setParameterNames.clear();
+ setOrderBy.clear();
+ com.sun.tools.javac.util.List nonQueryArgs = args.tail;
+ if (!nonQueryArgs.isEmpty()) {
+ if (isSortCall(nonQueryArgs.head)) {
+ nonQueryArgs = nonQueryArgs.tail;
+ }
+
+ if (!nonQueryArgs.isEmpty()) {
+ JCTree.JCExpression firstArg = nonQueryArgs.head;
+ isParametersCall(firstArg);
+ if (setParameterNames.isEmpty()) {
+ int i = 1;
+ for (JCTree.JCExpression arg : nonQueryArgs) {
+ setParameterLabels.add(i++);
+ }
+ }
+ }
+ }
+ }
+
+ private boolean isParametersCall(JCTree.JCExpression firstArg) {
+ if (firstArg.getKind() == Tree.Kind.METHOD_INVOCATION) {
+ JCTree.JCMethodInvocation invocation = (JCTree.JCMethodInvocation)firstArg;
+ JCTree.JCExpression method = invocation.meth;
+ if (method.getKind() == Tree.Kind.MEMBER_SELECT) {
+ JCTree.JCFieldAccess fa = (JCTree.JCFieldAccess) method;
+ if (fa.name.toString().equals("and") && isParametersCall(fa.selected)) {
+ JCTree.JCLiteral queryArg = firstArgument(invocation);
+ if (queryArg != null && queryArg.value instanceof String) {
+ String name = (String) queryArg.value;
+ setParameterNames.add(name);
+ return true;
+ }
+ }
+ else if (fa.name.toString().equals("with")
+ && fa.selected.getKind() == Tree.Kind.IDENTIFIER) {
+ String target = ((JCTree.JCIdent)fa.selected).name.toString();
+ if (target.equals("Parameters")) {
+ JCTree.JCLiteral queryArg = firstArgument(invocation);
+ if (queryArg != null && queryArg.value instanceof String) {
+ String name = (String) queryArg.value;
+ setParameterNames.add(name);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isSortCall(JCTree.JCExpression firstArg) {
+ if (firstArg.getKind() == Tree.Kind.METHOD_INVOCATION) {
+ JCTree.JCMethodInvocation invocation = (JCTree.JCMethodInvocation)firstArg;
+ JCTree.JCExpression method = invocation.meth;
+ if (method.getKind() == Tree.Kind.MEMBER_SELECT) {
+ JCTree.JCFieldAccess fa = (JCTree.JCFieldAccess) method;
+ String fieldName = fa.name.toString();
+ if ((fieldName.equals("and")
+ || fieldName.equals("descending")
+ || fieldName.equals("ascending")
+ || fieldName.equals("direction"))
+ && isSortCall(fa.selected)) {
+ for (JCTree.JCExpression e : invocation.args) {
+ if (e instanceof JCTree.JCLiteral) {
+ JCTree.JCLiteral lit = (JCTree.JCLiteral)e;
+ if (lit.value instanceof String) {
+ setOrderBy.add((String)lit.value);
+ }
+ }
+ }
+ return true;
+ }
+ else if ((fieldName.equals("by")
+ || fieldName.equals("descending")
+ || fieldName.equals("ascending"))
+ && fa.selected.getKind() == Tree.Kind.IDENTIFIER) {
+ String target = ((JCTree.JCIdent)fa.selected).name.toString();
+ if (target.equals("Sort")) {
+ for (JCTree.JCExpression e : invocation.args) {
+ if (e instanceof JCTree.JCLiteral) {
+ JCTree.JCLiteral lit = (JCTree.JCLiteral)e;
+ if (lit.value instanceof String) {
+ setOrderBy.add((String)lit.value);
+ }
+ }
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ JCTree.JCLiteral firstArgument(JCTree.JCMethodInvocation call) {
+ for (JCTree.JCExpression e : call.args) {
+ return e instanceof JCTree.JCLiteral ?
+ (JCTree.JCLiteral) e : null;
+ }
+ return null;
+ }
+
+ @Override
+ public void visitApply(JCTree.JCMethodInvocation jcMethodInvocation) {
+ String name = getMethodName(jcMethodInvocation.meth);
+ switch (name) {
+ case "getResultList":
+ case "getSingleResult":
+ case "getSingleResultOrNull":
+ immediatelyCalled = true;
+ super.visitApply(jcMethodInvocation);
+ immediatelyCalled = false;
+ break;
+ case "count":
+ case "delete":
+ case "update":
+ case "exists":
+ case "stream":
+ case "list":
+ case "find":
+ switch (jcMethodInvocation.meth.getKind()) {
+ // disable this until we figure out how to type the LHS
+// case MEMBER_SELECT:
+// JCTree.JCFieldAccess fa = (JCFieldAccess) jcMethodInvocation.meth;
+// switch (fa.selected.getKind()) {
+// case IDENTIFIER:
+// JCTree.JCIdent target = (JCIdent) fa.selected;
+// JCTree.JCLiteral queryArg = firstArgument(jcMethodInvocation);
+// if (queryArg != null && queryArg.value instanceof String) {
+// String panacheQl = (String) queryArg.value;
+// checkPanacheQuery(queryArg, target.name.toString(), name, panacheQl, jcMethodInvocation.args);
+// }
+// break;
+// }
+// break;
+ case IDENTIFIER:
+ JCTree.JCLiteral queryArg = firstArgument(jcMethodInvocation);
+ if (queryArg != null && queryArg.value instanceof String && panacheEntity != null) {
+ String panacheQl = (String) queryArg.value;
+ checkPanacheQuery(queryArg, panacheEntity.getSimpleName().toString(), name, panacheQl, jcMethodInvocation.args);
+ }
+ break;
+ }
+ super.visitApply(jcMethodInvocation); //needed!
+ break;
+ case "createQuery":
+ case "createSelectionQuery":
+ case "createMutationQuery":
+ JCTree.JCLiteral queryArg = firstArgument(jcMethodInvocation);
+ if (queryArg != null && queryArg.value instanceof String) {
+ String hql = (String) queryArg.value;
+ check(queryArg, hql, true);
+ }
+ super.visitApply(jcMethodInvocation);
+ break;
+ case "setParameter":
+ JCTree.JCLiteral paramArg = firstArgument(jcMethodInvocation);
+ if (paramArg != null) {
+ if (paramArg.value instanceof String) {
+ setParameterNames.add((String) paramArg.value);
+ }
+ else if (paramArg.value instanceof Integer) {
+ setParameterLabels.add((Integer) paramArg.value);
+ }
+ }
+ super.visitApply(jcMethodInvocation);
+ break;
+ default:
+ super.visitApply(jcMethodInvocation); //needed!
+ break;
+ }
+ }
+
+ @Override
+ public void visitAnnotation(JCTree.JCAnnotation jcAnnotation) {
+ AnnotationMirror annotation = jcAnnotation.attribute;
+ String name = annotation.getAnnotationType().toString();
+ if (name.equals(jpa("NamedQuery"))) {
+ for (JCTree.JCExpression arg : jcAnnotation.args) {
+ if (arg instanceof JCTree.JCAssign) {
+ JCTree.JCAssign assign = (JCTree.JCAssign) arg;
+ if ("query".equals(assign.lhs.toString())
+ && assign.rhs instanceof JCTree.JCLiteral) {
+ JCTree.JCLiteral jcLiteral =
+ (JCTree.JCLiteral) assign.rhs;
+ if (jcLiteral.value instanceof String) {
+ check(jcLiteral, (String) jcLiteral.value, false);
+ }
+ }
+ }
+ }
+ }
+ else {
+ super.visitAnnotation(jcAnnotation); //needed!
+ }
+ }
+
+ });
+ }
+ }
+ }
+
+ private static boolean isCheckAnnotation(AnnotationMirror am) {
+ return am.getAnnotationType().asElement().toString().equals(CHECK_HQL);
+ }
+
+ private static boolean isCheckable(Element element) {
+ for (AnnotationMirror am: element.getAnnotationMirrors()) {
+ if (isCheckAnnotation(am)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private List getWhitelist(Element element) {
+ List list = new ArrayList<>();
+ element.getAnnotationMirrors().forEach(am -> {
+ if (isCheckAnnotation(am)) {
+ am.getElementValues().forEach((var, act) -> {
+ switch (var.getSimpleName().toString()) {
+ case "whitelist":
+ if (act instanceof Attribute.Array) {
+ for (Attribute a: ((Attribute.Array) act).values) {
+ Object value = a.getValue();
+ if (value instanceof String) {
+ list.add(value.toString());
+ }
+ }
+ }
+ break;
+ case "dialect":
+ if (act instanceof Attribute.Class) {
+ String name = act.getValue().toString().replace(".class","");
+ try {
+ Dialect dialect = (Dialect) Class.forName(name).newInstance();
+ list.addAll(dialect.getFunctions().keySet());
+ }
+ catch (Exception e2) {
+ javacProcessor.getProcessingEnv().getMessager()
+ .printMessage(Diagnostic.Kind.ERROR,
+ "could not create dialect " + name,
+ element, am, act);
+ }
+ }
+ break;
+ }
+ });
+ }
+ });
+ return list;
+ }
+
+ private static String getMethodName(ExpressionTree select) {
+ if (select instanceof MemberSelectTree) {
+ MemberSelectTree ref = (MemberSelectTree) select;
+ return ref.getIdentifier().toString();
+ }
+ else if (select instanceof IdentifierTree) {
+ IdentifierTree ref = (IdentifierTree) select;
+ return ref.getName().toString();
+ }
+ else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/hibernate/query/validator/JavacProcessor.java b/src/main/java/org/hibernate/query/validator/JavacProcessor.java
index 4d2d721..92f77ba 100644
--- a/src/main/java/org/hibernate/query/validator/JavacProcessor.java
+++ b/src/main/java/org/hibernate/query/validator/JavacProcessor.java
@@ -1,58 +1,44 @@
package org.hibernate.query.validator;
-import static org.hibernate.query.validator.HQLProcessor.CHECK_HQL;
-import static org.hibernate.query.validator.HQLProcessor.jpa;
-import static org.hibernate.query.validator.Validation.validate;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.util.Context;
import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
-import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.Elements;
-import javax.tools.Diagnostic;
-import javax.tools.JavaFileObject;
-
-import org.hibernate.QueryException;
-import org.hibernate.dialect.Dialect;
-
-import com.sun.source.tree.ExpressionTree;
-import com.sun.source.tree.IdentifierTree;
-import com.sun.source.tree.MemberSelectTree;
-import com.sun.source.tree.Tree.Kind;
-import com.sun.tools.javac.code.Attribute;
-import com.sun.tools.javac.model.JavacElements;
-import com.sun.tools.javac.processing.JavacProcessingEnvironment;
-import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.tree.JCTree.JCExpression;
-import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
-import com.sun.tools.javac.tree.TreeScanner;
-import com.sun.tools.javac.util.Context;
-import com.sun.tools.javac.util.Log;
-import com.sun.tools.javac.util.Pair;
+import java.util.Set;
-import antlr.RecognitionException;
/**
* Annotation processor that validates HQL and JPQL queries
* for `javac`.
*
* @see CheckHQL
+ *
+ * @author Gavin King
*/
//@SupportedAnnotationTypes(CHECK_HQL)
public class JavacProcessor extends AbstractProcessor {
static Mocker sessionFactory = Mocker.variadic(JavacSessionFactory.class);
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ ModularityWorkaround.addOpens();
+ super.init(processingEnv);
+ }
+
+ ProcessingEnvironment getProcessingEnv() {
+ return processingEnv;
+ }
+
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ final JavacChecker javacChecker = new JavacChecker(this);
for (Element element : roundEnv.getRootElements()) {
if (element instanceof PackageElement) {
// for (Element member : element.getEnclosedElements()) {
@@ -60,379 +46,18 @@ public boolean process(Set extends TypeElement> annotations, RoundEnvironment
// }
}
else {
- checkHQL(element);
+ javacChecker.checkHQL(element);
}
}
return false;
}
- private void checkHQL(Element element) {
- Elements elementUtils = processingEnv.getElementUtils();
- if (isCheckable(element) || isCheckable(element.getEnclosingElement())) {
- List whitelist = getWhitelist(element);
- JCTree tree = ((JavacElements) elementUtils).getTree(element);
- TypeElement panacheEntity = PanacheUtils.isPanache(element, processingEnv.getTypeUtils(), elementUtils);
- if (tree != null) {
- tree.accept(new TreeScanner() {
- Set setParameterLabels = new HashSet<>();
- Set setParameterNames = new HashSet<>();
- Set setOrderBy = new HashSet<>();
- boolean immediatelyCalled;
-
- private void check(JCTree.JCLiteral jcLiteral, String hql,
- boolean inCreateQueryMethod) {
- ErrorReporter handler = new ErrorReporter(jcLiteral, element);
- validate(hql, inCreateQueryMethod && immediatelyCalled,
- setParameterLabels, setParameterNames, handler,
- sessionFactory.make(whitelist, handler, processingEnv));
- }
-
- private void checkPanacheQuery(JCTree.JCLiteral jcLiteral, String targetType, String methodName, String panacheQl,
- com.sun.tools.javac.util.List args) {
- ErrorReporter handler = new ErrorReporter(jcLiteral, element);
- collectPanacheArguments(args);
- int[] offset = new int[1];
- String hql = PanacheUtils.panacheQlToHql(handler, targetType, methodName,
- panacheQl, offset, setParameterLabels, setOrderBy);
- if (hql == null)
- return;
- validate(hql, true,
- setParameterLabels, setParameterNames, handler,
- sessionFactory.make(whitelist, handler, processingEnv), offset[0]);
- }
-
- private void collectPanacheArguments(com.sun.tools.javac.util.List args) {
- // first arg is pql
- // second arg can be Sort, Object..., Map or Parameters
- setParameterLabels.clear();
- setParameterNames.clear();
- setOrderBy.clear();
- com.sun.tools.javac.util.List nonQueryArgs = args.tail;
- if (!nonQueryArgs.isEmpty()) {
- if (isSortCall(nonQueryArgs.head)) {
- nonQueryArgs = nonQueryArgs.tail;
- }
-
- if (!nonQueryArgs.isEmpty()) {
- JCExpression firstArg = nonQueryArgs.head;
- isParametersCall(firstArg);
- if (setParameterNames.isEmpty()) {
- int i = 1;
- for (JCExpression arg : nonQueryArgs) {
- setParameterLabels.add(i++);
- }
- }
- }
- }
- }
-
- private boolean isParametersCall(JCExpression firstArg) {
- if (firstArg.getKind() == Kind.METHOD_INVOCATION) {
- JCTree.JCMethodInvocation invocation = (JCTree.JCMethodInvocation)firstArg;
- JCExpression method = invocation.meth;
- if (method.getKind() == Kind.MEMBER_SELECT) {
- JCTree.JCFieldAccess fa = (JCFieldAccess) method;
- if (fa.name.toString().equals("and") && isParametersCall(fa.selected)) {
- JCTree.JCLiteral queryArg = firstArgument(invocation);
- if (queryArg != null && queryArg.value instanceof String) {
- String name = (String) queryArg.value;
- setParameterNames.add(name);
- return true;
- }
- }
- else if (fa.name.toString().equals("with")
- && fa.selected.getKind() == Kind.IDENTIFIER) {
- String target = ((JCTree.JCIdent)fa.selected).name.toString();
- if (target.equals("Parameters")) {
- JCTree.JCLiteral queryArg = firstArgument(invocation);
- if (queryArg != null && queryArg.value instanceof String) {
- String name = (String) queryArg.value;
- setParameterNames.add(name);
- return true;
- }
- }
- }
- }
- }
- return false;
- }
-
- private boolean isSortCall(JCExpression firstArg) {
- if (firstArg.getKind() == Kind.METHOD_INVOCATION) {
- JCTree.JCMethodInvocation invocation = (JCTree.JCMethodInvocation)firstArg;
- JCExpression method = invocation.meth;
- if (method.getKind() == Kind.MEMBER_SELECT) {
- JCTree.JCFieldAccess fa = (JCFieldAccess) method;
- String fieldName = fa.name.toString();
- if ((fieldName.equals("and")
- || fieldName.equals("descending")
- || fieldName.equals("ascending")
- || fieldName.equals("direction"))
- && isSortCall(fa.selected)) {
- for (JCTree.JCExpression e : invocation.args) {
- if (e instanceof JCTree.JCLiteral) {
- JCTree.JCLiteral lit = (JCTree.JCLiteral)e;
- if (lit.value instanceof String) {
- setOrderBy.add((String)lit.value);
- }
- }
- }
- return true;
- }
- else if ((fieldName.equals("by")
- || fieldName.equals("descending")
- || fieldName.equals("ascending"))
- && fa.selected.getKind() == Kind.IDENTIFIER) {
- String target = ((JCTree.JCIdent)fa.selected).name.toString();
- if (target.equals("Sort")) {
- for (JCTree.JCExpression e : invocation.args) {
- if (e instanceof JCTree.JCLiteral) {
- JCTree.JCLiteral lit = (JCTree.JCLiteral)e;
- if (lit.value instanceof String) {
- setOrderBy.add((String)lit.value);
- }
- }
- }
- return true;
- }
- }
- }
- }
- return false;
- }
-
- JCTree.JCLiteral firstArgument(JCTree.JCMethodInvocation call) {
- for (JCTree.JCExpression e : call.args) {
- return e instanceof JCTree.JCLiteral ?
- (JCTree.JCLiteral) e : null;
- }
- return null;
- }
-
- @Override
- public void visitApply(JCTree.JCMethodInvocation jcMethodInvocation) {
- String name = getMethodName(jcMethodInvocation.meth);
- switch (name) {
- case "getResultList":
- case "getSingleResult":
- immediatelyCalled = true;
- super.visitApply(jcMethodInvocation);
- immediatelyCalled = false;
- break;
- case "count":
- case "delete":
- case "update":
- case "exists":
- case "stream":
- case "list":
- case "find":
- switch (jcMethodInvocation.meth.getKind()) {
- // disable this until we figure out how to type the LHS
-// case MEMBER_SELECT:
-// JCTree.JCFieldAccess fa = (JCFieldAccess) jcMethodInvocation.meth;
-// switch (fa.selected.getKind()) {
-// case IDENTIFIER:
-// JCTree.JCIdent target = (JCIdent) fa.selected;
-// JCTree.JCLiteral queryArg = firstArgument(jcMethodInvocation);
-// if (queryArg != null && queryArg.value instanceof String) {
-// String panacheQl = (String) queryArg.value;
-// checkPanacheQuery(queryArg, target.name.toString(), name, panacheQl, jcMethodInvocation.args);
-// }
-// break;
-// }
-// break;
- case IDENTIFIER:
- JCTree.JCLiteral queryArg = firstArgument(jcMethodInvocation);
- if (queryArg != null && queryArg.value instanceof String && panacheEntity != null) {
- String panacheQl = (String) queryArg.value;
- checkPanacheQuery(queryArg, panacheEntity.getSimpleName().toString(), name, panacheQl, jcMethodInvocation.args);
- }
- break;
- }
- super.visitApply(jcMethodInvocation); //needed!
- break;
- case "createQuery":
- JCTree.JCLiteral queryArg = firstArgument(jcMethodInvocation);
- if (queryArg != null && queryArg.value instanceof String) {
- String hql = (String) queryArg.value;
- check(queryArg, hql, true);
- }
- super.visitApply(jcMethodInvocation);
- break;
- case "setParameter":
- JCTree.JCLiteral paramArg = firstArgument(jcMethodInvocation);
- if (paramArg != null) {
- if (paramArg.value instanceof String) {
- setParameterNames.add((String) paramArg.value);
- }
- else if (paramArg.value instanceof Integer) {
- setParameterLabels.add((Integer) paramArg.value);
- }
- }
- super.visitApply(jcMethodInvocation);
- break;
- default:
- super.visitApply(jcMethodInvocation); //needed!
- break;
- }
- }
-
- @Override
- public void visitAnnotation(JCTree.JCAnnotation jcAnnotation) {
- AnnotationMirror annotation = jcAnnotation.attribute;
- String name = annotation.getAnnotationType().toString();
- if (name.equals(jpa("NamedQuery"))) {
- for (JCTree.JCExpression arg : jcAnnotation.args) {
- if (arg instanceof JCTree.JCAssign) {
- JCTree.JCAssign assign = (JCTree.JCAssign) arg;
- if ("query".equals(assign.lhs.toString())
- && assign.rhs instanceof JCTree.JCLiteral) {
- JCTree.JCLiteral jcLiteral =
- (JCTree.JCLiteral) assign.rhs;
- if (jcLiteral.value instanceof String) {
- check(jcLiteral, (String) jcLiteral.value, false);
- }
- }
- }
- }
- }
- else {
- super.visitAnnotation(jcAnnotation); //needed!
- }
- }
-
- });
- }
- }
- }
-
- private static boolean isCheckAnnotation(AnnotationMirror am) {
- return am.getAnnotationType().asElement().toString().equals(CHECK_HQL);
- }
-
- private static boolean isCheckable(Element element) {
- for (AnnotationMirror am: element.getAnnotationMirrors()) {
- if (isCheckAnnotation(am)) {
- return true;
- }
- }
- return false;
- }
-
- private List getWhitelist(Element element) {
- List list = new ArrayList<>();
- element.getAnnotationMirrors().forEach(am -> {
- if (isCheckAnnotation(am)) {
- am.getElementValues().forEach((var, act) -> {
- switch (var.getSimpleName().toString()) {
- case "whitelist":
- if (act instanceof Attribute.Array) {
- for (Attribute a: ((Attribute.Array) act).values) {
- Object value = a.getValue();
- if (value instanceof String) {
- list.add(value.toString());
- }
- }
- }
- break;
- case "dialect":
- if (act instanceof Attribute.Class) {
- String name = act.getValue().toString().replace(".class","");
- try {
- Dialect dialect = (Dialect) Class.forName(name).newInstance();
- list.addAll(dialect.getFunctions().keySet());
- }
- catch (Exception e2) {
- processingEnv.getMessager()
- .printMessage(Diagnostic.Kind.ERROR,
- "could not create dialect " + name,
- element, am, act);
- }
- }
- break;
- }
- });
- }
- });
- return list;
- }
-
- private static String getMethodName(ExpressionTree select) {
- if (select instanceof MemberSelectTree) {
- MemberSelectTree ref = (MemberSelectTree) select;
- return ref.getIdentifier().toString();
- }
- else if (select instanceof IdentifierTree) {
- IdentifierTree ref = (IdentifierTree) select;
- return ref.getName().toString();
- }
- else {
- return null;
- }
- }
-
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
- class ErrorReporter implements Validation.Handler {
-
- private static final String KEY = "proc.messager";
-
- private Log log;
- private JCTree.JCLiteral literal;
-
- ErrorReporter(JCTree.JCLiteral literal, Element element) {
- this.literal = literal;
-
- Context context = getContext();
- log = Log.instance(context);
- Pair pair = JavacElements.instance(context)
- .getTreeAndTopLevel(element, null, null);
- JavaFileObject sourcefile = pair == null ? null :
- pair.snd.sourcefile;
- if (sourcefile != null) {
- log.useSource(sourcefile);
- }
- }
-
- @Override
- public int getErrorCount() {
- return 0;
- }
-
- @Override
- public void throwQueryException() throws QueryException {}
-
- @Override
- public void error(int start, int end, String message) {
- log.error(literal.pos + start, KEY, message);
- }
-
- @Override
- public void warn(int start, int end, String message) {
- log.warning(literal.pos + start, KEY, message);
- }
-
- @Override
- public void reportError(RecognitionException e) {
- log.error(literal.pos + e.column, KEY, e.getMessage());
- }
-
- @Override
- public void reportError(String text) {
- log.error(literal, KEY, text);
- }
-
- @Override
- public void reportWarning(String text) {
- log.warning(literal, KEY, text);
- }
-
- }
-
- private Context getContext() {
+ Context getContext() {
return ((JavacProcessingEnvironment) processingEnv).getContext();
}
}
diff --git a/src/main/java/org/hibernate/query/validator/JavacSessionFactory.java b/src/main/java/org/hibernate/query/validator/JavacSessionFactory.java
index 45d117d..7a78898 100644
--- a/src/main/java/org/hibernate/query/validator/JavacSessionFactory.java
+++ b/src/main/java/org/hibernate/query/validator/JavacSessionFactory.java
@@ -1,12 +1,20 @@
package org.hibernate.query.validator;
-import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.TypeTag;
+import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Names;
import org.hibernate.hql.internal.ast.ParseErrorHandler;
+import org.hibernate.type.BasicType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.CompositeCustomType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.PrimitiveType;
import org.hibernate.type.Type;
-import org.hibernate.type.*;
import org.hibernate.usertype.CompositeUserType;
import javax.lang.model.element.AnnotationMirror;
@@ -16,13 +24,20 @@
import javax.persistence.AccessType;
import java.beans.Introspector;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static java.util.Arrays.stream;
-import static org.hibernate.internal.util.StringHelper.*;
+import static org.hibernate.internal.util.StringHelper.qualify;
+import static org.hibernate.internal.util.StringHelper.root;
+import static org.hibernate.internal.util.StringHelper.split;
+import static org.hibernate.internal.util.StringHelper.unroot;
import static org.hibernate.query.validator.HQLProcessor.jpa;
+/**
+ * @author Gavin King
+ */
public abstract class JavacSessionFactory extends MockSessionFactory {
private static final Mocker component = Mocker.variadic(Component.class);
@@ -38,7 +53,7 @@ public JavacSessionFactory(List functionWhitelist,
ParseErrorHandler handler,
JavacProcessingEnvironment processingEnv) {
super(functionWhitelist, handler);
- Context context = processingEnv.getContext();
+ final Context context = processingEnv.getContext();
names = Names.instance(context);
types = Types.instance(context);
syms = Symtab.instance(context);
@@ -140,8 +155,8 @@ private static CollectionType collectionType(
}
public static abstract class Component implements CompositeUserType {
- private String[] propertyNames;
- private Type[] propertyTypes;
+ private final String[] propertyNames;
+ private final Type[] propertyTypes;
Symbol.TypeSymbol type;
public Component(Symbol.TypeSymbol type,
@@ -157,8 +172,7 @@ public Component(Symbol.TypeSymbol type,
AccessType accessType =
getAccessType(type, defaultAccessType);
for (Symbol member: type.members()
- .getElements(symbol
- -> isPersistable(symbol, accessType))) {
+ .getSymbols(symbol -> isPersistable(symbol, accessType))) {
String name = propertyName(member);
Type propertyType =
propertyType(member, entityName,
@@ -273,12 +287,10 @@ private Symbol.ClassSymbol findEntityClass(String entityName) {
Symbol.ClassSymbol type = findClassByQualifiedName(entityName);
return isEntity(type) ? type : null;
}
- for (Symbol.PackageSymbol pack:
- new ArrayList<>(syms.packages.values())) {
+ for (Symbol pack: syms.unnamedModule.enclosedPackages) {
try {
for (Symbol type: pack.members()
- .getElements(symbol ->
- isMatchingEntity(symbol, entityName))) {
+ .getSymbols(symbol -> isMatchingEntity(symbol, entityName))) {
return (Symbol.ClassSymbol) type;
}
}
@@ -306,8 +318,7 @@ private static Symbol findProperty(Symbol.TypeSymbol type, String propertyName,
AccessType accessType =
getAccessType(type, defaultAccessType);
for (Symbol member: type.members()
- .getElements(symbol -> isMatchingProperty(
- symbol, propertyName, accessType))) {
+ .getSymbols(symbol -> isMatchingProperty(symbol, propertyName, accessType))) {
return member;
}
}
@@ -321,7 +332,7 @@ private static Symbol findProperty(Symbol.TypeSymbol type, String propertyName,
private static boolean isMatchingProperty(Symbol symbol, String propertyName,
AccessType accessType) {
return isPersistable(symbol, accessType)
- && propertyName.equals(propertyName(symbol));
+ && propertyName.equals(propertyName(symbol));
}
private static boolean isGetterMethod(Symbol.MethodSymbol method) {
@@ -331,7 +342,7 @@ private static boolean isGetterMethod(Symbol.MethodSymbol method) {
String methodName = method.name.toString();
TypeTag returnType = method.getReturnType().getTag();
return methodName.startsWith("get") && returnType != TypeTag.VOID
- || methodName.startsWith("is") && returnType == TypeTag.BOOLEAN;
+ || methodName.startsWith("is") && returnType == TypeTag.BOOLEAN;
}
private static boolean hasAnnotation(Symbol member, String annotationName) {
@@ -345,6 +356,16 @@ static AnnotationMirror getAnnotation(Symbol member, String annotationName) {
return mirror;
}
}
+ if (annotationName.startsWith(new StringBuilder("javax.").append("persistence.").toString())) {
+ annotationName = "jakarta" + annotationName.substring(5);
+ for (AnnotationMirror mirror : member.getAnnotationMirrors()) {
+ if (qualifiedName((com.sun.tools.javac.code.Type.ClassType) mirror.getAnnotationType())
+ .equals(annotationName)) {
+ return mirror;
+ }
+ }
+ }
+
return null;
}
@@ -360,13 +381,13 @@ private static Object getAnnotationMember(AnnotationMirror annotation, String me
private static boolean isMappedClass(Symbol.TypeSymbol type) {
return hasAnnotation(type, jpa("Entity"))
- || hasAnnotation(type, jpa("Embeddable"))
- || hasAnnotation(type, jpa("MappedSuperclass"));
+ || hasAnnotation(type, jpa("Embeddable"))
+ || hasAnnotation(type, jpa("MappedSuperclass"));
}
private static boolean isEntity(Symbol.TypeSymbol member) {
return member instanceof Symbol.ClassSymbol
- && hasAnnotation(member, jpa("Entity"));
+ && hasAnnotation(member, jpa("Entity"));
}
private static boolean isId(Symbol member) {
@@ -375,7 +396,7 @@ private static boolean isId(Symbol member) {
private static boolean isTransient(Symbol member) {
return hasAnnotation(member, jpa("Transient"))
- || (member.flags() & Flags.TRANSIENT)!=0;
+ || (member.flags() & Flags.TRANSIENT)!=0;
}
private static boolean isEmbeddableType(Symbol.TypeSymbol type) {
@@ -384,7 +405,7 @@ private static boolean isEmbeddableType(Symbol.TypeSymbol type) {
private static boolean isEmbeddedProperty(Symbol member) {
return hasAnnotation(member, jpa("Embedded"))
- || hasAnnotation(member.type.tsym, jpa("Embeddable"));
+ || hasAnnotation(member.type.tsym, jpa("Embeddable"));
}
private static boolean isElementCollectionProperty(Symbol member) {
@@ -393,12 +414,12 @@ private static boolean isElementCollectionProperty(Symbol member) {
private static boolean isToOneAssociation(Symbol member) {
return hasAnnotation(member, jpa("ManyToOne"))
- || hasAnnotation(member, jpa("OneToOne"));
+ || hasAnnotation(member, jpa("OneToOne"));
}
private static boolean isToManyAssociation(Symbol member) {
return hasAnnotation(member, jpa("ManyToMany"))
- || hasAnnotation(member, jpa("OneToMany"));
+ || hasAnnotation(member, jpa("OneToMany"));
}
private static AnnotationMirror toOneAnnotation(Symbol member) {
@@ -504,7 +525,8 @@ private com.sun.tools.javac.code.Type getElementCollectionElementType(Symbol pro
jpa("ElementCollection"));
com.sun.tools.javac.code.Type classType = (com.sun.tools.javac.code.Type)
getAnnotationMember(annotation, "getElementCollectionClass");
- return classType == null || classType.getKind() == TypeKind.VOID ?
+ return classType == null
+ || classType.getKind() == TypeKind.VOID ?
getCollectionElementType(property) :
classType;
}
@@ -518,7 +540,7 @@ boolean isClassDefined(String qualifiedName) {
boolean isFieldDefined(String qualifiedClassName, String fieldName) {
Symbol.ClassSymbol type = findClassByQualifiedName(qualifiedClassName);
return type != null
- && type.members().lookup(names.fromString(fieldName)).sym != null;
+ && type.members().findFirst( names.fromString(fieldName) ) != null;
}
@Override
@@ -526,7 +548,7 @@ boolean isConstructorDefined(String qualifiedClassName,
List argumentTypes) {
Symbol.ClassSymbol symbol = findClassByQualifiedName(qualifiedClassName);
if (symbol==null) return false;
- for (Symbol cons: symbol.members().getElements(Symbol::isConstructor)) {
+ for (Symbol cons: symbol.members().getSymbols(Symbol::isConstructor)) {
Symbol.MethodSymbol constructor = (Symbol.MethodSymbol) cons;
if (constructor.params.length()==argumentTypes.size()) {
boolean argumentsCheckOut = true;
@@ -611,13 +633,14 @@ private static Class> toPrimitiveClass(Symbol.VarSymbol param) {
}
private Symbol.ClassSymbol findClassByQualifiedName(String path) {
- return syms.classes.get(names.fromString(path));
+ Iterator it = syms.getClassesForName(names.fromString(path)).iterator();
+ return it.hasNext() ? it.next() : null;
}
private static AccessType getDefaultAccessType(Symbol.TypeSymbol type) {
//iterate up the superclass hierarchy
while (type instanceof Symbol.ClassSymbol) {
- for (Symbol member: type.members().getElements()) {
+ for (Symbol member: type.members().getSymbols()) {
if (isId(member)) {
return member instanceof Symbol.MethodSymbol ?
AccessType.PROPERTY : AccessType.FIELD;
@@ -652,11 +675,11 @@ private static boolean isPersistable(Symbol member, AccessType accessType) {
}
else if (member instanceof Symbol.VarSymbol) {
return accessType == AccessType.FIELD
- || hasAnnotation(member, jpa("Access"));
+ || hasAnnotation(member, jpa("Access"));
}
else if (member instanceof Symbol.MethodSymbol) {
return isGetterMethod((Symbol.MethodSymbol) member)
- && (accessType == AccessType.PROPERTY
+ && (accessType == AccessType.PROPERTY
|| hasAnnotation(member, jpa("Access")));
}
else {
diff --git a/src/main/java/org/hibernate/query/validator/MockCollectionPersister.java b/src/main/java/org/hibernate/query/validator/MockCollectionPersister.java
index aaccb1b..8f719f7 100644
--- a/src/main/java/org/hibernate/query/validator/MockCollectionPersister.java
+++ b/src/main/java/org/hibernate/query/validator/MockCollectionPersister.java
@@ -12,6 +12,9 @@
import static org.hibernate.internal.util.StringHelper.root;
+/**
+ * @author Gavin King
+ */
public abstract class MockCollectionPersister implements QueryableCollection {
private static final String[] ID_COLUMN = {"id"};
diff --git a/src/main/java/org/hibernate/query/validator/MockEntityPersister.java b/src/main/java/org/hibernate/query/validator/MockEntityPersister.java
index 815a817..e5d0fbc 100644
--- a/src/main/java/org/hibernate/query/validator/MockEntityPersister.java
+++ b/src/main/java/org/hibernate/query/validator/MockEntityPersister.java
@@ -7,6 +7,7 @@
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.SelectFragment;
+import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.type.ClassType;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
@@ -17,6 +18,9 @@
import static org.hibernate.query.validator.MockSessionFactory.typeHelper;
+/**
+ * @author Gavin King
+ */
public abstract class MockEntityPersister implements EntityPersister, Queryable, DiscriminatorMetadata {
private static final String[] ID_COLUMN = {"id"};
@@ -63,6 +67,12 @@ public SessionFactoryImplementor getFactory() {
return factory;
}
+ @Override
+ public EntityMetamodel getEntityMetamodel() {
+ //TODO: this is bad
+ throw new UnsupportedOperationException();
+ }
+
@Override
public String getEntityName() {
return entityName;
diff --git a/src/main/java/org/hibernate/query/validator/MockJdbcServicesInitiator.java b/src/main/java/org/hibernate/query/validator/MockJdbcServicesInitiator.java
index f971c0e..df6cf3e 100644
--- a/src/main/java/org/hibernate/query/validator/MockJdbcServicesInitiator.java
+++ b/src/main/java/org/hibernate/query/validator/MockJdbcServicesInitiator.java
@@ -7,6 +7,9 @@
import java.util.Map;
+/**
+ * @author Gavin King
+ */
class MockJdbcServicesInitiator extends JdbcServicesInitiator {
static final JdbcServicesInitiator INSTANCE = new MockJdbcServicesInitiator();
diff --git a/src/main/java/org/hibernate/query/validator/MockSessionFactory.java b/src/main/java/org/hibernate/query/validator/MockSessionFactory.java
index a3b20e9..48abcc8 100644
--- a/src/main/java/org/hibernate/query/validator/MockSessionFactory.java
+++ b/src/main/java/org/hibernate/query/validator/MockSessionFactory.java
@@ -17,6 +17,7 @@
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.hql.internal.ast.ParseErrorHandler;
+import org.hibernate.internal.FastSessionServices;
import org.hibernate.internal.TypeLocatorImpl;
import org.hibernate.metamodel.internal.MetamodelImpl;
import org.hibernate.metamodel.spi.MetamodelImplementor;
@@ -34,6 +35,9 @@
import static java.util.Collections.*;
import static org.hibernate.internal.util.StringHelper.isEmpty;
+/**
+ * @author Gavin King
+ */
public abstract class MockSessionFactory implements SessionFactoryImplementor {
private static final SessionFactoryOptions options = Mocker.nullary(MockSessionFactoryOptions.class).get();
@@ -247,6 +251,13 @@ public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver() {
return options.getCurrentTenantIdentifierResolver();
}
+ @Override
+ public FastSessionServices getFastSessionServices() {
+ throw new UnsupportedOperationException();
+ }
+
+
+
@Override
public SQLFunctionRegistry getSqlFunctionRegistry() {
return new SQLFunctionRegistry(getJdbcServices().getDialect(),
diff --git a/src/main/java/org/hibernate/query/validator/MockSessionFactoryOptions.java b/src/main/java/org/hibernate/query/validator/MockSessionFactoryOptions.java
index 34f08e2..bee650b 100644
--- a/src/main/java/org/hibernate/query/validator/MockSessionFactoryOptions.java
+++ b/src/main/java/org/hibernate/query/validator/MockSessionFactoryOptions.java
@@ -15,6 +15,9 @@
import static java.util.Collections.emptyMap;
+/**
+ * @author Gavin King
+ */
public abstract class MockSessionFactoryOptions implements SessionFactoryOptions {
private static final SessionFactoryObserver[] NO_OBSERVERS = new SessionFactoryObserver[0];
diff --git a/src/main/java/org/hibernate/query/validator/ModularityWorkaround.java b/src/main/java/org/hibernate/query/validator/ModularityWorkaround.java
new file mode 100644
index 0000000..4aa4d41
--- /dev/null
+++ b/src/main/java/org/hibernate/query/validator/ModularityWorkaround.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018-2021 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package org.hibernate.query.validator;
+
+import sun.misc.Unsafe;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * COPY/PASTE from Lombok!
+ */
+public class ModularityWorkaround {
+
+ private static Object getJdkCompilerModule() {
+ /* call public api: ModuleLayer.boot().findModule("jdk.compiler").get();
+ but use reflection because we don't want this code to crash on jdk1.7 and below.
+ In that case, none of this stuff was needed in the first place, so we just exit via
+ the catch block and do nothing.
+ */
+
+ try {
+ Class> cModuleLayer = Class.forName("java.lang.ModuleLayer");
+ Method mBoot = cModuleLayer.getDeclaredMethod("boot");
+ Object bootLayer = mBoot.invoke(null);
+ Class> cOptional = Class.forName("java.util.Optional");
+ Method mFindModule = cModuleLayer.getDeclaredMethod("findModule", String.class);
+ Object oCompilerO = mFindModule.invoke(bootLayer, "jdk.compiler");
+ return cOptional.getDeclaredMethod("get").invoke(oCompilerO);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Useful from jdk9 and up; required from jdk16 and up.
+ * This code is supposed to gracefully do nothing on jdk8
+ * and below, as this operation isn't needed there. */
+ public static void addOpens() {
+ Class> cModule;
+ try {
+ cModule = Class.forName("java.lang.Module");
+ } catch (ClassNotFoundException e) {
+ return; //jdk8-; this is not needed.
+ }
+
+ Unsafe unsafe = getUnsafe();
+ Object jdkCompilerModule = getJdkCompilerModule();
+ Object ownModule = getOwnModule();
+ String[] allPkgs = {
+ "com.sun.tools.javac.code",
+ "com.sun.tools.javac.model",
+ "com.sun.tools.javac.processing",
+ "com.sun.tools.javac.tree",
+ "com.sun.tools.javac.util",
+ "com.sun.tools.javac.resources"
+ };
+
+ try {
+ Method m = cModule.getDeclaredMethod("implAddOpens", String.class, cModule);
+ long firstFieldOffset = getFirstFieldOffset(unsafe);
+ unsafe.putBooleanVolatile(m, firstFieldOffset, true);
+ for (String p : allPkgs) {
+ m.invoke(jdkCompilerModule, p, ownModule);
+ }
+ } catch (Exception ignore) {}
+ }
+
+ private static long getFirstFieldOffset(Unsafe unsafe) {
+ try {
+ return unsafe.objectFieldOffset(Parent.class.getDeclaredField("first"));
+ } catch (NoSuchFieldException e) {
+ // can't happen.
+ throw new RuntimeException(e);
+ } catch (SecurityException e) {
+ // can't happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Unsafe getUnsafe() {
+ try {
+ Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ return (Unsafe) theUnsafe.get(null);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private static Object getOwnModule() {
+ try {
+ Method m = Permit.getMethod(Class.class, "getModule");
+ return m.invoke(JavacProcessor.class);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/hibernate/query/validator/Parent.java b/src/main/java/org/hibernate/query/validator/Parent.java
new file mode 100644
index 0000000..be06989
--- /dev/null
+++ b/src/main/java/org/hibernate/query/validator/Parent.java
@@ -0,0 +1,12 @@
+package org.hibernate.query.validator;
+
+import java.io.OutputStream;
+
+@SuppressWarnings("all")
+public class Parent {
+ boolean first;
+ static final Object staticObj = OutputStream.class;
+ volatile Object second;
+ private static volatile boolean staticSecond;
+ private static volatile boolean staticThird;
+}
\ No newline at end of file
diff --git a/src/main/java/org/hibernate/query/validator/Permit.java b/src/main/java/org/hibernate/query/validator/Permit.java
new file mode 100644
index 0000000..9b4cdde
--- /dev/null
+++ b/src/main/java/org/hibernate/query/validator/Permit.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2018-2021 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package org.hibernate.query.validator;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * COPY/PASTE from Lombok!
+ */
+// sunapi suppresses javac's warning about using Unsafe; 'all' suppresses
+// eclipse's warning about the unspecified 'sunapi' key. Leave them both.
+// Yes, javac's definition of the word 'all' is quite contrary to what the
+// dictionary says it means. 'all' does NOT include 'sunapi' according to javac.
+@SuppressWarnings({"sunapi", "all"})
+public class Permit {
+ private Permit() {}
+
+
+ private static final long ACCESSIBLE_OVERRIDE_FIELD_OFFSET;
+ private static final IllegalAccessException INIT_ERROR;
+ private static final sun.misc.Unsafe UNSAFE = (sun.misc.Unsafe) reflectiveStaticFieldAccess(sun.misc.Unsafe.class, "theUnsafe");
+
+ static {
+ Field f;
+ long g;
+ Throwable ex;
+
+ try {
+ g = getOverrideFieldOffset();
+ ex = null;
+ } catch (Throwable t) {
+ f = null;
+ g = -1L;
+ ex = t;
+ }
+
+ ACCESSIBLE_OVERRIDE_FIELD_OFFSET = g;
+ if (ex == null) INIT_ERROR = null;
+ else if (ex instanceof IllegalAccessException) INIT_ERROR = (IllegalAccessException) ex;
+ else {
+ INIT_ERROR = new IllegalAccessException("Cannot initialize Unsafe-based permit");
+ INIT_ERROR.initCause(ex);
+ }
+ }
+
+ public static T setAccessible(T accessor) {
+ if (INIT_ERROR == null) {
+ UNSAFE.putBoolean(accessor, ACCESSIBLE_OVERRIDE_FIELD_OFFSET, true);
+ } else {
+ accessor.setAccessible(true);
+ }
+
+ return accessor;
+ }
+
+ private static long getOverrideFieldOffset() throws Throwable {
+ Field f = null;
+ Throwable saved = null;
+ try {
+ f = AccessibleObject.class.getDeclaredField("override");
+ } catch (Throwable t) {
+ saved = t;
+ }
+
+ if (f != null) {
+ return UNSAFE.objectFieldOffset(f);
+ }
+ // The below seems very risky, but for all AccessibleObjects in java today it does work,
+ // and starting with JDK12, making the field accessible is no longer possible.
+ try {
+ return UNSAFE.objectFieldOffset(Fake.class.getDeclaredField("override"));
+ } catch (Throwable t) {
+ throw saved;
+ }
+ }
+
+ static class Fake {
+ boolean override;
+ Object accessCheckCache;
+ }
+
+ public static Method getMethod(Class> c, String mName, Class>... parameterTypes) throws NoSuchMethodException {
+ Method m = null;
+ Class> oc = c;
+ while (c != null) {
+ try {
+ m = c.getDeclaredMethod(mName, parameterTypes);
+ break;
+ } catch (NoSuchMethodException e) {}
+ c = c.getSuperclass();
+ }
+
+ if (m == null) throw new NoSuchMethodException(oc.getName() + " :: " + mName + "(args)");
+ return setAccessible(m);
+ }
+
+ public static Method permissiveGetMethod(Class> c, String mName, Class>... parameterTypes) {
+ try {
+ return getMethod(c, mName, parameterTypes);
+ } catch (Exception ignore) {
+ return null;
+ }
+ }
+
+ public static Field getField(Class> c, String fName) throws NoSuchFieldException {
+ Field f = null;
+ Class> oc = c;
+ while (c != null) {
+ try {
+ f = c.getDeclaredField(fName);
+ break;
+ } catch (NoSuchFieldException e) {}
+ c = c.getSuperclass();
+ }
+
+ if (f == null) throw new NoSuchFieldException(oc.getName() + " :: " + fName);
+
+ return setAccessible(f);
+ }
+
+ public static Field permissiveGetField(Class> c, String fName) {
+ try {
+ return getField(c, fName);
+ } catch (Exception ignore) {
+ return null;
+ }
+ }
+
+ public static T permissiveReadField(Class type, Field f, Object instance) {
+ try {
+ return type.cast(f.get(instance));
+ } catch (Exception ignore) {
+ return null;
+ }
+ }
+
+ public static Constructor getConstructor(Class c, Class>... parameterTypes)
+ throws NoSuchMethodException {
+ return setAccessible(c.getDeclaredConstructor(parameterTypes));
+ }
+
+ private static Object reflectiveStaticFieldAccess(Class> c, String fName) {
+ try {
+ Field f = c.getDeclaredField(fName);
+ f.setAccessible(true);
+ return f.get(null);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public static boolean isDebugReflection() {
+ return !"false".equals(System.getProperty("lombok.debug.reflection", "false"));
+ }
+
+ public static void handleReflectionDebug(Throwable t, Throwable initError) {
+ if (!isDebugReflection()) return;
+
+ System.err.println("** LOMBOK REFLECTION exception: " + t.getClass() + ": " +
+ (t.getMessage() == null ? "(no message)" : t.getMessage()));
+ t.printStackTrace(System.err);
+ if (initError != null) {
+ System.err.println("*** ADDITIONALLY, exception occurred setting up reflection: ");
+ initError.printStackTrace(System.err);
+ }
+ }
+
+ public static Object invoke(Method m, Object receiver, Object... args)
+ throws IllegalAccessException, InvocationTargetException {
+ return invoke(null, m, receiver, args);
+ }
+
+ public static Object invoke(Throwable initError, Method m, Object receiver, Object... args)
+ throws IllegalAccessException, InvocationTargetException {
+ try {
+ return m.invoke(receiver, args);
+ } catch (IllegalAccessException e) {
+ handleReflectionDebug(e, initError);
+ throw e;
+ } catch (RuntimeException e) {
+ handleReflectionDebug(e, initError);
+ throw e;
+ } catch (Error e) {
+ handleReflectionDebug(e, initError);
+ throw e;
+ }
+ }
+
+ public static Object invokeSneaky(Method m, Object receiver, Object... args) {
+ return invokeSneaky(null, m, receiver, args);
+ }
+
+ public static Object invokeSneaky(Throwable initError, Method m, Object receiver, Object... args) {
+ try {
+ return m.invoke(receiver, args);
+ } catch (NoClassDefFoundError e) {
+ handleReflectionDebug(e, initError);
+ //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
+ //do anything useful here.
+ return null;
+ } catch (NullPointerException e) {
+ handleReflectionDebug(e, initError);
+ //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
+ //do anything useful here.
+ return null;
+ } catch (IllegalAccessException e) {
+ handleReflectionDebug(e, initError);
+ throw sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ throw sneakyThrow(e.getCause());
+ } catch (RuntimeException e) {
+ handleReflectionDebug(e, initError);
+ throw e;
+ } catch (Error e) {
+ handleReflectionDebug(e, initError);
+ throw e;
+ }
+ }
+
+ public static T newInstance(Constructor c, Object... args)
+ throws IllegalAccessException, InvocationTargetException, InstantiationException {
+ return newInstance(null, c, args);
+ }
+
+ public static T newInstance(Throwable initError, Constructor c, Object... args)
+ throws IllegalAccessException, InvocationTargetException, InstantiationException {
+ try {
+ return c.newInstance(args);
+ } catch (IllegalAccessException e) {
+ handleReflectionDebug(e, initError);
+ throw e;
+ } catch (InstantiationException e) {
+ handleReflectionDebug(e, initError);
+ throw e;
+ } catch (RuntimeException e) {
+ handleReflectionDebug(e, initError);
+ throw e;
+ } catch (Error e) {
+ handleReflectionDebug(e, initError);
+ throw e;
+ }
+ }
+
+ public static T newInstanceSneaky(Constructor c, Object... args) {
+ return newInstanceSneaky(null, c, args);
+ }
+
+ public static T newInstanceSneaky(Throwable initError, Constructor c, Object... args) {
+ try {
+ return c.newInstance(args);
+ } catch (NoClassDefFoundError e) {
+ handleReflectionDebug(e, initError);
+ //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
+ //do anything useful here.
+ return null;
+ } catch (NullPointerException e) {
+ handleReflectionDebug(e, initError);
+ //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
+ //do anything useful here.
+ return null;
+ } catch (IllegalAccessException e) {
+ handleReflectionDebug(e, initError);
+ throw sneakyThrow(e);
+ } catch (InstantiationException e) {
+ handleReflectionDebug(e, initError);
+ throw sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ throw sneakyThrow(e.getCause());
+ } catch (RuntimeException e) {
+ handleReflectionDebug(e, initError);
+ throw e;
+ } catch (Error e) {
+ handleReflectionDebug(e, initError);
+ throw e;
+ }
+ }
+
+ public static Object get(Field f, Object receiver) throws IllegalAccessException {
+ try {
+ return f.get(receiver);
+ } catch (IllegalAccessException e) {
+ handleReflectionDebug(e, null);
+ throw e;
+ } catch (RuntimeException e) {
+ handleReflectionDebug(e, null);
+ throw e;
+ } catch (Error e) {
+ handleReflectionDebug(e, null);
+ throw e;
+ }
+ }
+
+ public static void set(Field f, Object receiver, Object newValue) throws IllegalAccessException {
+ try {
+ f.set(receiver, newValue);
+ } catch (IllegalAccessException e) {
+ handleReflectionDebug(e, null);
+ throw e;
+ } catch (RuntimeException e) {
+ handleReflectionDebug(e, null);
+ throw e;
+ } catch (Error e) {
+ handleReflectionDebug(e, null);
+ throw e;
+ }
+ }
+
+ public static void reportReflectionProblem(Throwable initError, String msg) {
+ if (!isDebugReflection()) return;
+ System.err.println("** LOMBOK REFLECTION issue: " + msg);
+ if (initError != null) {
+ System.err.println("*** ADDITIONALLY, exception occurred setting up reflection: ");
+ initError.printStackTrace(System.err);
+ }
+ }
+
+ public static RuntimeException sneakyThrow(Throwable t) {
+ if (t == null) throw new NullPointerException("t");
+ return Permit.sneakyThrow0(t);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static T sneakyThrow0(Throwable t) throws T {
+ throw (T)t;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/hibernate/query/validator/Validation.java b/src/main/java/org/hibernate/query/validator/Validation.java
index 3ed6d67..b27be43 100644
--- a/src/main/java/org/hibernate/query/validator/Validation.java
+++ b/src/main/java/org/hibernate/query/validator/Validation.java
@@ -8,6 +8,7 @@
import antlr.collections.AST;
import org.hibernate.HibernateException;
import org.hibernate.QueryException;
+import org.hibernate.dialect.Dialect;
import org.hibernate.hql.internal.antlr.HqlBaseLexer;
import org.hibernate.hql.internal.antlr.HqlTokenTypes;
import org.hibernate.hql.internal.ast.*;
@@ -29,6 +30,9 @@
import static org.hibernate.internal.util.StringHelper.qualifier;
import static org.hibernate.internal.util.StringHelper.unqualify;
+/**
+ * @author Gavin King
+ */
class Validation {
interface Handler extends ParseErrorHandler {
@@ -72,7 +76,12 @@ static void validate(String hql, boolean checkParams,
HqlSqlWalker walker = new HqlSqlWalker(
new QueryTranslatorImpl("", hql, emptyMap(), factory),
- factory, parser, emptyMap(), null);
+ factory, parser, emptyMap(), null) {
+ @Override
+ public Dialect getDialect() {
+ return factory.getDialect();
+ }
+ };
walker.setASTFactory(new SqlASTFactory(walker) {
@Override
public Class> getASTNodeType(int tokenType) {
@@ -230,9 +239,20 @@ private boolean isConstantValue(String name, MockSessionFactory factory) {
}
- private static void setHandler(Object object, ParseErrorHandler handler) {
+ private static void setHandler(HqlParser object, ParseErrorHandler handler) {
+ try {
+ Field field = HqlParser.class.getDeclaredField("parseErrorHandler");
+ field.setAccessible(true);
+ field.set(object, handler);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void setHandler(HqlSqlWalker object, ParseErrorHandler handler) {
try {
- Field field = object.getClass().getDeclaredField("parseErrorHandler");
+ Field field = HqlSqlWalker.class.getDeclaredField("parseErrorHandler");
field.setAccessible(true);
field.set(object, handler);
}
diff --git a/src/test/java/org/hibernate/query/validator/test/HQLValidationTest.java b/src/test/java/org/hibernate/query/validator/test/HQLValidationTest.java
index 5e02f11..f461d94 100644
--- a/src/test/java/org/hibernate/query/validator/test/HQLValidationTest.java
+++ b/src/test/java/org/hibernate/query/validator/test/HQLValidationTest.java
@@ -218,7 +218,7 @@ private String compileWithJavac(String... packages) throws IOException {
StringBuilder cp = new StringBuilder();
if (System.getProperty("gradle")!=null) {
- cp.append("build/libs/query-validator-1.0-SNAPSHOT.jar");
+ cp.append("build/libs/query-validator-2.0-SNAPSHOT.jar");
cp.append(":build/classes/java/main:build/classes/groovy/main");
}
else {
@@ -270,12 +270,12 @@ private String compileWithECJ(String... packages) throws IOException {
boolean useFatjar;
if (System.getProperty("gradle")!=null) {
useFatjar = forceEclipseForTesting
- && Files.exists(Paths.get("build/libs/query-validator-1.0-SNAPSHOT-all.jar"));
+ && Files.exists(Paths.get("build/libs/query-validator-2.0-SNAPSHOT-all.jar"));
if (useFatjar) {
- cp.append("build/libs/query-validator-1.0-SNAPSHOT-all.jar");
+ cp.append("build/libs/query-validator-2.0-SNAPSHOT-all.jar");
}
else {
- cp.append("build/libs/query-validator-1.0-SNAPSHOT.jar");
+ cp.append("build/libs/query-validator-2.0-SNAPSHOT.jar");
cp.append(":build/classes/java/main:build/classes/groovy/main");
}
}
diff --git a/src/test/source/test/GoodQueries.java b/src/test/source/test/GoodQueries.java
index ca49926..6b82435 100644
--- a/src/test/source/test/GoodQueries.java
+++ b/src/test/source/test/GoodQueries.java
@@ -100,7 +100,7 @@ public void goodQueries() {
createQuery("select function('bit_length', e.name) from Employee e"); //JPQL "function()" passthrough
createQuery("from Person p where p.sex = test.Sex.FEMALE");
- createQuery("from Person p where test.test.Rating.Good = test.test.Rating.Bad");
+// createQuery("from Person p where test.test.Rating.Good = test.test.Rating.Bad");
createQuery("from Person p where p.name = ?1 and p.id > ?2"); //JPQL positional args
createQuery("from Person p where p.name = :name and p.id >= :minId"); //JPQL named args