| /* |
| * Copyright (c) 1998, 2021 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 |
| // tware - added handling of database delimiters |
| // 11/19/2012-2.5 Guy Pelletier |
| // - 389090: JPA 2.1 DDL Generation Support (foreign key metadata support) |
| // 11/22/2012-2.5 Guy Pelletier |
| // - 389090: JPA 2.1 DDL Generation Support (index metadata support) |
| // 12/07/2012-2.5 Guy Pelletier |
| // - 389090: JPA 2.1 DDL Generation Support (foreign key metadata support) |
| package org.eclipse.persistence.internal.helper; |
| |
| import java.io.*; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.persistence.internal.core.helper.CoreTable; |
| import org.eclipse.persistence.internal.databaseaccess.*; |
| import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter; |
| import org.eclipse.persistence.tools.schemaframework.ForeignKeyConstraint; |
| import org.eclipse.persistence.tools.schemaframework.IndexDefinition; |
| |
| /** |
| * INTERNAL: |
| * <p> <b>Purpose</b>: |
| * Define a fully qualified table name.<p> |
| * <b>Responsibilities</b>: <ul> |
| * <li> Allow specification of a qualifier to the table, i.e. creator or database. |
| * </ul> |
| *@see DatabaseField |
| */ |
| public class DatabaseTable implements CoreTable, Cloneable, Serializable { |
| protected String name; |
| protected String tableQualifier; |
| protected String qualifiedName; |
| |
| /** JPA 2.1 Foreign key specification data */ |
| protected Map<String, ForeignKeyConstraint> foreignKeyConstraints; |
| |
| /** |
| * Contains the user specified unique constraints. JPA 2.0 introduced |
| * the name element, therefore, if specified we will use that name |
| * to create the constraint. Constraints with no name will be added to the |
| * map under the null key and generated with a default name. |
| * Therefore, when a name is given the list size should only ever be |
| * 1. We will validate. The null key could have multiples however they will |
| * have their names defaulted (as we did before). |
| */ |
| protected Map<String, List<List<String>>> uniqueConstraints; |
| |
| /** |
| * Store the set of indexes defined through meta-data for the table. |
| */ |
| protected List<IndexDefinition> indexes; |
| |
| protected boolean useDelimiters = false; |
| |
| protected String creationSuffix; |
| |
| /** |
| * Initialize the newly allocated instance of this class. |
| * By default their is no qualifier. |
| */ |
| public DatabaseTable() { |
| name = ""; |
| tableQualifier = ""; |
| } |
| |
| public DatabaseTable(String possiblyQualifiedName) { |
| this(possiblyQualifiedName, null, null); |
| } |
| |
| public DatabaseTable(String possiblyQualifiedName, String startDelimiter, String endDelimiter) { |
| setPossiblyQualifiedName(possiblyQualifiedName, startDelimiter, endDelimiter); |
| } |
| |
| public DatabaseTable(String tableName, String qualifier) { |
| this(tableName, qualifier, false, null, null); |
| } |
| |
| public DatabaseTable(String tableName, String qualifier, boolean useDelimiters, String startDelimiter, String endDelimiter) { |
| setName(tableName, startDelimiter, endDelimiter); |
| this.tableQualifier = qualifier; |
| this.useDelimiters = useDelimiters; |
| } |
| |
| public void addForeignKeyConstraint(ForeignKeyConstraint foreignKeyConstraint) { |
| if (foreignKeyConstraints == null) { |
| foreignKeyConstraints = new HashMap<>(); |
| } |
| |
| foreignKeyConstraints.put(foreignKeyConstraint.getName(), foreignKeyConstraint); |
| } |
| |
| /** |
| * Add an index definition to this table. |
| */ |
| public void addIndex(IndexDefinition index) { |
| getIndexes().add(index); |
| } |
| |
| /** |
| * Add the unique constraint for the columns names. Used for DDL generation. |
| * For now we just add all the unique constraints as we would have before |
| * when we didn't have a name. |
| */ |
| public void addUniqueConstraints(String name, List<String> columnNames) { |
| if (getUniqueConstraints().containsKey(name)) { |
| getUniqueConstraints().get(name).add(columnNames); |
| } else { |
| List<List<String>> value = new ArrayList<>(); |
| value.add(columnNames); |
| getUniqueConstraints().put(name, value); |
| } |
| } |
| |
| /** |
| * Return a shallow copy of the receiver. |
| */ |
| @Override |
| public DatabaseTable clone() { |
| try { |
| return (DatabaseTable)super.clone(); |
| } catch (CloneNotSupportedException exception) { |
| throw new InternalError(exception.getMessage()); |
| } |
| } |
| |
| /** |
| * Two tables are equal if their names and tables are equal, |
| * or their names are equal and one does not have a qualifier assigned. |
| * This allows an unqualified table to equal the same fully qualified one. |
| */ |
| @Override |
| public boolean equals(Object object) { |
| if (object instanceof DatabaseTable) { |
| return equals((DatabaseTable)object); |
| } |
| return false; |
| } |
| |
| /** |
| * Two tables are equal if their names and tables are equal, |
| * or their names are equal and one does not have a qualifier assigned. |
| * This allows an unqualified table to equal the same fully qualified one. |
| */ |
| public boolean equals(DatabaseTable table) { |
| if (this == table) { |
| return true; |
| } |
| if (table == null) { |
| return false; |
| } |
| if (DatabasePlatform.shouldIgnoreCaseOnFieldComparisons) { |
| if (this.name.equalsIgnoreCase(table.name)) { |
| if ((this.tableQualifier.length() == 0) || (table.tableQualifier.length() == 0) || (this.tableQualifier.equalsIgnoreCase(table.tableQualifier))) { |
| return true; |
| } |
| } |
| } else { |
| if (this.name.equals(table.name)) { |
| if ((this.tableQualifier.length() == 0) || (table.tableQualifier.length() == 0) || (this.tableQualifier.equals(table.tableQualifier))) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * returns the suffix applied to the CREATE table statement on this field for DDL generation. |
| */ |
| public String getCreationSuffix() { |
| return creationSuffix; |
| } |
| |
| public ForeignKeyConstraint getForeignKeyConstraint(String name) { |
| return foreignKeyConstraints.get(name); |
| } |
| |
| public Map<String, ForeignKeyConstraint> getForeignKeyConstraints() { |
| return foreignKeyConstraints; |
| } |
| |
| /** |
| * Return a list of index definitions. |
| * Used for DDL generation. |
| */ |
| public List<IndexDefinition> getIndexes() { |
| if (this.indexes == null) { |
| this.indexes = new ArrayList<>(); |
| } |
| return this.indexes; |
| } |
| |
| /** |
| * Get method for table name. |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * Get method for table name. |
| */ |
| public String getNameDelimited(DatasourcePlatform platform) { |
| if (useDelimiters){ |
| return platform.getStartDelimiter() + name + platform.getEndDelimiter(); |
| } |
| return name; |
| } |
| |
| public String getQualifiedName() { |
| if (qualifiedName == null) { |
| if (tableQualifier.equals("")) { |
| qualifiedName = getName(); |
| } else { |
| qualifiedName = getTableQualifier() + "." + getName(); |
| } |
| } |
| |
| return qualifiedName; |
| } |
| |
| public String getQualifiedNameDelimited(DatasourcePlatform platform) { |
| if (tableQualifier.equals("")) { |
| if (useDelimiters){ |
| return platform.getStartDelimiter() + getName() + platform.getEndDelimiter(); |
| } else { |
| return getName(); |
| } |
| } else { |
| if (useDelimiters){ |
| return platform.getStartDelimiter() + getTableQualifier() + platform.getEndDelimiter() + "." |
| + platform.getStartDelimiter() + getName() + platform.getEndDelimiter(); |
| } else { |
| return getTableQualifier() + "." + getName(); |
| } |
| } |
| } |
| |
| /** |
| * Print the table's SQL from clause. |
| */ |
| public void printSQL(ExpressionSQLPrinter printer) throws IOException { |
| printer.getWriter().write(getQualifiedNameDelimited(printer.getPlatform())); |
| } |
| |
| public String getTableQualifierDelimited(DatasourcePlatform platform) { |
| if (useDelimiters && tableQualifier != null && !tableQualifier.equals("")){ |
| return platform.getStartDelimiter() + tableQualifier + platform.getEndDelimiter(); |
| } |
| return tableQualifier; |
| } |
| |
| public String getTableQualifier() { |
| return tableQualifier; |
| } |
| |
| public boolean hasUniqueConstraints() { |
| return (this.uniqueConstraints != null) && (!this.uniqueConstraints.isEmpty()); |
| } |
| |
| public boolean hasForeignKeyConstraints() { |
| return foreignKeyConstraints != null; |
| } |
| |
| /** |
| * Return the hashcode of the name, because it is fairly unique. |
| */ |
| @Override |
| public int hashCode() { |
| return getName().hashCode(); |
| } |
| |
| public boolean hasIndexes() { |
| return (this.indexes != null) && (!this.indexes.isEmpty()); |
| } |
| |
| /** |
| * Return a list of the unique constraints for this table. |
| * Used for DDL generation. |
| */ |
| public Map<String, List<List<String>>> getUniqueConstraints() { |
| if (this.uniqueConstraints == null) { |
| this.uniqueConstraints = new HashMap<>(); |
| } |
| return this.uniqueConstraints; |
| } |
| |
| /** |
| * Determine whether the receiver has any identification information. |
| * Return true if the name or qualifier of the receiver are nonempty. |
| */ |
| public boolean hasName() { |
| if ((getName().length() == 0) && (getTableQualifier().length() == 0)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * INTERNAL: |
| * Is this decorated / has an AS OF (some past time) clause. |
| * <b>Example:</b> |
| * SELECT ... FROM EMPLOYEE AS OF TIMESTAMP (exp) t0 ... |
| */ |
| public boolean isDecorated() { |
| return false; |
| } |
| |
| protected void resetQualifiedName() { |
| this.qualifiedName = null; |
| } |
| |
| public void setCreationSuffix(String creationSuffix) { |
| this.creationSuffix = creationSuffix; |
| } |
| |
| /** |
| * Set the table name. |
| * Used when aliasing table names. |
| */ |
| public void setName(String name) { |
| setName(name, null, null); |
| } |
| |
| /** |
| * Set the table name. |
| * Used when aliasing table names. |
| * |
| * If the name contains database delimiters, they will be stripped and a flag will be set to have them |
| * added when the DatabaseTable is written to SQL |
| * |
| */ |
| public void setName(String name, String startDelimiter, String endDelimiter) { |
| if (name != null && (startDelimiter != null) && (endDelimiter != null) && !startDelimiter.equals("")&& !endDelimiter.equals("") && name.startsWith(startDelimiter) && name.endsWith(endDelimiter)){ |
| this.name = name.substring(startDelimiter.length(), name.length() - endDelimiter.length()); |
| useDelimiters = true; |
| } else { |
| this.name = name ; |
| } |
| resetQualifiedName(); |
| } |
| |
| /** |
| * Used to map the project xml. Any time a string name is read from the |
| * project xml, we must check if it is fully qualified and split the |
| * actual name from the qualifier. |
| * |
| */ |
| public void setPossiblyQualifiedName(String possiblyQualifiedName) { |
| setPossiblyQualifiedName(possiblyQualifiedName, null, null); |
| } |
| |
| public void setPossiblyQualifiedName(String possiblyQualifiedName, String startDelimiter, String endDelimiter) { |
| resetQualifiedName(); |
| |
| int index = possiblyQualifiedName.lastIndexOf('.'); |
| |
| if (index == -1) { |
| setName(possiblyQualifiedName, startDelimiter, endDelimiter); |
| this.tableQualifier = ""; |
| } else { |
| setName(possiblyQualifiedName.substring(index + 1, possiblyQualifiedName.length()), startDelimiter, endDelimiter); |
| setTableQualifier(possiblyQualifiedName.substring(0, index), startDelimiter, endDelimiter); |
| |
| if((startDelimiter != null) && possiblyQualifiedName.startsWith(startDelimiter) && (endDelimiter != null) && possiblyQualifiedName.endsWith(endDelimiter)) { |
| // It's 'Qualifier.Name' - it should be treated as a single string. |
| // Would that be 'Qualifier'.'Name' both setName and setTableQualifier methods would have set useDelimeters to true. |
| if(!this.useDelimiters) { |
| setName(possiblyQualifiedName); |
| this.tableQualifier = ""; |
| } |
| } |
| } |
| } |
| |
| public void setTableQualifier(String qualifier) { |
| setTableQualifier(qualifier, null, null); |
| } |
| |
| public void setTableQualifier(String qualifier, String startDelimiter, String endDelimiter) { |
| if ((startDelimiter != null) && (endDelimiter != null) && !startDelimiter.equals("")&& !endDelimiter.equals("") && qualifier.startsWith(startDelimiter) && qualifier.endsWith(endDelimiter)){ |
| this.tableQualifier = qualifier.substring(startDelimiter.length(), qualifier.length() - endDelimiter.length()); |
| useDelimiters = true; |
| } else { |
| this.tableQualifier = qualifier; |
| } |
| resetQualifiedName(); |
| } |
| |
| @Override |
| public String toString() { |
| return "DatabaseTable(" + getQualifiedName() + ")"; |
| } |
| |
| public void setUseDelimiters(boolean useDelimiters) { |
| this.useDelimiters = useDelimiters; |
| } |
| |
| public boolean shouldUseDelimiters() { |
| return useDelimiters; |
| } |
| } |