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

org.eclipse.persistence.internal.jpa.parsing.ParseTreeContext Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998, 2023 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.jpa.parsing;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.JPQLException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * INTERNAL
 * 

Purpose: The ParseTreeContext holds and manages context information for the parse tree for validation. *

Responsibilities:

    *
  • Associate schema names with variables *
  • Associate identifier with nodes *
  • Answer an alias for a variable name *
  • Answer a class for a variable name *
  • Answer a class loader *
  • Answer true if there is a class for a variable name *
  • Answer a node for a given identifier *
  • Print the context on a string *
* @see ParseTree * @author Jon Driscoll and Joel Lucuik * @since TopLink 4.0 */ public class ParseTreeContext { private Map variableDecls; private String baseVariable; private int currentScope; private Set outerScopeVariables; private Map> fetchJoins; private TypeHelper typeHelper; private Map parameterTypes; private List parameterNames; private NodeFactory nodeFactory; private String queryInfo; /** * INTERNAL * Return a new initialized ParseTreeContext */ public ParseTreeContext(NodeFactory nodeFactory, String queryInfo) { super(); variableDecls = new HashMap<>(); currentScope = 0; fetchJoins = new HashMap<>(); typeHelper = null; parameterTypes = new HashMap<>(); parameterNames = new ArrayList<>(); this.nodeFactory = nodeFactory; this.queryInfo = queryInfo; } /** * INTERNAL * Associate the given schema with the given variable. */ public void registerSchema(String variable, String schema, int line, int column) { VariableDecl decl = variableDecls.get(variable); if (decl == null) { decl = new VariableDecl(variable, schema); variableDecls.put(variable, decl); } else { String text = decl.isRangeVariable ? decl.schema : decl.path.getAsString(); throw JPQLException.multipleVariableDeclaration( getQueryInfo(), line, column, variable, text); } } /** * INTERNAL * Associate the given path with the given variable. */ public void registerJoinVariable(String variable, Node path, int line, int column) { VariableDecl decl = variableDecls.get(variable); if (decl == null) { decl = new VariableDecl(variable, path); variableDecls.put(variable, decl); } else { String text = decl.isRangeVariable ? decl.schema : decl.path.getAsString(); throw JPQLException.multipleVariableDeclaration( getQueryInfo(), line, column, variable, text); } } /** * INTERNAL */ public void unregisterVariable(String variable) { variableDecls.remove(variable); } /** * INTERNAL * Returns true if the specified string denotes a variable. */ public boolean isVariable(String variable) { VariableDecl decl = variableDecls.get(variable); return decl != null; } /** * INTERNAL * Returns true if the specified string denotes a range variable. */ public boolean isRangeVariable(String variable) { VariableDecl decl = variableDecls.get(variable); return (decl != null) && decl.isRangeVariable; } /** * INTERNAL * Returns the abstract schema name if the specified string denotes a * range variable. */ public String schemaForVariable(String variable) { VariableDecl decl = variableDecls.get(variable); return (decl != null) ? decl.schema : null; } /** * INTERNAL * Answer the class associated with the provided schema name */ public Class classForSchemaName(String schemaName, GenerationContext context) { ClassDescriptor descriptor = context.getSession().getDescriptorForAlias(schemaName); if (descriptor == null) { throw JPQLException.entityTypeNotFound(getQueryInfo(), schemaName); } Class theClass = descriptor.getJavaClass(); if (theClass == null) { throw JPQLException.resolutionClassNotFoundException(getQueryInfo(), schemaName); } return theClass; } /** * INTERNAL * getVariableNameForClass(): * Answer the name mapped to the specified class. Answer null if none found. * SELECT OBJECT (emp) FROM Employee emp * getVariableNameForClass(Employee.class) => "emp" */ public String getVariableNameForClass(Class theClass, GenerationContext context) { for (Iterator> i = variableDecls.entrySet().iterator(); i.hasNext(); ) { Map.Entry entry = i.next(); String nextVariable = entry.getKey(); VariableDecl decl = entry.getValue(); if ((decl.schema != null) && (theClass == this.classForSchemaName(decl.schema, context))) { return nextVariable; } } return null; } /** * INTERNAL * Returns the path if the specified string denotes a join or collection * member variable. */ public Node pathForVariable(String variable) { VariableDecl decl = variableDecls.get(variable); return (decl != null) ? decl.path : null; } /** */ public String getBaseVariable() { return baseVariable; } /** */ public void setBaseVariable(String variable) { this.baseVariable = variable; } /** */ public NodeFactory getNodeFactory() { return nodeFactory; } /** */ public String getQueryInfo() { return queryInfo; } /** * INTERNAL * Returns true if the specified string denotes a variable declared in an * outer scope. */ public boolean isDeclaredInOuterScope(String variable) { VariableDecl decl = variableDecls.get(variable); return decl != null && (decl.scope < currentScope); } /** * INTERNAL * Sets the scope of the specified variable to the current scope. */ public void setScopeOfVariable(String variable) { VariableDecl decl = variableDecls.get(variable); if (decl != null) { decl.scope = currentScope; } } /** * INTERNAL * Enters a new scope. This initializes the set of outer scope variables. */ public void enterScope() { currentScope++; resetOuterScopeVariables(); } /** * INTERNAL * Leaves the current scope. */ public void leaveScope() { currentScope--; } /** * INTERNAL * Adds the specified variable to the set of outer scope variables. */ public void registerOuterScopeVariable(String variable) { outerScopeVariables.add(variable); } /** * INTERNAL * Returns the set of outer scope variables. */ public Set getOuterScopeVariables() { return outerScopeVariables; } /** * INTERNAL * Resets the set of outer scope variables. */ public void resetOuterScopeVariables() { outerScopeVariables = new HashSet<>(); } /** * INTERNAL * Resets the set of outer scope variables. */ public void resetOuterScopeVariables(Set variables) { outerScopeVariables = variables; } /** * Associate the given variableName with the given node representing a * JOIN FETCH node. */ public void registerFetchJoin(String variableName, Node node) { List joins = fetchJoins.get(variableName); if (joins == null) { joins = new ArrayList<>(); fetchJoins.put(variableName, joins); } joins.add(node); } /** Returns a list of FETCH JOIN nodes for the specified attached to the * specified variable. */ public List getFetchJoins(String variableName) { return fetchJoins.get(variableName); } /** Mark the specified variable as used if it is declared in the current * scope. */ public void usedVariable(String variable) { VariableDecl decl = variableDecls.get(variable); if ((decl != null) && (decl.scope == currentScope)) { decl.used = true; } } /** Returns s set of variables that are declared in the current scope, * but not used in the query. */ public Set getUnusedVariables() { Set unused = new HashSet<>(); for (Iterator> i = variableDecls.entrySet().iterator(); i.hasNext();) { Map.Entry entry = i.next(); String variable = entry.getKey(); VariableDecl decl = entry.getValue(); if ((decl.scope == currentScope) && !decl.used) { unused.add(variable); } } return unused; } //answer true if two or more variables are mapped to the same type name in variableTypes //true => "SELECT OBJECT (emp1) FROM Employee emp1, Employee emp2 WHERE ..." //false => "SELECT OBJECT (emp) FROM Employee emp WHERE ..." public boolean hasMoreThanOneVariablePerType() { Map typeNamesToVariables = new HashMap<>(); int nrOfRangeVariables = 0; //Map the Aliases to the variable names, then check the count for (Iterator> i = variableDecls.entrySet().iterator(); i.hasNext(); ) { Map.Entry entry = i.next(); String variable = entry.getKey(); VariableDecl decl = entry.getValue(); if (decl.isRangeVariable) { nrOfRangeVariables++; typeNamesToVariables.put(decl.schema, variable); } } return typeNamesToVariables.size() != nrOfRangeVariables; } //answer true if two or more aliases are involved in the FROM (different types) //true => "SELECT OBJECT (emp1) FROM Employee emp1, Address addr1 WHERE ..." //false => "SELECT OBJECT (emp) FROM Employee emp WHERE ..." //false => "SELECT OBJECT (emp1) FROM Employee emp1, Employee emp2 WHERE ..." public boolean hasMoreThanOneAliasInFrom() { Map typeNamesToVariables = new HashMap<>(); for (Iterator> i = variableDecls.entrySet().iterator(); i.hasNext(); ) { Map.Entry entry = i.next(); String variable = entry.getKey(); VariableDecl decl = entry.getValue(); if (decl.isRangeVariable) { typeNamesToVariables.put(decl.schema, variable); } } return typeNamesToVariables.size() > 1; } /** * INTERNAL * Returns the type helper stored in this context. */ public TypeHelper getTypeHelper() { return typeHelper; } /** * INTERNAL * Stores the specified type helper in this context. */ public void setTypeHelper(TypeHelper typeHelper) { this.typeHelper = typeHelper; } /** * INTERNAL * Add a parameter. */ public void addParameter(String parameterName) { if (!parameterNames.contains(parameterName)){ parameterNames.add(parameterName); } } /** * INTERNAL * Defines the type of the parameter with the specified name. */ public void defineParameterType(String parameterName, Object parameterType, int line, int column) { if (parameterTypes.containsKey(parameterName)) { // existing entry Object oldType = parameterTypes.get(parameterName); if (typeHelper.isAssignableFrom(oldType, parameterType)) { // OK } else if (typeHelper.isAssignableFrom(parameterType, oldType)) { // new parameter type is more general parameterTypes.put(parameterName, parameterType); } else { // error case old usage and new usage do not match type throw JPQLException.invalidMultipleUseOfSameParameter( getQueryInfo(), line, column, parameterName, typeHelper.getTypeName(oldType), typeHelper.getTypeName(parameterType)); } } else { // new entry parameterTypes.put(parameterName, parameterType); } } /** * INTERNAL * Returns true if the query has at least one parameter. */ public boolean hasParameters() { return !parameterNames.isEmpty(); } /** * INTERNAL * Return the type of the specified parameter. */ public Object getParameterType(String parameter) { return parameterTypes.get(parameter); } /** * INTERNAL * Return the parameter names. */ public List getParameterNames() { return parameterNames; } /** * INTERNAL * Class defining the type of the values the variableDecls map. * It holds the following values: * variable - the name of the variable * isRangeVariable - true if the variable is declared as range variable * schema - the abstract for a range variable * path - the path for join or collection member variable * scope - the scope of the variable * used - true if the variable is used in any of the clauses */ static class VariableDecl { public final String variable; public final boolean isRangeVariable; public final String schema; public final Node path; public int scope; public boolean used; public VariableDecl(String variable, String schema) { this.variable = variable; this.isRangeVariable = true; this.schema = schema; this.path = null; this.used = false; } public VariableDecl(String variable, Node path) { this.variable = variable; this.isRangeVariable = false; this.schema = null; this.path = path; this.used = false; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy