1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
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 */
25 package org.hibernate.type;
26
27 import java.io.Serializable;
28 import java.math.BigDecimal;
29 import java.math.BigInteger;
30 import java.sql.Blob;
31 import java.sql.Clob;
32 import java.sql.Time;
33 import java.sql.Timestamp;
34 import java.util.Calendar;
35 import java.util.Collections;
36 import java.util.Comparator;
37 import java.util.GregorianCalendar;
38 import java.util.HashMap;
39 import java.util.Locale;
40 import java.util.Map;
41 import java.util.Properties;
42 import java.util.TimeZone;
43
44 import org.hibernate.Hibernate;
45 import org.hibernate.MappingException;
46 import org.hibernate.classic.Lifecycle;
47 import org.hibernate.engine.SessionImplementor;
48 import org.hibernate.intercept.LazyPropertyInitializer;
49 import org.hibernate.property.BackrefPropertyAccessor;
50 import org.hibernate.tuple.StandardProperty;
51 import org.hibernate.usertype.CompositeUserType;
52 import org.hibernate.usertype.UserType;
53 import org.hibernate.usertype.ParameterizedType;
54 import org.hibernate.util.ReflectHelper;
55
56 /**
57 * Used internally to obtain instances of <tt>Type</tt>. Applications should use static methods
58 * and constants on <tt>org.hibernate.Hibernate</tt>.
59 *
60 * @see org.hibernate.Hibernate
61 * @author Gavin King
62 */
63 public final class TypeFactory {
64
65 private static final Map BASIC_TYPES;
66
67 static {
68 HashMap basics = new HashMap();
69 basics.put( boolean.class.getName(), Hibernate.BOOLEAN );
70 basics.put( long.class.getName(), Hibernate.LONG );
71 basics.put( short.class.getName(), Hibernate.SHORT );
72 basics.put( int.class.getName(), Hibernate.INTEGER );
73 basics.put( byte.class.getName(), Hibernate.BYTE );
74 basics.put( float.class.getName(), Hibernate.FLOAT );
75 basics.put( double.class.getName(), Hibernate.DOUBLE );
76 basics.put( char.class.getName(), Hibernate.CHARACTER );
77 basics.put( Hibernate.CHARACTER.getName(), Hibernate.CHARACTER );
78 basics.put( Hibernate.INTEGER.getName(), Hibernate.INTEGER );
79 basics.put( Hibernate.STRING.getName(), Hibernate.STRING );
80 basics.put( Hibernate.DATE.getName(), Hibernate.DATE );
81 basics.put( Hibernate.TIME.getName(), Hibernate.TIME );
82 basics.put( Hibernate.TIMESTAMP.getName(), Hibernate.TIMESTAMP );
83 basics.put( "dbtimestamp", new DbTimestampType() );
84 basics.put( Hibernate.LOCALE.getName(), Hibernate.LOCALE );
85 basics.put( Hibernate.CALENDAR.getName(), Hibernate.CALENDAR );
86 basics.put( Hibernate.CALENDAR_DATE.getName(), Hibernate.CALENDAR_DATE );
87 basics.put( Hibernate.CURRENCY.getName(), Hibernate.CURRENCY );
88 basics.put( Hibernate.TIMEZONE.getName(), Hibernate.TIMEZONE );
89 basics.put( Hibernate.CLASS.getName(), Hibernate.CLASS );
90 basics.put( Hibernate.TRUE_FALSE.getName(), Hibernate.TRUE_FALSE );
91 basics.put( Hibernate.YES_NO.getName(), Hibernate.YES_NO );
92 basics.put( Hibernate.BINARY.getName(), Hibernate.BINARY );
93 basics.put( Hibernate.TEXT.getName(), Hibernate.TEXT );
94 basics.put( Hibernate.BLOB.getName(), Hibernate.BLOB );
95 basics.put( Hibernate.CLOB.getName(), Hibernate.CLOB );
96 basics.put( Hibernate.BIG_DECIMAL.getName(), Hibernate.BIG_DECIMAL );
97 basics.put( Hibernate.BIG_INTEGER.getName(), Hibernate.BIG_INTEGER );
98 basics.put( Hibernate.SERIALIZABLE.getName(), Hibernate.SERIALIZABLE );
99 basics.put( Hibernate.OBJECT.getName(), Hibernate.OBJECT );
100 basics.put( Boolean.class.getName(), Hibernate.BOOLEAN );
101 basics.put( Long.class.getName(), Hibernate.LONG );
102 basics.put( Short.class.getName(), Hibernate.SHORT );
103 basics.put( Integer.class.getName(), Hibernate.INTEGER );
104 basics.put( Byte.class.getName(), Hibernate.BYTE );
105 basics.put( Float.class.getName(), Hibernate.FLOAT );
106 basics.put( Double.class.getName(), Hibernate.DOUBLE );
107 basics.put( Character.class.getName(), Hibernate.CHARACTER );
108 basics.put( String.class.getName(), Hibernate.STRING );
109 basics.put( java.util.Date.class.getName(), Hibernate.TIMESTAMP );
110 basics.put( Time.class.getName(), Hibernate.TIME );
111 basics.put( Timestamp.class.getName(), Hibernate.TIMESTAMP );
112 basics.put( java.sql.Date.class.getName(), Hibernate.DATE );
113 basics.put( BigDecimal.class.getName(), Hibernate.BIG_DECIMAL );
114 basics.put( BigInteger.class.getName(), Hibernate.BIG_INTEGER );
115 basics.put( Locale.class.getName(), Hibernate.LOCALE );
116 basics.put( Calendar.class.getName(), Hibernate.CALENDAR );
117 basics.put( GregorianCalendar.class.getName(), Hibernate.CALENDAR );
118 if ( CurrencyType.CURRENCY_CLASS != null ) {
119 basics.put( CurrencyType.CURRENCY_CLASS.getName(), Hibernate.CURRENCY );
120 }
121 basics.put( TimeZone.class.getName(), Hibernate.TIMEZONE );
122 basics.put( Object.class.getName(), Hibernate.OBJECT );
123 basics.put( Class.class.getName(), Hibernate.CLASS );
124 basics.put( byte[].class.getName(), Hibernate.BINARY );
125 basics.put( "byte[]", Hibernate.BINARY );
126 basics.put( Byte[].class.getName(), Hibernate.WRAPPER_BINARY );
127 basics.put( "Byte[]", Hibernate.WRAPPER_BINARY );
128 basics.put( char[].class.getName(), Hibernate.CHAR_ARRAY );
129 basics.put( "char[]", Hibernate.CHAR_ARRAY );
130 basics.put( Character[].class.getName(), Hibernate.CHARACTER_ARRAY );
131 basics.put( "Character[]", Hibernate.CHARACTER_ARRAY );
132 basics.put( Blob.class.getName(), Hibernate.BLOB );
133 basics.put( Clob.class.getName(), Hibernate.CLOB );
134 basics.put( Serializable.class.getName(), Hibernate.SERIALIZABLE );
135
136 Type type = new AdaptedImmutableType(Hibernate.DATE);
137 basics.put( type.getName(), type );
138 type = new AdaptedImmutableType(Hibernate.TIME);
139 basics.put( type.getName(), type );
140 type = new AdaptedImmutableType(Hibernate.TIMESTAMP);
141 basics.put( type.getName(), type );
142 type = new AdaptedImmutableType( new DbTimestampType() );
143 basics.put( type.getName(), type );
144 type = new AdaptedImmutableType(Hibernate.CALENDAR);
145 basics.put( type.getName(), type );
146 type = new AdaptedImmutableType(Hibernate.CALENDAR_DATE);
147 basics.put( type.getName(), type );
148 type = new AdaptedImmutableType(Hibernate.SERIALIZABLE);
149 basics.put( type.getName(), type );
150 type = new AdaptedImmutableType(Hibernate.BINARY);
151 basics.put( type.getName(), type );
152
153 BASIC_TYPES = Collections.unmodifiableMap( basics );
154 }
155
156 private TypeFactory() {
157 throw new UnsupportedOperationException();
158 }
159
160 /**
161 * A one-to-one association type for the given class
162 */
163 public static EntityType oneToOne(
164 String persistentClass,
165 ForeignKeyDirection foreignKeyType,
166 String uniqueKeyPropertyName,
167 boolean lazy,
168 boolean unwrapProxy,
169 boolean isEmbeddedInXML,
170 String entityName,
171 String propertyName
172 ) {
173 return new OneToOneType(
174 persistentClass,
175 foreignKeyType,
176 uniqueKeyPropertyName,
177 lazy,
178 unwrapProxy,
179 isEmbeddedInXML,
180 entityName,
181 propertyName
182 );
183 }
184
185 /**
186 * A many-to-one association type for the given class
187 */
188 public static EntityType manyToOne(String persistentClass) {
189 return new ManyToOneType( persistentClass );
190 }
191
192 /**
193 * A many-to-one association type for the given class
194 */
195 public static EntityType manyToOne(String persistentClass, boolean lazy) {
196 return new ManyToOneType( persistentClass, lazy );
197 }
198
199 /**
200 * A many-to-one association type for the given class
201 */
202 public static EntityType manyToOne(
203 String persistentClass,
204 String uniqueKeyPropertyName,
205 boolean lazy,
206 boolean unwrapProxy,
207 boolean isEmbeddedInXML,
208 boolean ignoreNotFound
209 ) {
210 return new ManyToOneType(
211 persistentClass,
212 uniqueKeyPropertyName,
213 lazy,
214 unwrapProxy,
215 isEmbeddedInXML,
216 ignoreNotFound
217 );
218 }
219
220 /**
221 * Given the name of a Hibernate basic type, return an instance of
222 * <tt>org.hibernate.type.Type</tt>.
223 */
224 public static Type basic(String name) {
225 return (Type) BASIC_TYPES.get( name );
226 }
227
228 /**
229 * Uses heuristics to deduce a Hibernate type given a string naming the type or Java class.
230 * Return an instance of <tt>org.hibernate.type.Type</tt>.
231 */
232 public static Type heuristicType(String typeName) throws MappingException {
233 return heuristicType( typeName, null );
234 }
235
236 /**
237 * Uses heuristics to deduce a Hibernate type given a string naming the type or Java class.
238 * Return an instance of <tt>org.hibernate.type.Type</tt>.
239 */
240 public static Type heuristicType(String typeName, Properties parameters)
241 throws MappingException {
242 Type type = TypeFactory.basic( typeName );
243 if ( type == null ) {
244 Class typeClass;
245 try {
246 typeClass = ReflectHelper.classForName( typeName );
247 }
248 catch (ClassNotFoundException cnfe) {
249 typeClass = null;
250 }
251 if ( typeClass != null ) {
252 if ( Type.class.isAssignableFrom( typeClass ) ) {
253 try {
254 type = (Type) typeClass.newInstance();
255 }
256 catch (Exception e) {
257 throw new MappingException(
258 "Could not instantiate Type: " + typeClass.getName(),
259 e
260 );
261 }
262 injectParameters(type, parameters);
263 }
264 else if ( CompositeUserType.class.isAssignableFrom( typeClass ) ) {
265 type = new CompositeCustomType( typeClass, parameters );
266 }
267 else if ( UserType.class.isAssignableFrom( typeClass ) ) {
268 type = new CustomType( typeClass, parameters );
269 }
270 else if ( Lifecycle.class.isAssignableFrom( typeClass ) ) {
271 type = Hibernate.entity( typeClass );
272 }
273 else if ( Serializable.class.isAssignableFrom( typeClass ) ) {
274 type = Hibernate.serializable( typeClass );
275 }
276 }
277 }
278 return type;
279
280 }
281
282 /**
283 * The legacy contract.
284 *
285 * @deprecated Use {@link #customCollection(String, java.util.Properties, String, String, boolean)} instead
286 */
287 public static CollectionType customCollection(
288 String typeName,
289 String role,
290 String propertyRef,
291 boolean embedded) {
292 return customCollection( typeName, null, role, propertyRef, embedded );
293 }
294
295 public static CollectionType customCollection(
296 String typeName,
297 Properties typeParameters,
298 String role,
299 String propertyRef,
300 boolean embedded) {
301 Class typeClass;
302 try {
303 typeClass = ReflectHelper.classForName( typeName );
304 }
305 catch ( ClassNotFoundException cnfe ) {
306 throw new MappingException( "user collection type class not found: " + typeName, cnfe );
307 }
308 CustomCollectionType result = new CustomCollectionType( typeClass, role, propertyRef, embedded );
309 if ( typeParameters != null ) {
310 TypeFactory.injectParameters( result.getUserType(), typeParameters );
311 }
312 return result;
313 }
314
315 // Collection Types:
316
317 public static CollectionType array(String role, String propertyRef, boolean embedded,
318 Class elementClass) {
319 return new ArrayType( role, propertyRef, elementClass, embedded );
320 }
321
322 public static CollectionType list(String role, String propertyRef, boolean embedded) {
323 return new ListType( role, propertyRef, embedded );
324 }
325
326 public static CollectionType bag(String role, String propertyRef, boolean embedded) {
327 return new BagType( role, propertyRef, embedded );
328 }
329
330 public static CollectionType idbag(String role, String propertyRef, boolean embedded) {
331 return new IdentifierBagType( role, propertyRef, embedded );
332 }
333
334 public static CollectionType map(String role, String propertyRef, boolean embedded) {
335 return new MapType( role, propertyRef, embedded );
336 }
337
338 public static CollectionType orderedMap(String role, String propertyRef, boolean embedded) {
339 return new OrderedMapType( role, propertyRef, embedded );
340 }
341
342 public static CollectionType set(String role, String propertyRef, boolean embedded) {
343 return new SetType( role, propertyRef, embedded );
344 }
345
346 public static CollectionType orderedSet(String role, String propertyRef, boolean embedded) {
347 return new OrderedSetType( role, propertyRef, embedded );
348 }
349
350 public static CollectionType sortedMap(String role, String propertyRef, boolean embedded,
351 Comparator comparator) {
352 return new SortedMapType( role, propertyRef, comparator, embedded );
353 }
354
355 public static CollectionType sortedSet(String role, String propertyRef, boolean embedded,
356 Comparator comparator) {
357 return new SortedSetType( role, propertyRef, comparator, embedded );
358 }
359
360 public static void injectParameters(Object type, Properties parameters) {
361 if (type instanceof ParameterizedType) {
362 ( (ParameterizedType) type ).setParameterValues(parameters);
363 }
364 else if ( parameters!=null && !parameters.isEmpty() ) {
365 throw new MappingException(
366 "type is not parameterized: " +
367 type.getClass().getName()
368 );
369 }
370 }
371
372
373 // convenience methods relating to operations across arrays of types...
374
375 /**
376 * Deep copy a series of values from one array to another...
377 *
378 * @param values The values to copy (the source)
379 * @param types The value types
380 * @param copy an array indicating which values to include in the copy
381 * @param target The array into which to copy the values
382 * @param session The orginating session
383 */
384 public static void deepCopy(
385 final Object[] values,
386 final Type[] types,
387 final boolean[] copy,
388 final Object[] target,
389 final SessionImplementor session) {
390 for ( int i = 0; i < types.length; i++ ) {
391 if ( copy[i] ) {
392 if ( values[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY
393 || values[i] == BackrefPropertyAccessor.UNKNOWN ) {
394 target[i] = values[i];
395 }
396 else {
397 target[i] = types[i].deepCopy( values[i], session.getEntityMode(), session
398 .getFactory() );
399 }
400 }
401 }
402 }
403
404 /**
405 * Apply the {@link Type#beforeAssemble} operation across a series of values.
406 *
407 * @param row The values
408 * @param types The value types
409 * @param session The orginating session
410 */
411 public static void beforeAssemble(
412 final Serializable[] row,
413 final Type[] types,
414 final SessionImplementor session) {
415 for ( int i = 0; i < types.length; i++ ) {
416 if ( row[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY
417 && row[i] != BackrefPropertyAccessor.UNKNOWN ) {
418 types[i].beforeAssemble( row[i], session );
419 }
420 }
421 }
422
423 /**
424 * Apply the {@link Type#assemble} operation across a series of values.
425 *
426 * @param row The values
427 * @param types The value types
428 * @param session The orginating session
429 * @param owner The entity "owning" the values
430 * @return The assembled state
431 */
432 public static Object[] assemble(
433 final Serializable[] row,
434 final Type[] types,
435 final SessionImplementor session,
436 final Object owner) {
437 Object[] assembled = new Object[row.length];
438 for ( int i = 0; i < types.length; i++ ) {
439 if ( row[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY || row[i] == BackrefPropertyAccessor.UNKNOWN ) {
440 assembled[i] = row[i];
441 }
442 else {
443 assembled[i] = types[i].assemble( row[i], session, owner );
444 }
445 }
446 return assembled;
447 }
448
449 /**
450 * Apply the {@link Type#disassemble} operation across a series of values.
451 *
452 * @param row The values
453 * @param types The value types
454 * @param nonCacheable An array indicating which values to include in the disassemled state
455 * @param session The orginating session
456 * @param owner The entity "owning" the values
457 * @return The disassembled state
458 */
459 public static Serializable[] disassemble(
460 final Object[] row,
461 final Type[] types,
462 final boolean[] nonCacheable,
463 final SessionImplementor session,
464 final Object owner) {
465 Serializable[] disassembled = new Serializable[row.length];
466 for ( int i = 0; i < row.length; i++ ) {
467 if ( nonCacheable!=null && nonCacheable[i] ) {
468 disassembled[i] = LazyPropertyInitializer.UNFETCHED_PROPERTY;
469 }
470 else if ( row[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY || row[i] == BackrefPropertyAccessor.UNKNOWN ) {
471 disassembled[i] = (Serializable) row[i];
472 }
473 else {
474 disassembled[i] = types[i].disassemble( row[i], session, owner );
475 }
476 }
477 return disassembled;
478 }
479
480 /**
481 * Apply the {@link Type#replace} operation across a series of values.
482 *
483 * @param original The source of the state
484 * @param target The target into which to replace the source values.
485 * @param types The value types
486 * @param session The orginating session
487 * @param owner The entity "owning" the values
488 * @param copyCache A map representing a cache of already replaced state
489 * @return The replaced state
490 */
491 public static Object[] replace(
492 final Object[] original,
493 final Object[] target,
494 final Type[] types,
495 final SessionImplementor session,
496 final Object owner,
497 final Map copyCache) {
498 Object[] copied = new Object[original.length];
499 for ( int i = 0; i < types.length; i++ ) {
500 if ( original[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY
501 || original[i] == BackrefPropertyAccessor.UNKNOWN ) {
502 copied[i] = target[i];
503 }
504 else {
505 copied[i] = types[i].replace( original[i], target[i], session, owner, copyCache );
506 }
507 }
508 return copied;
509 }
510
511 /**
512 * Apply the {@link Type#replace} operation across a series of values.
513 *
514 * @param original The source of the state
515 * @param target The target into which to replace the source values.
516 * @param types The value types
517 * @param session The orginating session
518 * @param owner The entity "owning" the values
519 * @param copyCache A map representing a cache of already replaced state
520 * @param foreignKeyDirection FK directionality to be applied to the replacement
521 * @return The replaced state
522 */
523 public static Object[] replace(
524 final Object[] original,
525 final Object[] target,
526 final Type[] types,
527 final SessionImplementor session,
528 final Object owner,
529 final Map copyCache,
530 final ForeignKeyDirection foreignKeyDirection) {
531 Object[] copied = new Object[original.length];
532 for ( int i = 0; i < types.length; i++ ) {
533 if ( original[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY
534 || original[i] == BackrefPropertyAccessor.UNKNOWN ) {
535 copied[i] = target[i];
536 }
537 else {
538 copied[i] = types[i].replace( original[i], target[i], session, owner, copyCache, foreignKeyDirection );
539 }
540 }
541 return copied;
542 }
543
544 /**
545 * Apply the {@link Type#replace} operation across a series of values, as
546 * long as the corresponding {@link Type} is an association.
547 * <p/>
548 * If the corresponding type is a component type, then apply {@link #replaceAssociations}
549 * accross the component subtypes but do not replace the component value itself.
550 *
551 * @param original The source of the state
552 * @param target The target into which to replace the source values.
553 * @param types The value types
554 * @param session The orginating session
555 * @param owner The entity "owning" the values
556 * @param copyCache A map representing a cache of already replaced state
557 * @param foreignKeyDirection FK directionality to be applied to the replacement
558 * @return The replaced state
559 */
560 public static Object[] replaceAssociations(
561 final Object[] original,
562 final Object[] target,
563 final Type[] types,
564 final SessionImplementor session,
565 final Object owner,
566 final Map copyCache,
567 final ForeignKeyDirection foreignKeyDirection) {
568 Object[] copied = new Object[original.length];
569 for ( int i = 0; i < types.length; i++ ) {
570 if ( original[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY
571 || original[i] == BackrefPropertyAccessor.UNKNOWN ) {
572 copied[i] = target[i];
573 }
574 else if ( types[i].isComponentType() ) {
575 // need to extract the component values and check for subtype replacements...
576 AbstractComponentType componentType = ( AbstractComponentType ) types[i];
577 Type[] subtypes = componentType.getSubtypes();
578 Object[] origComponentValues = original[i] == null ? new Object[subtypes.length] : componentType.getPropertyValues( original[i], session );
579 Object[] targetComponentValues = target[i] == null ? new Object[subtypes.length] : componentType.getPropertyValues( target[i], session );
580 replaceAssociations( origComponentValues, targetComponentValues, subtypes, session, null, copyCache, foreignKeyDirection );
581 copied[i] = target[i];
582 }
583 else if ( !types[i].isAssociationType() ) {
584 copied[i] = target[i];
585 }
586 else {
587 copied[i] = types[i].replace( original[i], target[i], session, owner, copyCache, foreignKeyDirection );
588 }
589 }
590 return copied;
591 }
592
593 /**
594 * Determine if any of the given field values are dirty, returning an array containing
595 * indices of the dirty fields.
596 * <p/>
597 * If it is determined that no fields are dirty, null is returned.
598 *
599 * @param properties The property definitions
600 * @param currentState The current state of the entity
601 * @param previousState The baseline state of the entity
602 * @param includeColumns Columns to be included in the dirty checking, per property
603 * @param anyUninitializedProperties Does the entity currently hold any uninitialized property values?
604 * @param session The session from which the dirty check request originated.
605 * @return Array containing indices of the dirty properties, or null if no properties considered dirty.
606 */
607 public static int[] findDirty(
608 final StandardProperty[] properties,
609 final Object[] currentState,
610 final Object[] previousState,
611 final boolean[][] includeColumns,
612 final boolean anyUninitializedProperties,
613 final SessionImplementor session) {
614 int[] results = null;
615 int count = 0;
616 int span = properties.length;
617
618 for ( int i = 0; i < span; i++ ) {
619 final boolean dirty = currentState[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY
620 && properties[i].isDirtyCheckable( anyUninitializedProperties )
621 && properties[i].getType().isDirty( previousState[i], currentState[i], includeColumns[i], session );
622 if ( dirty ) {
623 if ( results == null ) {
624 results = new int[span];
625 }
626 results[count++] = i;
627 }
628 }
629
630 if ( count == 0 ) {
631 return null;
632 }
633 else {
634 int[] trimmed = new int[count];
635 System.arraycopy( results, 0, trimmed, 0, count );
636 return trimmed;
637 }
638 }
639
640 /**
641 * Determine if any of the given field values are modified, returning an array containing
642 * indices of the modified fields.
643 * <p/>
644 * If it is determined that no fields are dirty, null is returned.
645 *
646 * @param properties The property definitions
647 * @param currentState The current state of the entity
648 * @param previousState The baseline state of the entity
649 * @param includeColumns Columns to be included in the mod checking, per property
650 * @param anyUninitializedProperties Does the entity currently hold any uninitialized property values?
651 * @param session The session from which the dirty check request originated.
652 * @return Array containing indices of the modified properties, or null if no properties considered modified.
653 */
654 public static int[] findModified(
655 final StandardProperty[] properties,
656 final Object[] currentState,
657 final Object[] previousState,
658 final boolean[][] includeColumns,
659 final boolean anyUninitializedProperties,
660 final SessionImplementor session) {
661 int[] results = null;
662 int count = 0;
663 int span = properties.length;
664
665 for ( int i = 0; i < span; i++ ) {
666 final boolean modified = currentState[i]!=LazyPropertyInitializer.UNFETCHED_PROPERTY
667 && properties[i].isDirtyCheckable(anyUninitializedProperties)
668 && properties[i].getType().isModified( previousState[i], currentState[i], includeColumns[i], session );
669
670 if ( modified ) {
671 if ( results == null ) {
672 results = new int[span];
673 }
674 results[count++] = i;
675 }
676 }
677
678 if ( count == 0 ) {
679 return null;
680 }
681 else {
682 int[] trimmed = new int[count];
683 System.arraycopy( results, 0, trimmed, 0, count );
684 return trimmed;
685 }
686 }
687
688 }