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

org.hibernate.boot.model.source.internal.hbm.RelationalValueSourceHelper Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.boot.model.source.internal.hbm;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.hibernate.boot.MappingException;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmColumnType;
import org.hibernate.boot.model.TruthValue;
import org.hibernate.boot.model.source.spi.ColumnSource;
import org.hibernate.boot.model.source.spi.DerivedValueSource;
import org.hibernate.boot.model.source.spi.RelationalValueSource;
import org.hibernate.boot.model.source.spi.SizeSource;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;

/**
 * @author Steve Ebersole
 */
public class RelationalValueSourceHelper {

	/**
	 * Internal unifying contract used in creating {@link org.hibernate.mapping.Column}
	 * and {@link org.hibernate.mapping.Formula} instances.  It adapts the variances across
	 * the different mappings which contribute column/formula information.  For example,
	 * consider the {@code } mapping which might have:
    *
  • a {@code column} XML attribute
  • *
  • a {@code formula} XML attribute
  • *
  • one or more nested {@code } XML elements
  • *
  • a nested {@code } XML element
  • *
* as opposed to a {@code } mapping, which can only have:
    *
  • a {@code column} XML attribute
  • *
*/ public interface ColumnsAndFormulasSource { /** * What kind of XML element does this information come from? * * @return The source XML element type */ XmlElementMetadata getSourceType(); /** * The name of the source. May be {@code null} if none was specified. Will be treated * as {@code null} if the nature says it cannot be named. * * @return The name of the source. * * @see XmlElementMetadata#canBeNamed() */ String getSourceName(); /** * Access to any formula defined via XML attribute. * * @return formula, if one, as defined via XML attribute. */ String getFormulaAttribute(); /** * Access to any nested {@code } or {@code } XML elements. * * @return columns or formulas defined via nested XML elements. */ List getColumnOrFormulaElements(); /** * Access to any column defined via XML attribute. * * @return column, if one, as defined via XML attribute. */ String getColumnAttribute(); SizeSource getSizeSource(); Boolean isNullable(); boolean isUnique(); Set getIndexConstraintNames(); Set getUniqueKeyConstraintNames(); } public abstract static class AbstractColumnsAndFormulasSource implements ColumnsAndFormulasSource { @Override public String getFormulaAttribute() { return null; } @Override public String getColumnAttribute() { return null; } @Override public List getColumnOrFormulaElements() { return Collections.emptyList(); } @Override public SizeSource getSizeSource() { return null; } @Override public Boolean isNullable() { return null; } @Override public Set getIndexConstraintNames() { return Collections.emptySet(); } @Override public boolean isUnique() { return false; } @Override public Set getUniqueKeyConstraintNames() { return Collections.emptySet(); } } /** * Given a {@link ColumnsAndFormulasSource}, build a single {@link RelationalValueSource}. * More than one {@link RelationalValueSource} will result in an exception. * * @param mappingDocument the mapping document * @param containingTableName The logical name of the table containing the relational values * @param columnsAndFormulasSource the adapter describing the value sources. * * @return The single RelationalValueSource. */ public static RelationalValueSource buildValueSource( MappingDocument mappingDocument, String containingTableName, RelationalValueSourceHelper.ColumnsAndFormulasSource columnsAndFormulasSource) { final List sources = buildValueSources( mappingDocument, containingTableName, columnsAndFormulasSource ); if ( sources.size() > 1 ) { final String errorMessage; if ( columnsAndFormulasSource.getSourceType().canBeNamed() && StringHelper.isNotEmpty( columnsAndFormulasSource.getSourceName() ) ) { errorMessage = String.format( Locale.ENGLISH, "Expecting just a single formula/column in context of <%s name=\"%s\"/>", columnsAndFormulasSource.getSourceType().getElementName(), columnsAndFormulasSource.getSourceName() ); } else { errorMessage = String.format( Locale.ENGLISH, "Expecting just a single formula/column in context of <%s/>", columnsAndFormulasSource.getSourceType().getElementName() ); } throw new MappingException( errorMessage, mappingDocument.getOrigin() ); } return sources.get( 0 ); } /** * Given a {@link ColumnsAndFormulasSource}, build a single {@link RelationalValueSource} * which is required to be a column. More than one {@link RelationalValueSource} will result * in an exception. A formula, rather than a column, will result in an exception. * * @param mappingDocument the mapping document * @param containingTableName The logical name of the table containing the relational values * @param columnsAndFormulasSource the adapter describing the value sources. * * @return The single ColumnSource. */ public static ColumnSource buildColumnSource( MappingDocument mappingDocument, String containingTableName, RelationalValueSourceHelper.ColumnsAndFormulasSource columnsAndFormulasSource) { final List sources = buildValueSources( mappingDocument, containingTableName, columnsAndFormulasSource ); if ( sources.size() > 1 ) { final String errorMessage; if ( columnsAndFormulasSource.getSourceType().canBeNamed() && StringHelper.isNotEmpty( columnsAndFormulasSource.getSourceName() ) ) { errorMessage = String.format( Locale.ENGLISH, "Expecting just a single formula/column in context of <%s name=\"%s\"/>", columnsAndFormulasSource.getSourceType().getElementName(), columnsAndFormulasSource.getSourceName() ); } else { errorMessage = String.format( Locale.ENGLISH, "Expecting just a single formula/column in context of <%s/>", columnsAndFormulasSource.getSourceType().getElementName() ); } throw new MappingException( errorMessage, mappingDocument.getOrigin() ); } final RelationalValueSource result = sources.get( 0 ); if ( !ColumnSource.class.isInstance( result ) ) { final String errorMessage; if ( columnsAndFormulasSource.getSourceType().canBeNamed() && StringHelper.isNotEmpty( columnsAndFormulasSource.getSourceName() ) ) { errorMessage = String.format( Locale.ENGLISH, "Expecting single column in context of <%s name=\"%s\"/>, but found formula [%s]", columnsAndFormulasSource.getSourceType().getElementName(), columnsAndFormulasSource.getSourceName(), ( (DerivedValueSource) result ).getExpression() ); } else { errorMessage = String.format( Locale.ENGLISH, "Expecting single column in context of <%s/>, but found formula [%s]", columnsAndFormulasSource.getSourceType().getElementName(), ( (DerivedValueSource) result ).getExpression() ); } throw new MappingException( errorMessage, mappingDocument.getOrigin() ); } return (ColumnSource) result; } /** * Given a {@link ColumnsAndFormulasSource}, build the corresponding list of * {@link ColumnSource}. Any formula, rather than a column, will result in an exception. * * @param mappingDocument the mapping document * @param containingTableName The logical name of the table containing the relational values * @param columnsAndFormulasSource the adapter describing the value sources. * * @return The corresponding list. */ public static List buildColumnSources( MappingDocument mappingDocument, String containingTableName, RelationalValueSourceHelper.ColumnsAndFormulasSource columnsAndFormulasSource) { final List sources = buildValueSources( mappingDocument, containingTableName, columnsAndFormulasSource ); final List columnSources = CollectionHelper.arrayList( sources.size() ); for ( RelationalValueSource source : sources ) { if ( !ColumnSource.class.isInstance( source ) ) { final String errorMessage; if ( columnsAndFormulasSource.getSourceType().canBeNamed() && StringHelper.isNotEmpty( columnsAndFormulasSource.getSourceName() ) ) { errorMessage = String.format( Locale.ENGLISH, "Expecting only columns in context of <%s name=\"%s\"/>, but found formula [%s]", columnsAndFormulasSource.getSourceType().getElementName(), columnsAndFormulasSource.getSourceName(), ( (DerivedValueSource) source ).getExpression() ); } else { errorMessage = String.format( Locale.ENGLISH, "Expecting only columns in context of <%s/>, but found formula [%s]", columnsAndFormulasSource.getSourceType().getElementName(), ( (DerivedValueSource) source ).getExpression() ); } throw new MappingException( errorMessage, mappingDocument.getOrigin() ); } columnSources.add( (ColumnSource) source ); } return columnSources; } /** * Given a {@link ColumnsAndFormulasSource}, build the corresponding list of * {@link RelationalValueSource} * * @param mappingDocument the mapping document * @param containingTableName The logical name of the table containing the relational values * @param columnsAndFormulasSource the adapter describing the value sources. * * @return The corresponding list. */ public static List buildValueSources( MappingDocument mappingDocument, String containingTableName, RelationalValueSourceHelper.ColumnsAndFormulasSource columnsAndFormulasSource) { List result = new ArrayList(); if ( StringHelper.isNotEmpty( columnsAndFormulasSource.getFormulaAttribute() ) ) { // we have an explicit formula attribute (i.e., ) validateUseOfFormulaAttribute( mappingDocument, columnsAndFormulasSource ); result.add( new FormulaImpl( mappingDocument, containingTableName, columnsAndFormulasSource.getFormulaAttribute() ) ); } else if ( CollectionHelper.isNotEmpty( columnsAndFormulasSource.getColumnOrFormulaElements() ) ) { validateUseOfColumnOrFormulaNestedElements( mappingDocument, columnsAndFormulasSource ); for ( Object selectable : columnsAndFormulasSource.getColumnOrFormulaElements() ) { if ( selectable instanceof JaxbHbmColumnType ) { final JaxbHbmColumnType columnElement = (JaxbHbmColumnType) selectable; result.add( new ColumnSourceImpl( mappingDocument, containingTableName, columnElement, columnsAndFormulasSource.getIndexConstraintNames(), columnsAndFormulasSource.getUniqueKeyConstraintNames() ) ); } else if ( selectable instanceof String ) { result.add( new FormulaImpl( mappingDocument, containingTableName, (String) selectable ) ); } else { throw new MappingException( "Unexpected column/formula JAXB type : " + selectable.getClass().getName(), mappingDocument.getOrigin() ); } } } else { // we have either an explicitly named column via the column attribute, or an implicit // column reference. Aside from applying an implicit naming strategy (or not), these 2 // case are handled the exact same way result.add( new ColumnAttributeSourceImpl( mappingDocument, containingTableName, columnsAndFormulasSource.getColumnAttribute(), columnsAndFormulasSource.getSizeSource(), interpretNullabilityToTruthValue( columnsAndFormulasSource.isNullable() ), columnsAndFormulasSource.isUnique() ? TruthValue.TRUE : TruthValue.FALSE, columnsAndFormulasSource.getIndexConstraintNames(), columnsAndFormulasSource.getUniqueKeyConstraintNames() ) ); } return result; } private static TruthValue interpretNullabilityToTruthValue(Boolean nullable) { if ( nullable == null ) { return TruthValue.UNKNOWN; } else { return nullable ? TruthValue.TRUE : TruthValue.FALSE; } } private static void validateUseOfFormulaAttribute( MappingDocument sourceDocument, RelationalValueSourceHelper.ColumnsAndFormulasSource columnsAndFormulasSource) { // 1) make sure there is no column attribute if ( StringHelper.isNotEmpty( columnsAndFormulasSource.getColumnAttribute() ) ) { final String errorMessage; if ( columnsAndFormulasSource.getSourceType().canBeNamed() && StringHelper.isNotEmpty( columnsAndFormulasSource.getSourceName() ) ) { errorMessage = String.format( Locale.ENGLISH, "column attribute and formula attribute may not be specified together near <%s name=\"%s\" column=\"%s\" formula=\"%s\" />", columnsAndFormulasSource.getSourceType().getElementName(), columnsAndFormulasSource.getSourceName(), columnsAndFormulasSource.getColumnAttribute(), columnsAndFormulasSource.getFormulaAttribute() ); } else { errorMessage = String.format( Locale.ENGLISH, "column attribute and formula attribute may not be specified together near <%s column=\"%s\" formula=\"%s\" />", columnsAndFormulasSource.getSourceType().getElementName(), columnsAndFormulasSource.getColumnAttribute(), columnsAndFormulasSource.getFormulaAttribute() ); } throw new MappingException( errorMessage, sourceDocument.getOrigin() ); } // 2) and no column/formula nested elements if ( CollectionHelper.isNotEmpty( columnsAndFormulasSource.getColumnOrFormulaElements() ) ) { final String errorMessage; if ( columnsAndFormulasSource.getSourceType().canBeNamed() && StringHelper.isNotEmpty( columnsAndFormulasSource.getSourceName() ) ) { errorMessage = String.format( Locale.ENGLISH, "formula attribute may not be specified along with or subelement(s) near <%s name=\"%s\" formula=\"%s\" />", columnsAndFormulasSource.getSourceType().getElementName(), columnsAndFormulasSource.getSourceName(), columnsAndFormulasSource.getFormulaAttribute() ); } else { errorMessage = String.format( Locale.ENGLISH, "formula attribute may not be specified along with or subelement(s) near <%s formula=\"%s\" />", columnsAndFormulasSource.getSourceType().getElementName(), columnsAndFormulasSource.getFormulaAttribute() ); } throw new MappingException( errorMessage, sourceDocument.getOrigin() ); } } private static void validateUseOfColumnOrFormulaNestedElements( MappingDocument sourceDocument, RelationalValueSourceHelper.ColumnsAndFormulasSource columnsAndFormulasSource) { if ( StringHelper.isNotEmpty( columnsAndFormulasSource.getColumnAttribute() ) ) { final String errorMessage; if ( columnsAndFormulasSource.getSourceType().canBeNamed() && StringHelper.isNotEmpty( columnsAndFormulasSource.getSourceName() ) ) { errorMessage = String.format( Locale.ENGLISH, "column attribute may not be specified along with or subelement(s) near <%s name=\"%s\" column=\"%s\" />", columnsAndFormulasSource.getSourceType().getElementName(), columnsAndFormulasSource.getSourceName(), columnsAndFormulasSource.getColumnAttribute() ); } else { errorMessage = String.format( Locale.ENGLISH, "column attribute may not be specified along with or subelement(s) near <%s column=\"%s\" />", columnsAndFormulasSource.getSourceType().getElementName(), columnsAndFormulasSource.getColumnAttribute() ); } throw new MappingException( errorMessage, sourceDocument.getOrigin() ); } } private static void validateCustomWriteFragment( MappingDocument sourceDocument, RelationalValueSourceHelper.ColumnsAndFormulasSource columnsAndFormulasSource, JaxbHbmColumnType columnMapping, String customWrite) { if ( customWrite != null && !customWrite.matches("[^?]*\\?[^?]*") ) { final String errorMessage; if ( columnsAndFormulasSource.getSourceType().canBeNamed() && StringHelper.isNotEmpty( columnsAndFormulasSource.getSourceName() ) ) { errorMessage = String.format( Locale.ENGLISH, "write expression must contain exactly one value placeholder ('?') character near for <%s name=\"%s\" />", columnMapping.getName(), customWrite, columnsAndFormulasSource.getSourceType().getElementName(), columnsAndFormulasSource.getSourceName() ); } else { errorMessage = String.format( Locale.ENGLISH, "write expression must contain exactly one value placeholder ('?') character near for <%s />", columnMapping.getName(), customWrite, columnsAndFormulasSource.getSourceType().getElementName() ); } throw new MappingException( errorMessage, sourceDocument.getOrigin() ); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy