KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > hql > ast > tree > IntoClause


1 // $Id: IntoClause.java,v 1.1 2005/07/12 20:27:16 steveebersole Exp $
2
package org.hibernate.hql.ast.tree;
3
4 import java.util.ArrayList JavaDoc;
5 import java.util.List JavaDoc;
6
7 import org.hibernate.QueryException;
8 import org.hibernate.persister.entity.Queryable;
9 import org.hibernate.type.Type;
10 import org.hibernate.util.ArrayHelper;
11
12 import antlr.collections.AST;
13
14 /**
15  * Represents an entity referenced in the INTO clause of an HQL
16  * INSERT statement.
17  *
18  * @author Steve Ebersole
19  */

20 public class IntoClause extends HqlSqlWalkerNode implements DisplayableNode {
21
22     private Queryable persister;
23     private String JavaDoc columnSpec = "";
24     private Type[] types;
25
26     private boolean discriminated;
27     private boolean explicitIdInsertion;
28     private boolean explicitVersionInsertion;
29
30
31     public void initialize(Queryable persister) {
32         if ( persister.isAbstract() ) {
33             throw new QueryException( "cannot insert into abstract class (no table)" );
34         }
35         this.persister = persister;
36         initializeColumns();
37
38         if ( getWalker().getSessionFactoryHelper().hasPhysicalDiscriminatorColumn( persister ) ) {
39             discriminated = true;
40             columnSpec += ", " + persister.getDiscriminatorColumnName();
41         }
42
43         resetText();
44     }
45
46     private void resetText() {
47         setText( "into " + getTableName() + " ( " + columnSpec + " )" );
48     }
49
50     public String JavaDoc getTableName() {
51         return persister.getSubclassTableName( 0 );
52     }
53
54     public Queryable getQueryable() {
55         return persister;
56     }
57
58     public String JavaDoc getEntityName() {
59         return persister.getEntityName();
60     }
61
62     public Type[] getInsertionTypes() {
63         return types;
64     }
65
66     public boolean isDiscriminated() {
67         return discriminated;
68     }
69
70     public boolean isExplicitIdInsertion() {
71         return explicitIdInsertion;
72     }
73
74     public boolean isExplicitVersionInsertion() {
75         return explicitVersionInsertion;
76     }
77
78     public void prependIdColumnSpec() {
79         columnSpec = persister.getIdentifierColumnNames()[0] + ", " + columnSpec;
80         resetText();
81     }
82
83     public void prependVersionColumnSpec() {
84         columnSpec = persister.getPropertyColumnNames( persister.getVersionProperty() )[0] + ", " + columnSpec;
85         resetText();
86     }
87
88     public void validateTypes(SelectClause selectClause) throws QueryException {
89         Type[] selectTypes = selectClause.getQueryReturnTypes();
90         if ( selectTypes.length != types.length ) {
91             throw new QueryException( "number of select types did not match those for insert" );
92         }
93
94         for ( int i = 0; i < types.length; i++ ) {
95             // we may need to come up with a notion of "assignable" here instead of
96
// direct comparison of the types. Thinking of situations like
97
// DateType and TimestampType where both correspond to the same
98
// column data type. Maybe something like Dialect.areEquivalent(),
99
// or Type.isEquavalent(). The dialect approach is better for things like
100
// the given scenario, but would not really handle custom types very well.
101
if ( !types[i].equals( selectTypes[i] ) ) {
102                 throw new QueryException(
103                         "insertion type [" + types[i] + "] did not match selection type [" +
104                         selectTypes[i] + "] at position " + i
105                 );
106             }
107         }
108
109         // otherwise, everything ok.
110
}
111
112     /**
113      * Returns additional display text for the AST node.
114      *
115      * @return String - The additional display text.
116      */

117     public String JavaDoc getDisplayText() {
118         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
119         buf.append( "IntoClause{" );
120         buf.append( "entityName=" ).append( getEntityName() );
121         buf.append( ",tableName=" ).append( getTableName() );
122         buf.append( ",columns={" ).append( columnSpec ).append( "}" );
123         buf.append( "}" );
124         return buf.toString();
125     }
126
127     private void initializeColumns() {
128         AST propertySpec = getFirstChild();
129         List JavaDoc types = new ArrayList JavaDoc();
130         visitPropertySpecNodes( propertySpec.getFirstChild(), types );
131         this.types = ArrayHelper.toTypeArray( types );
132         columnSpec = columnSpec.substring( 0, columnSpec.length() - 2 );
133     }
134
135     private void visitPropertySpecNodes(AST propertyNode, List JavaDoc types) {
136         if ( propertyNode == null ) {
137             return;
138         }
139         // TODO : we really need to be able to deal with component paths here also;
140
// this is difficult because the hql-sql grammar expects all those node types
141
// to be FromReferenceNodes. One potential fix here would be to convert the
142
// IntoClause to just use a FromClause/FromElement combo (as a child of the
143
// InsertStatement) and move all this logic into the InsertStatement. That's
144
// probably the easiest approach (read: least amount of changes to the grammar
145
// and code), but just doesn't feel right as then an insert would contain
146
// 2 from-clauses
147
String JavaDoc name = propertyNode.getText();
148         if ( isSuperclassProperty( name ) ) {
149             throw new QueryException( "INSERT statements cannot refer to superclass/joined properties [" + name + "]" );
150         }
151
152         if ( name.equals( persister.getIdentifierPropertyName() ) ) {
153             explicitIdInsertion = true;
154         }
155
156         if ( persister.isVersioned() ) {
157             if ( name.equals( persister.getPropertyNames()[ persister.getVersionProperty() ] ) ) {
158                 explicitVersionInsertion = true;
159             }
160         }
161
162         String JavaDoc[] columnNames = persister.toColumns( name );
163         renderColumns( columnNames );
164         types.add( persister.toType( name ) );
165
166         // visit width-first, then depth
167
visitPropertySpecNodes( propertyNode.getNextSibling(), types );
168         visitPropertySpecNodes( propertyNode.getFirstChild(), types );
169     }
170
171     private void renderColumns(String JavaDoc[] columnNames) {
172         for ( int i = 0; i < columnNames.length; i++ ) {
173             columnSpec += columnNames[i] + ", ";
174         }
175     }
176
177     private boolean isSuperclassProperty(String JavaDoc propertyName) {
178         // really there are two situations where it should be ok to allow the insertion
179
// into properties defined on a superclass:
180
// 1) union-subclass with an abstract root entity
181
// 2) discrim-subclass
182
//
183
// #1 is handled already because of the fact that
184
// UnionSubclassPersister alreay always returns 0
185
// for this call...
186
//
187
// we may want to disallow it for discrim-subclass just for
188
// consistency-sake (currently does not work anyway)...
189
return persister.getSubclassPropertyTableNumber( propertyName ) != 0;
190     }
191 }
192
Popular Tags