1   /*
2    * Hibernate, Relational Persistence for Idiomatic Java
3    *
4    * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
5    * indicated by the @author tags or express copyright attribution
6    * statements applied by the authors.  All third-party contributions are
7    * distributed under license by Red Hat Inc.
8    *
9    * This copyrighted material is made available to anyone wishing to use, modify,
10   * copy, or redistribute it subject to the terms and conditions of the GNU
11   * Lesser General Public License, as published by the Free Software Foundation.
12   *
13   * This program is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15   * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
16   * for more details.
17   *
18   * You should have received a copy of the GNU Lesser General Public License
19   * along with this distribution; if not, write to:
20   * Free Software Foundation, Inc.
21   * 51 Franklin Street, Fifth Floor
22   * Boston, MA  02110-1301  USA
23   */
24  package org.hibernate.cfg;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.Map;
28  
29  import org.hibernate.AnnotationException;
30  import org.hibernate.AssertionFailure;
31  import org.hibernate.MappingException;
32  import org.hibernate.mapping.Column;
33  import org.hibernate.mapping.Component;
34  import org.hibernate.mapping.PersistentClass;
35  import org.hibernate.mapping.Property;
36  import org.hibernate.mapping.SimpleValue;
37  
38  /**
39   * @author Emmanuel Bernard
40   */
41  public class CopyIdentifierComponentSecondPass implements SecondPass {
42  	private final String referencedEntityName;
43  	private final Component component;
44  	private final Mappings mappings;
45  	private final Ejb3JoinColumn[] joinColumns;
46  
47  	public CopyIdentifierComponentSecondPass(
48  			Component comp,
49  			String referencedEntityName,
50  			Ejb3JoinColumn[] joinColumns,
51  			Mappings mappings) {
52  		this.component = comp;
53  		this.referencedEntityName = referencedEntityName;
54  		this.mappings = mappings;
55  		this.joinColumns = joinColumns;
56  	}
57  
58  	@SuppressWarnings({ "unchecked" })
59  	public void doSecondPass(Map persistentClasses) throws MappingException {
60  		PersistentClass referencedPersistentClass = (PersistentClass) persistentClasses.get( referencedEntityName );
61  		// TODO better error names
62  		if ( referencedPersistentClass == null ) {
63  			throw new AnnotationException( "Unknown entity name: " + referencedEntityName );
64  		}
65  		if ( ! ( referencedPersistentClass.getIdentifier() instanceof Component ) ) {
66  			throw new AssertionFailure(
67  					"Unexpected identifier type on the referenced entity when mapping a @MapsId: "
68  							+ referencedEntityName
69  			);
70  		}
71  		Component referencedComponent = (Component) referencedPersistentClass.getIdentifier();
72  		Iterator<Property> properties = referencedComponent.getPropertyIterator();
73  
74  
75  		//prepare column name structure
76  		boolean isExplicitReference = true;
77  		Map<String, Ejb3JoinColumn> columnByReferencedName = new HashMap<String, Ejb3JoinColumn>(joinColumns.length);
78  		for (Ejb3JoinColumn joinColumn : joinColumns) {
79  			final String referencedColumnName = joinColumn.getReferencedColumn();
80  			if ( referencedColumnName == null || BinderHelper.isEmptyAnnotationValue( referencedColumnName ) ) {
81  				break;
82  			}
83  			//JPA 2 requires referencedColumnNames to be case insensitive
84  			columnByReferencedName.put( referencedColumnName.toLowerCase(), joinColumn );
85  		}
86  		//try default column orientation
87  		int index = 0;
88  		if ( columnByReferencedName.isEmpty() ) {
89  			isExplicitReference = false;
90  			for (Ejb3JoinColumn joinColumn : joinColumns) {
91  				columnByReferencedName.put( "" + index, joinColumn );
92  				index++;
93  			}
94  			index = 0;
95  		}
96  
97  		while ( properties.hasNext() ) {
98  			Property referencedProperty = properties.next();
99  			if ( referencedProperty.isComposite() ) {
100 				throw new AssertionFailure( "Unexpected nested component on the referenced entity when mapping a @MapsId: "
101 						+ referencedEntityName);
102 			}
103 			else {
104 				Property property = new Property();
105 				property.setName( referencedProperty.getName() );
106 				property.setNodeName( referencedProperty.getNodeName() );
107 				//FIXME set optional?
108 				//property.setOptional( property.isOptional() );
109 				property.setPersistentClass( component.getOwner() );
110 				property.setPropertyAccessorName( referencedProperty.getPropertyAccessorName() );
111 				SimpleValue value = new SimpleValue( mappings, component.getTable() );
112 				property.setValue( value );
113 				final SimpleValue referencedValue = (SimpleValue) referencedProperty.getValue();
114 				value.setTypeName( referencedValue.getTypeName() );
115 				value.setTypeParameters( referencedValue.getTypeParameters() );
116 				final Iterator<Column> columns = referencedValue.getColumnIterator();
117 
118 				if ( joinColumns[0].isNameDeferred() ) {
119 					joinColumns[0].copyReferencedStructureAndCreateDefaultJoinColumns(
120 						referencedPersistentClass,
121 						columns,
122 						value);
123 				}
124 				else {
125 					//FIXME take care of Formula
126 					while ( columns.hasNext() ) {
127 						Column column = columns.next();
128 						final Ejb3JoinColumn joinColumn;
129 						String logicalColumnName = null;
130 						if ( isExplicitReference ) {
131 							final String columnName = column.getName();
132 							logicalColumnName = mappings.getLogicalColumnName( columnName, referencedPersistentClass.getTable() );
133 							//JPA 2 requires referencedColumnNames to be case insensitive
134 							joinColumn = columnByReferencedName.get( logicalColumnName.toLowerCase() );
135 						}
136 						else {
137 							joinColumn = columnByReferencedName.get( "" + index );
138 							index++;
139 						}
140 						if ( joinColumn == null && ! joinColumns[0].isNameDeferred() ) {
141 							throw new AnnotationException(
142 									isExplicitReference ?
143 											"Unable to find column reference in the @MapsId mapping: " + logicalColumnName :
144 											"Implicit column reference in the @MapsId mapping fails, try to use explicit referenceColumnNames: " + referencedEntityName
145 							);
146 						}
147 						final String columnName = joinColumn == null || joinColumn.isNameDeferred() ? "tata_" + column.getName() : joinColumn
148 								.getName();
149 						value.addColumn( new Column( columnName ) );
150 						column.setValue( value );
151 					}
152 				}
153 				component.addProperty( property );
154 			}
155 		}
156 	}
157 }