| /* |
| * Copyright (c) 1997, 2018 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. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the |
| * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| * version 2 with the GNU Classpath Exception, which is available at |
| * https://www.gnu.org/software/classpath/license.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| */ |
| |
| package com.sun.jdo.spi.persistence.generator.database; |
| |
| |
| import com.sun.jdo.spi.persistence.utility.StringHelper; |
| |
| import com.sun.jdo.spi.persistence.utility.logging.Logger; |
| |
| /** |
| * Represents how a JDBC type (i.e., one defined by java.sql.Types) is |
| * created in the database. |
| */ |
| class JDBCInfo { |
| |
| // |
| // These constants are used to locate properties indicating values for |
| // the fields in instances of JDBCInfo. |
| // |
| // |
| |
| /** Indicator that property designates the type of a mapped SQL type. */ |
| private static final String INDICATOR_TYPE = |
| DatabaseGenerationConstants.INDICATOR_JDBC_TYPE; |
| |
| /** Indicator that property designates nullability of mapped SQL type. */ |
| private static final String INDICATOR_NULLABLE = |
| DatabaseGenerationConstants.INDICATOR_JDBC_NULLABLE; |
| |
| /** Indicator that property designates the precision of mapped SQL type. */ |
| private static final String INDICATOR_PRECISION = |
| DatabaseGenerationConstants.INDICATOR_JDBC_PRECISION; |
| |
| /** Indicator that property designates the scale of mapped SQL type. */ |
| private static final String INDICATOR_SCALE = |
| DatabaseGenerationConstants.INDICATOR_JDBC_SCALE; |
| |
| /** Indicator that property designates length of a mapped SQL type. */ |
| private static final String INDICATOR_LENGTH = |
| DatabaseGenerationConstants.INDICATOR_JDBC_LENGTH; |
| |
| /** Indicator that a type does not have a length associated with it. */ |
| private static final String NO_LENGTH_INDICATOR = "null"; |
| |
| /** Flag value which indicates that a JDBCInfo does not have a length. */ |
| private static final Integer NO_LENGTH = new Integer(-1); |
| |
| /** Logger for warning & error messages */ |
| private static final Logger logger = |
| LogHelperDatabaseGenerator.getLogger(); |
| |
| /** Value from java.sql.Types. */ |
| private int jdbcType; |
| |
| /** True iff a column of this type is nullable; default is false. */ |
| private boolean nullable = false; |
| |
| /** Indicates precision of a fixed-point number column; default is null. */ |
| private Integer precision = null; |
| |
| /** Indicates scale of a fixed-point number column; default is null. */ |
| private Integer scale = null; |
| |
| /** Indicates length of a char, etc. column; default is null. */ |
| private Integer length = null; |
| |
| |
| // |
| // Allow determining if which fields have been assigned values. |
| // |
| |
| /** Indicates which fields in this instance have been set. */ |
| private byte fieldsWithValues = 0; |
| |
| /** Mask to indicate whether or not {@link #jdbcType} has a value. */ |
| private static final byte MASK_JDBC_TYPE = 1 << 0; |
| |
| /** Mask to indicate whether or not {@link #nullable} has a value. */ |
| private static final byte MASK_NULLABLE = 1 << 1; |
| |
| /** Mask to indicate whether or not {@link #precision} has a value. */ |
| private static final byte MASK_PRECISION = 1 << 2; |
| |
| /** Mask to indicate whether or not {@link #scale} has a value. */ |
| private static final byte MASK_SCALE = 1 << 3; |
| |
| /** Mask to indicate whether or not {@link #length} has a value. */ |
| private static final byte MASK_LENGTH = 1 << 4; |
| |
| /** Mask to access all field flags at once. */ |
| private static final byte MASK_ALL = MASK_JDBC_TYPE | MASK_NULLABLE |
| | MASK_PRECISION | MASK_SCALE | MASK_LENGTH; |
| |
| |
| /** |
| * Constructor which initializes all fields. |
| * @param jdbcType See {@link jdbcType}. |
| * @param precision See {@link precision}. |
| * @param scale See {@link scale}. |
| * @param length See {@link length}. |
| * @param nullable See {@link nullable}. |
| */ |
| JDBCInfo(int jdbcType, Integer precision, Integer scale, |
| Integer length, boolean nullable) { |
| |
| this.jdbcType = jdbcType; |
| this.precision = precision; |
| this.scale = scale; |
| this.length = length; |
| this.nullable = nullable; |
| |
| fieldsWithValues = MASK_ALL; |
| } |
| |
| /** |
| * Use this constructor in conjunction with multiple setValue to |
| * initialize an instance. |
| */ |
| JDBCInfo() { } |
| |
| |
| /** |
| * Sets the value of one field of this JDBCInfo. |
| * @param indicator Determines which field is set. |
| * @param value String form of the new value of a field. Must not be |
| * null. Empty String means to reset the field value to its default, |
| * except for jdbcType: That field has no default, so given an empty |
| * String the value of jdbcType is unchanged. |
| * @throws IllegalJDBCTypeException if <code>indicator</code> shows that |
| * we are setting a JDBC Type and <code>value</code> is not |
| * recognized as being a valid member of java.sql.Types. |
| */ |
| void setValue(String value, String indicator) |
| throws IllegalJDBCTypeException { |
| |
| if (indicator.equals(INDICATOR_TYPE)) { |
| if (!StringHelper.isEmpty(value)) { |
| Integer type = MappingPolicy.getJdbcType(value); |
| if (null == type) { |
| throw new IllegalJDBCTypeException(); |
| } |
| this.jdbcType = type.intValue(); |
| this.fieldsWithValues |= MASK_JDBC_TYPE; |
| } |
| |
| } else if (indicator.equals(INDICATOR_NULLABLE)) { |
| if (StringHelper.isEmpty(value)) { |
| this.nullable = false; // default |
| } else { |
| this.nullable = Boolean.valueOf(value).booleanValue(); |
| } |
| this.fieldsWithValues |= MASK_NULLABLE; |
| |
| } else if (indicator.equals(INDICATOR_PRECISION)) { |
| this.precision = getIntegerValue(value); |
| this.fieldsWithValues |= MASK_PRECISION; |
| |
| } else if (indicator.equals(INDICATOR_SCALE)) { |
| this.scale = getIntegerValue(value); |
| this.fieldsWithValues |= MASK_SCALE; |
| |
| } else if (indicator.equals(INDICATOR_LENGTH)) { |
| if (value.trim().equals(NO_LENGTH_INDICATOR)) { |
| this.length = NO_LENGTH; |
| } else { |
| this.length = getIntegerValue(value); |
| } |
| this.fieldsWithValues |= MASK_LENGTH; |
| } |
| } |
| |
| /** |
| * @param s String whose Integer value is sought. |
| * @return the value of s as an Integer, or null if s is empty. |
| */ |
| private Integer getIntegerValue(String s) { |
| Integer rc = null; |
| if (!StringHelper.isEmpty(s)) { |
| rc = new Integer(s); |
| } |
| return rc; |
| } |
| |
| // |
| // A note about "completeness". |
| // JDBCInfo instances are created one of 2 ways: By loading a .properties |
| // file for a database, or by a user override. In the first case, the |
| // JDBCInfo will have values for all relevant fields, because that is the |
| // way we have created the .properties files. ("relevant" here means |
| // that, e.g., a length value will be present if required for a field |
| // type for which length is relevant such as VARCHAR.) |
| // |
| // In the second case, a user override might provide only one overriden |
| // value, let's say length. So for a particular field name, we may know |
| // only that it should have a length; of course we need more. So before |
| // allowing access to a JDBCInfo that was created for a field, complete() |
| // it with a JDBCInfo that was created for that field's type. |
| // |
| // See MappingPolicy.JDBCInfo(). |
| // |
| |
| |
| /** |
| * Fill in values for fields based on values in another JDBCInfo |
| * instance. Only those fields for which this instance does not already |
| * have a value are changed. |
| * @param other Another instance of JDBCInfo that has values which are |
| * used to set as-yet-unset values in this instance. |
| */ |
| // XXX For precision and scale, this is not entirely correct. |
| // We should check if this.<val> is set. If so, it must not be greater |
| // than other.<val>. In other words, the other instance's value |
| // overrules the value in this instance, because the other value was |
| // provided in the dbvendor-specific .properties file, which users must |
| // not override. If this instance (i.e., the user override's instance) |
| // specifies an invalid override, we should log a warning, warn the user, |
| // and use the other.<val>. |
| void complete(JDBCInfo other) { |
| if (logger.isLoggable(Logger.FINEST)) { |
| logger.finest("Entering JDBCInfo.complete: " // NOI18N |
| + "\nthis: " + this // NOI18N |
| + "\nother: " + other); // NOI18N |
| } |
| if (MASK_ALL != fieldsWithValues) { |
| if ((fieldsWithValues & MASK_JDBC_TYPE) == 0) { |
| this.jdbcType = other.jdbcType; |
| } |
| |
| if ((fieldsWithValues & MASK_NULLABLE) == 0) { |
| this.nullable = other.nullable; |
| } |
| |
| if ((fieldsWithValues & MASK_PRECISION) == 0) { |
| this.precision = other.precision; |
| } |
| |
| if ((fieldsWithValues & MASK_SCALE) == 0) { |
| this.scale = other.scale; |
| } |
| |
| if ((fieldsWithValues & MASK_LENGTH) == 0 |
| || (NO_LENGTH.equals(other.length)) |
| || (other.length.intValue() < this.length.intValue())) { |
| this.length = other.length; |
| } |
| |
| fieldsWithValues = MASK_ALL; |
| } |
| if (logger.isLoggable(Logger.FINEST)) { |
| logger.finest("Leaving JDBCInfo.complete: " // NOI18N |
| + "\nthis: " + this); // NOI18N |
| } |
| } |
| |
| /** |
| * @return <code>true</code> if this instance has been assigned values |
| * for all fields. |
| */ |
| boolean isComplete() { |
| return fieldsWithValues == MASK_ALL; |
| } |
| |
| /** |
| * Update this JDBCInfo with information from the other JDBCInfo. |
| * @param other The JDBCInfo with values that will overwrite values in |
| * this JDBCInfo. |
| */ |
| void override(JDBCInfo other) { |
| if (null != other) { |
| this.jdbcType = other.jdbcType; |
| } |
| } |
| |
| /** |
| * @return true iff this instance has a value for jdbcType |
| */ |
| public boolean hasJdbcType() { |
| return (fieldsWithValues & MASK_JDBC_TYPE) == 1; |
| } |
| |
| /** |
| * @return The JDBC corresponding to this JDBCInfo. See |
| * {@link java.sql.Types}. |
| */ |
| public int getJdbcType() { |
| return jdbcType; |
| } |
| |
| /** |
| * @return <code>true</code> of columns based on this JDBCInfo should be |
| * nullable. |
| */ |
| public boolean getNullable() { |
| return nullable; |
| } |
| |
| /** |
| * @return The precision of of columns based on this JDBCInfo. |
| */ |
| public Integer getPrecision() { |
| return precision; |
| } |
| |
| /** |
| * @return The scale of of columns based on this JDBCInfo. |
| */ |
| public Integer getScale() { |
| return scale; |
| } |
| |
| /** |
| * @return The length of of columns based on this JDBCInfo, or null if |
| * this JDBCInfo does not need a length (e.g. CLOB on Oracle). |
| */ |
| public Integer getLength() { |
| return NO_LENGTH.equals(length) ? null : length; |
| } |
| |
| /** |
| * Debugging support. |
| * @return A String with the value of each field. |
| */ |
| public String toString() { |
| return "JDBCInfo:" // NOI18N |
| + " jdbcType=" + jdbcType // NOI18N |
| + " nullable=" + nullable // NOI18N |
| + " precision=" + precision // NOI18N |
| + " scale=" + scale // NOI18N |
| + " length=" + length // NOI18N |
| + " fieldsWithValues=0x" + Integer.toHexString(fieldsWithValues); // NOI18N |
| } |
| |
| /** |
| * Used to indicate that a given JDBC Type name is not recognized. |
| */ |
| static class IllegalJDBCTypeException extends Exception { |
| IllegalJDBCTypeException() { } |
| } |
| } |