1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.meta;
20
21 import java.io.Externalizable;
22 import java.io.IOException;
23 import java.io.ObjectInput;
24 import java.io.ObjectOutput;
25 import java.io.Serializable;
26 import java.lang.reflect.Constructor;
27 import java.lang.reflect.Field;
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.Member;
30 import java.lang.reflect.Method;
31 import java.lang.reflect.Modifier;
32 import java.security.AccessController;
33 import java.security.PrivilegedActionException;
34 import java.util.ArrayList;
35 import java.util.Calendar;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.Comparator;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.Iterator;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Properties;
45 import java.util.Set;
46 import java.util.TimeZone;
47
48 import org.apache.commons.collections.comparators.ComparatorChain;
49 import org.apache.commons.lang.StringUtils;
50 import org.apache.openjpa.conf.OpenJPAConfiguration;
51 import org.apache.openjpa.kernel.OpenJPAStateManager;
52 import org.apache.openjpa.kernel.StoreContext;
53 import org.apache.openjpa.lib.conf.Configurations;
54 import org.apache.openjpa.lib.log.Log;
55 import org.apache.openjpa.lib.util.J2DoPrivHelper;
56 import org.apache.openjpa.lib.util.JavaVersions;
57 import org.apache.openjpa.lib.util.Localizer;
58 import org.apache.openjpa.lib.util.Options;
59 import org.apache.openjpa.lib.xml.Commentable;
60 import org.apache.openjpa.util.Exceptions;
61 import org.apache.openjpa.util.InternalException;
62 import org.apache.openjpa.util.MetaDataException;
63 import org.apache.openjpa.util.OpenJPAException;
64 import org.apache.openjpa.util.UnsupportedException;
65 import org.apache.openjpa.util.ImplHelper;
66 import org.apache.openjpa.util.UserException;
67
68 import serp.util.Strings;
69
70 /**
71 * Metadata for a managed class field.
72 *
73 * @author Abe White
74 */
75 public class FieldMetaData
76 extends Extensions
77 implements ValueMetaData, MetaDataContext, MetaDataModes, Commentable {
78
79 /**
80 * Constant specifying that no null-value was given.
81 */
82 public static final int NULL_UNSET = -1;
83
84 /**
85 * Constant specifying to use a datastore null to persist null values
86 * in object fields.
87 */
88 public static final int NULL_NONE = 0;
89
90 /**
91 * Constant specifying to use a datastore default value to persist null
92 * values in object fields.
93 */
94 public static final int NULL_DEFAULT = 1;
95
96 /**
97 * Constant specifying to throw an exception when attempting to persist
98 * null values in object fields.
99 */
100 public static final int NULL_EXCEPTION = 2;
101
102 /**
103 * Constant specifying the management level of a field.
104 */
105 public static final int MANAGE_PERSISTENT = 3;
106
107 /**
108 * Constant specifying the management level of a field.
109 */
110 public static final int MANAGE_TRANSACTIONAL = 1;
111
112 /**
113 * Constant specifying the management level of a field.
114 */
115 public static final int MANAGE_NONE = 0;
116
117 private static final Localizer _loc = Localizer.forPackage
118 (FieldMetaData.class);
119
120 private static final int DFG_FALSE = 1;
121 private static final int DFG_TRUE = 2;
122 private static final int DFG_EXPLICIT = 4;
123
124 private static final Method DEFAULT_METHOD;
125 static {
126 try {
127 DEFAULT_METHOD = Object.class.getMethod("wait", (Class[]) null);
128 } catch (Exception e) {
129 // shouldn't ever happen
130 throw new InternalException(e);
131 }
132 }
133
134 // name and type
135 private final ValueMetaData _val;
136 private final ValueMetaData _key;
137 private final ValueMetaData _elem;
138 private final ClassMetaData _owner;
139 private final String _name;
140 private Class _dec = null;
141 private ClassMetaData _decMeta = null;
142 private String _fullName = null;
143 private String _embedFullName = null;
144 private int _resMode = MODE_NONE;
145
146 // load/store info
147 private String[] _comments = null;
148 private int _listIndex = -1;
149
150 ////////////////////////////////////////////////////////////////////
151 // Note: if you add additional state, make sure to add it to copy()
152 ////////////////////////////////////////////////////////////////////
153
154 // misc info
155 private Class _proxyClass = null;
156 private Object _initializer = null;
157 private boolean _transient = false;
158 private boolean _primKey = false;
159 private Boolean _version = null;
160 private int _nullValue = NULL_UNSET;
161 private int _manage = MANAGE_PERSISTENT;
162 private int _index = -1;
163 private int _decIndex = -1;
164 private int _pkIndex = -1;
165 private boolean _explicit = false;
166 private int _dfg = 0;
167 private Set _fgSet = null;
168 private String[] _fgs = null;
169 private String _lfg = null;
170 private Boolean _lrs = null;
171 private Boolean _stream = null;
172 private String _extName = null;
173 private String _factName = null;
174 private String _extString = null;
175 private Map _extValues = Collections.EMPTY_MAP;
176 private Map _fieldValues = Collections.EMPTY_MAP;
177 private Boolean _enumField = null;
178 private Boolean _lobField = null;
179 private Boolean _serializableField = null;
180 private boolean _generated = false;
181
182 // Members aren't serializable. Use a proxy that can provide a Member
183 // to avoid writing the full Externalizable implementation.
184 private MemberProvider _backingMember = null;
185
186 // Members aren't serializable. Initializing _extMethod and _factMethod to
187 // DEFAULT_METHOD is sufficient to trigger lazy population of these fields.
188 private transient Method _extMethod = DEFAULT_METHOD;
189 private transient Member _factMethod = DEFAULT_METHOD;
190
191 // intermediate and impl data
192 private boolean _intermediate = true;
193 private Boolean _implData = Boolean.TRUE;
194
195 // value generation
196 private int _valStrategy = -1;
197 private int _upStrategy = -1;
198 private String _seqName = ClassMetaData.DEFAULT_STRING;
199 private SequenceMetaData _seqMeta = null;
200
201 // inverses
202 private String _mappedBy = null;
203 private FieldMetaData _mappedByMeta = null;
204 private FieldMetaData[] _inverses = null;
205 private String _inverse = ClassMetaData.DEFAULT_STRING;
206
207 // ordering on load
208 private Order[] _orders = null;
209 private String _orderDec = null;
210 // indicate if this field is used by other field as "order by" value
211 private boolean _usedInOrderBy = false;
212
213 /**
214 * Constructor.
215 *
216 * @param name the field name
217 * @param type the field type
218 * @param owner the owning class metadata
219 */
220 protected FieldMetaData(String name, Class type, ClassMetaData owner) {
221 _name = name;
222 _owner = owner;
223 _dec = null;
224 _decMeta = null;
225 _val = owner.getRepository().newValueMetaData(this);
226 _key = owner.getRepository().newValueMetaData(this);
227 _elem = owner.getRepository().newValueMetaData(this);
228
229 setDeclaredType(type);
230 }
231
232 /**
233 * Supply the backing member object; this allows us to utilize
234 * parameterized type information if available.
235 */
236 public void backingMember(Member member) {
237 if (member == null)
238 return;
239 if (Modifier.isTransient(member.getModifiers()))
240 _transient = true;
241
242 _backingMember = new MemberProvider(member);
243
244 Class type;
245 Class[] types;
246 if (member instanceof Field) {
247 Field f = (Field) member;
248 type = f.getType();
249 types = JavaVersions.getParameterizedTypes(f);
250 } else {
251 Method meth = (Method) member;
252 type = meth.getReturnType();
253 types = JavaVersions.getParameterizedTypes(meth);
254 }
255
256 setDeclaredType(type);
257 if (Collection.class.isAssignableFrom(type)
258 && _elem.getDeclaredType() == Object.class
259 && types.length == 1) {
260 _elem.setDeclaredType(types[0]);
261 } else if (Map.class.isAssignableFrom(type)
262 && types.length == 2) {
263 if (_key.getDeclaredType() == Object.class)
264 _key.setDeclaredType(types[0]);
265 if (_elem.getDeclaredType() == Object.class)
266 _elem.setDeclaredType(types[1]);
267 }
268 }
269
270 /**
271 * Return the backing member supplied in {@link #backingMember}.
272 */
273 public Member getBackingMember() {
274 return (_backingMember == null) ? null : _backingMember.getMember();
275 }
276
277 /**
278 * The metadata repository.
279 */
280 public MetaDataRepository getRepository() {
281 return _owner.getRepository();
282 }
283
284 /**
285 * The class that defines the metadata for this field.
286 */
287 public ClassMetaData getDefiningMetaData() {
288 return _owner;
289 }
290
291 /**
292 * The declaring class.
293 */
294 public Class getDeclaringType() {
295 return (_dec == null) ? _owner.getDescribedType() : _dec;
296 }
297
298 /**
299 * The declaring class.
300 */
301 public void setDeclaringType(Class cls) {
302 _dec = cls;
303 _decMeta = null;
304 _fullName = null;
305 _embedFullName = null;
306 }
307
308 /**
309 * The declaring class.
310 */
311 public ClassMetaData getDeclaringMetaData() {
312 if (_dec == null)
313 return _owner;
314 if (_decMeta == null)
315 _decMeta = getRepository().getMetaData(_dec,
316 _owner.getEnvClassLoader(), true);
317 return _decMeta;
318 }
319
320 /**
321 * The field name.
322 */
323 public String getName() {
324 return _name;
325 }
326
327 /**
328 * The field name, qualified by the owning class.
329 * @deprecated Use getFullName(boolean) instead.
330 */
331 public String getFullName() {
332 return getFullName(false);
333 }
334
335 /**
336 * The field name, qualified by the owning class and optionally the
337 * embedding owner's name (if any).
338 */
339 public String getFullName(boolean embedOwner) {
340 if (_fullName == null)
341 _fullName = getDeclaringType().getName() + "." + _name;
342 if (embedOwner && _embedFullName == null) {
343 if (_owner.getEmbeddingMetaData() == null)
344 _embedFullName = _fullName;
345 else
346 _embedFullName = _owner.getEmbeddingMetaData().
347 getFieldMetaData().getFullName(true) + "." + _fullName;
348 }
349 return (embedOwner) ? _embedFullName : _fullName;
350 }
351
352 /**
353 * MetaData about the field value.
354 */
355 public ValueMetaData getValue() {
356 return _val;
357 }
358
359 /**
360 * Metadata about the key value.
361 */
362 public ValueMetaData getKey() {
363 return _key;
364 }
365
366 /**
367 * Metadata about the element value.
368 */
369 public ValueMetaData getElement() {
370 return _elem;
371 }
372
373 /**
374 * Return whether this field is mapped to the datastore. By default,
375 * returns true for all persistent fields whose defining class is mapped.
376 */
377 public boolean isMapped() {
378 return _manage == MANAGE_PERSISTENT && _owner.isMapped();
379 }
380
381 /**
382 * The type this field was initialized with, and therefore the
383 * type to use for proxies when loading data into this field.
384 */
385 public Class getProxyType() {
386 return (_proxyClass == null) ? getDeclaredType() : _proxyClass;
387 }
388
389 /**
390 * The type this field was initialized with, and therefore the
391 * type to use for proxies when loading data into this field.
392 */
393 public void setProxyType(Class type) {
394 _proxyClass = type;
395 }
396
397 /**
398 * The initializer used by the field, or null if none. This
399 * is additional information for initializing the field, such as
400 * a custom {@link Comparator} used by a {@link Set} or
401 * a {@link TimeZone} used by a {@link Calendar}.
402 */
403 public Object getInitializer() {
404 return _initializer;
405 }
406
407 /**
408 * The initializer used by the field, or null if none. This
409 * is additional information for initializing the field, such as
410 * a custom {@link Comparator} used by a {@link Set} or
411 * a {@link TimeZone} used by a {@link Calendar}.
412 */
413 public void setInitializer(Object initializer) {
414 _initializer = initializer;
415 }
416
417 /**
418 * Return whether this is a transient field.
419 */
420 public boolean isTransient() {
421 return _transient;
422 }
423
424 /**
425 * Return whether this is a transient field.
426 */
427 public void setTransient(boolean trans) {
428 _transient = trans;
429 }
430
431 /**
432 * The absolute index of this persistent/transactional field.
433 */
434 public int getIndex() {
435 return _index;
436 }
437
438 /**
439 * The absolute index of this persistent/transactional field.
440 */
441 public void setIndex(int index) {
442 _index = index;
443 }
444
445 /**
446 * The relative index of this persistent/transactional field.
447 */
448 public int getDeclaredIndex() {
449 return _decIndex;
450 }
451
452 /**
453 * The relative index of this persistent/transactional field.
454 */
455 public void setDeclaredIndex(int index) {
456 _decIndex = index;
457 }
458
459 /**
460 * The index in which this field was listed in the metadata. Defaults to
461 * <code>-1</code> if this field was not listed in the metadata.
462 */
463 public int getListingIndex() {
464 return _listIndex;
465 }
466
467 /**
468 * The index in which this field was listed in the metadata. Defaults to
469 * <code>-1</code> if this field was not listed in the metadata.
470 */
471 public void setListingIndex(int index) {
472 _listIndex = index;
473 }
474
475 /**
476 * The absolute primary key index for this field, or -1 if not a primary
477 * key. The first primary key field has index 0, the second index 1, etc.
478 */
479 public int getPrimaryKeyIndex() {
480 return _pkIndex;
481 }
482
483 /**
484 * The absolute primary key index for this field, or -1 if not a primary
485 * key. The first primary key field has index 0, the second index 1, etc.
486 */
487 public void setPrimaryKeyIndex(int index) {
488 _pkIndex = index;
489 }
490
491 /**
492 * Return the management level for the field. Will be one of:
493 * <ul>
494 * <li>{@link #MANAGE_PERSISTENT}: the field is persistent</li>
495 * <li>{@link #MANAGE_TRANSACTIONAL}: the field is transactional but not
496 * persistent</li>
497 * <li>{@link #MANAGE_NONE}: the field is not managed</li>
498 * </ul> Defaults to {@link #MANAGE_PERSISTENT}.
499 */
500 public int getManagement() {
501 return _manage;
502 }
503
504 /**
505 * Return the management level for the field. Will be one of:
506 * <ul>
507 * <li>{@link #MANAGE_PERSISTENT}: the field is persistent</li>
508 * <li>{@link #MANAGE_TRANSACTIONAL}: the field is transactional but not
509 * persistent</li>
510 * <li>{@link #MANAGE_NONE}: the field is not managed</li>
511 * </ul>
512 * Defaults to {@link #MANAGE_PERSISTENT}.
513 */
514 public void setManagement(int manage) {
515 if ((_manage == MANAGE_NONE) != (manage == MANAGE_NONE))
516 _owner.clearFieldCache();
517 _manage = manage;
518 }
519
520 /**
521 * Whether this is a primary key field.
522 */
523 public boolean isPrimaryKey() {
524 return _primKey;
525 }
526
527 /**
528 * Whether this is a primary key field.
529 */
530 public void setPrimaryKey(boolean primKey) {
531 _primKey = primKey;
532 }
533
534 /**
535 * For a primary key field, return the type of the corresponding object id
536 * class field.
537 */
538 public int getObjectIdFieldTypeCode() {
539 ClassMetaData relmeta = getDeclaredTypeMetaData();
540 if (relmeta == null)
541 return getDeclaredTypeCode();
542 if (relmeta.getIdentityType() == ClassMetaData.ID_DATASTORE) {
543 boolean unwrap = getRepository().getMetaDataFactory().getDefaults().
544 isDataStoreObjectIdFieldUnwrapped();
545 return (unwrap) ? JavaTypes.LONG : JavaTypes.OBJECT;
546 }
547 if (relmeta.isOpenJPAIdentity())
548 return relmeta.getPrimaryKeyFields()[0].getObjectIdFieldTypeCode();
549 return JavaTypes.OBJECT;
550 }
551
552 /**
553 * For a primary key field, return the type of the corresponding object id
554 * class field.
555 */
556 public Class getObjectIdFieldType() {
557 ClassMetaData relmeta = getDeclaredTypeMetaData();
558 if (relmeta == null)
559 return getDeclaredType();
560 switch (relmeta.getIdentityType()) {
561 case ClassMetaData.ID_DATASTORE:
562 boolean unwrap = getRepository().getMetaDataFactory().
563 getDefaults().isDataStoreObjectIdFieldUnwrapped();
564 return (unwrap) ? long.class : Object.class;
565 case ClassMetaData.ID_APPLICATION:
566 if (relmeta.isOpenJPAIdentity())
567 return relmeta.getPrimaryKeyFields()[0].
568 getObjectIdFieldType();
569 return (relmeta.getObjectIdType() == null) ? Object.class
570 : relmeta.getObjectIdType();
571 default:
572 return Object.class;
573 }
574 }
575
576 /**
577 * Whether this field holds optimistic version information.
578 */
579 public boolean isVersion() {
580 return _version == Boolean.TRUE;
581 }
582
583 /**
584 * Whether this field holds optimistic version information.
585 */
586 public void setVersion(boolean version) {
587 _version = (version) ? Boolean.TRUE : Boolean.FALSE;
588 }
589
590 /**
591 * Whether this field is in the default fetch group.
592 */
593 public boolean isInDefaultFetchGroup() {
594 if (_dfg == 0) {
595 if (_manage != MANAGE_PERSISTENT || isPrimaryKey() || isVersion())
596 _dfg = DFG_FALSE;
597 else {
598 // field left as default; dfg setting depends on type
599 switch (getTypeCode()) {
600 case JavaTypes.OBJECT:
601 if (isSerializable() || isEnum())
602 _dfg = DFG_TRUE;
603 else
604 _dfg = DFG_FALSE;
605 break;
606 case JavaTypes.ARRAY:
607 if (isLobArray())
608 _dfg = DFG_TRUE;
609 else
610 _dfg = DFG_FALSE;
611 break;
612 case JavaTypes.COLLECTION:
613 case JavaTypes.MAP:
614 case JavaTypes.PC:
615 case JavaTypes.PC_UNTYPED:
616 _dfg = DFG_FALSE;
617 break;
618 default:
619 _dfg = DFG_TRUE;
620 }
621 }
622 }
623 return (_dfg & DFG_TRUE) > 0;
624 }
625
626 private boolean isEnum() {
627 if (_enumField == null) {
628 Class dt = getDeclaredType();
629 _enumField = JavaVersions.isEnumeration(dt)
630 ? Boolean.TRUE : Boolean.FALSE;
631 }
632 return _enumField.booleanValue();
633 }
634
635 private boolean isSerializable() {
636 if (_serializableField == null) {
637 Class dt = getDeclaredType();
638 if (Serializable.class.isAssignableFrom(dt))
639 _serializableField = Boolean.TRUE;
640 else
641 _serializableField = Boolean.FALSE;
642 }
643 return _serializableField.booleanValue();
644 }
645
646 private boolean isLobArray() {
647 // check for byte[], Byte[], char[], Character[]
648 if (_lobField == null) {
649 Class dt = getDeclaredType();
650 if (dt == byte[].class || dt == Byte[].class ||
651 dt == char[].class || dt == Character[].class)
652 _lobField = Boolean.TRUE;
653 else
654 _lobField = Boolean.FALSE;
655 }
656 return _lobField.booleanValue();
657 }
658
659 /**
660 * Whether this field is in the default fetch group.
661 */
662 public void setInDefaultFetchGroup(boolean dfg) {
663 if (dfg)
664 _dfg = DFG_TRUE;
665 else
666 _dfg = DFG_FALSE;
667 _dfg |= DFG_EXPLICIT;
668 }
669
670 /**
671 * Whether the default fetch group setting is explicit.
672 */
673 public boolean isDefaultFetchGroupExplicit() {
674 return (_dfg & DFG_EXPLICIT) > 0;
675 }
676
677 /**
678 * Whether the default fetch group setting is explicit. Allow setting
679 * for testing.
680 */
681 public void setDefaultFetchGroupExplicit(boolean explicit) {
682 if (explicit)
683 _dfg |= DFG_EXPLICIT;
684 else
685 _dfg &= ~DFG_EXPLICIT;
686 }
687
688 /**
689 * Gets the name of the custom fetch groups those are associated to this
690 * receiver. This does not include the "default" and "all" fetch groups.
691 *
692 * @return the set of fetch group names, not including the default and
693 * all fetch groups.
694 */
695 public String[] getCustomFetchGroups() {
696 if (_fgs == null) {
697 if (_fgSet == null || _manage != MANAGE_PERSISTENT
698 || isPrimaryKey() || isVersion())
699 _fgs = new String[0];
700 else
701 _fgs = (String[]) _fgSet.toArray(new String[_fgSet.size()]);
702 }
703 return _fgs;
704 }
705
706 /**
707 * The fetch group that is to be loaded when this receiver is loaded, or
708 * null if none set.
709 */
710 public String getLoadFetchGroup () {
711 return _lfg;
712 }
713
714 /**
715 * The fetch group that is to be loaded when this receiver is loaded, or
716 * null if none set.
717 */
718 public void setLoadFetchGroup (String lfg) {
719 if ("".equals(lfg))
720 lfg = null;
721 _lfg = lfg;
722 }
723
724 /**
725 * Whether this field is in the given fetch group.
726 */
727 public boolean isInFetchGroup(String fg) {
728 if (_manage != MANAGE_PERSISTENT || isPrimaryKey() || isVersion())
729 return false;
730 if (FetchGroup.NAME_ALL.equals(fg))
731 return true;
732 if (FetchGroup.NAME_DEFAULT.equals(fg))
733 return isInDefaultFetchGroup();
734 return _fgSet != null && _fgSet.contains(fg);
735 }
736
737 /**
738 * Set whether this field is in the given fetch group.
739 *
740 * @param fg is the name of a fetch group that must be present in the
741 * class that declared this field or any of its persistent superclasses.
742 */
743 public void setInFetchGroup(String fg, boolean in) {
744 if (StringUtils.isEmpty(fg))
745 throw new MetaDataException(_loc.get("empty-fg-name", this));
746 if (fg.equals(FetchGroup.NAME_ALL))
747 return;
748 if (fg.equals(FetchGroup.NAME_DEFAULT)) {
749 setInDefaultFetchGroup(in);
750 return;
751 }
752 if (_owner.getFetchGroup(fg) == null)
753 throw new MetaDataException(_loc.get("unknown-fg", fg, this));
754 if (in && _fgSet == null)
755 _fgSet = new HashSet();
756 if ((in && _fgSet.add(fg))
757 || (!in && _fgSet != null && _fgSet.remove(fg)))
758 _fgs = null;
759 }
760
761 /**
762 * How the data store should treat null values for this field:
763 * <ul>
764 * <li>{@link #NULL_UNSET}: no value supplied</li>
765 * <li>{@link #NULL_NONE}: leave null values as null in the data store</li>
766 * <li>{@link #NULL_EXCEPTION}: throw an exception if this field is null
767 * at commit</li>
768 * <li>{@link #NULL_DEFAULT}: use the database default if this field is
769 * null at commit</li>
770 * </ul> Defaults to {@link #NULL_UNSET}.
771 */
772 public int getNullValue() {
773 return _nullValue;
774 }
775
776 /**
777 * How the data store should treat null values for this field:
778 * <ul>
779 * <li>{@link #NULL_UNSET}: no value supplied</li>
780 * <li>{@link #NULL_NONE}: leave null values as null in the data store</li>
781 * <li>{@link #NULL_EXCEPTION}: throw an exception if this field is null
782 * at commit</li>
783 * <li>{@link #NULL_DEFAULT}: use the database default if this field is
784 * null at commit</li>
785 * </ul> Defaults to {@link #NULL_UNSET}.
786 */
787 public void setNullValue(int nullValue) {
788 _nullValue = nullValue;
789 }
790
791 /**
792 * Whether this field is explicitly declared in the metadata.
793 */
794 public boolean isExplicit() {
795 return _explicit;
796 }
797
798 /**
799 * Whether this field is explicitly declared in the metadata.
800 */
801 public void setExplicit(boolean explicit) {
802 _explicit = explicit;
803 }
804
805 /**
806 * The field that this field shares a mapping with.
807 */
808 public String getMappedBy() {
809 return _mappedBy;
810 }
811
812 /**
813 * The field that this field shares a mapping with.
814 */
815 public void setMappedBy(String mapped) {
816 _mappedBy = mapped;
817 _mappedByMeta = null;
818 }
819
820 /**
821 * The field that this field shares a mapping with.
822 */
823 public FieldMetaData getMappedByMetaData() {
824 if (_mappedBy != null && _mappedByMeta == null) {
825 ClassMetaData meta = null;
826 switch (getTypeCode()) {
827 case JavaTypes.PC:
828 meta = getTypeMetaData();
829 break;
830 case JavaTypes.ARRAY:
831 case JavaTypes.COLLECTION:
832 case JavaTypes.MAP:
833 meta = _elem.getTypeMetaData();
834 break;
835 }
836
837 FieldMetaData field = (meta == null) ? null
838 : meta.getField(_mappedBy);
839 if (field == null)
840 throw new MetaDataException(_loc.get("no-mapped-by", this,
841 _mappedBy));
842 if (field.getMappedBy() != null)
843 throw new MetaDataException(_loc.get("circ-mapped-by", this,
844 _mappedBy));
845 _mappedByMeta = field;
846 }
847 return _mappedByMeta;
848 }
849
850 /**
851 * Logical inverse field.
852 */
853 public String getInverse() {
854 if (ClassMetaData.DEFAULT_STRING.equals(_inverse))
855 _inverse = null;
856 return _inverse;
857 }
858
859 /**
860 * Logical inverse field.
861 */
862 public void setInverse(String inverse) {
863 _inverses = null;
864 _inverse = inverse;
865 }
866
867 /**
868 * Return all inverses of this field.
869 */
870 public FieldMetaData[] getInverseMetaDatas() {
871 if (_inverses == null) {
872 // can't declare both an inverse owner and a logical inverse
873 String inv = getInverse();
874 if (_mappedBy != null && inv != null && !_mappedBy.equals(inv))
875 throw new MetaDataException(_loc.get("mapped-not-inverse",
876 this));
877
878 // get the metadata for the type on the other side of this relation
879 ClassMetaData meta = null;
880 switch (getTypeCode()) {
881 case JavaTypes.PC:
882 meta = getTypeMetaData();
883 break;
884 case JavaTypes.ARRAY:
885 case JavaTypes.COLLECTION:
886 meta = _elem.getTypeMetaData();
887 break;
888 }
889
890 Collection inverses = null;
891 if (meta != null) {
892 // add mapped by and named inverse, if any
893 FieldMetaData field = getMappedByMetaData();
894 if (field != null) {
895 // mapped by field isn't necessarily a pc type, but all
896 // inverses must be
897 if (field.getTypeCode() == JavaTypes.PC
898 || field.getElement().getTypeCode() == JavaTypes.PC) {
899 inverses = new ArrayList(3);
900 inverses.add(field);
901 }
902 } else if (inv != null) {
903 field = meta.getField(inv);
904 if (field == null)
905 throw new MetaDataException(_loc.get("no-inverse",
906 this, inv));
907 inverses = new ArrayList(3);
908 inverses.add(field);
909 }
910
911 // scan rel type for fields that name this field as an inverse
912 FieldMetaData[] fields = meta.getFields();
913 Class type = getDeclaringMetaData().getDescribedType();
914 for (int i = 0; i < fields.length; i++) {
915 // skip fields that aren't compatible with our owning class
916 switch (fields[i].getTypeCode()) {
917 case JavaTypes.PC:
918 if (!type.isAssignableFrom(fields[i].getType()))
919 continue;
920 break;
921 case JavaTypes.COLLECTION:
922 case JavaTypes.ARRAY:
923 if (!type.isAssignableFrom(fields[i].
924 getElement().getType()))
925 continue;
926 break;
927 default:
928 continue;
929 }
930
931 // if the field declares us as its inverse and we haven't
932 // already added it (we might have if we also declared it
933 // as our inverse), add it now
934 if (_name.equals(fields[i].getMappedBy())
935 || _name.equals(fields[i].getInverse())) {
936 if (inverses == null)
937 inverses = new ArrayList(3);
938 if (!inverses.contains(fields[i]))
939 inverses.add(fields[i]);
940 }
941 }
942 }
943
944 MetaDataRepository repos = getRepository();
945 if (inverses == null)
946 _inverses = repos.EMPTY_FIELDS;
947 else
948 _inverses = (FieldMetaData[]) inverses.toArray
949 (repos.newFieldMetaDataArray(inverses.size()));
950 }
951 return _inverses;
952 }
953
954 /**
955 * The strategy to use for insert value generation.
956 * One of the constants from {@link ValueStrategies}.
957 */
958 public int getValueStrategy() {
959 if (_valStrategy == -1)
960 _valStrategy = ValueStrategies.NONE;
961 return _valStrategy;
962 }
963
964 /**
965 * The strategy to use for insert value generation.
966 * One of the constants from {@link ValueStrategies}.
967 */
968 public void setValueStrategy(int strategy) {
969 _valStrategy = strategy;
970 if (strategy != ValueStrategies.SEQUENCE)
971 setValueSequenceName(null);
972 }
973
974 /**
975 * The value sequence name, or null for none.
976 */
977 public String getValueSequenceName() {
978 if (ClassMetaData.DEFAULT_STRING.equals(_seqName))
979 _seqName = null;
980 return _seqName;
981 }
982
983 /**
984 * The value sequence name, or null for none.
985 */
986 public void setValueSequenceName(String seqName) {
987 _seqName = seqName;
988 _seqMeta = null;
989 if (seqName != null)
990 setValueStrategy(ValueStrategies.SEQUENCE);
991 }
992
993 /**
994 * Metadata for the value sequence.
995 */
996 public SequenceMetaData getValueSequenceMetaData() {
997 if (_seqMeta == null && getValueSequenceName() != null)
998 _seqMeta = getRepository().getSequenceMetaData(_owner,
999 getValueSequenceName(), true);
1000 return _seqMeta;
1001 }
1002
1003 /**
1004 * The strategy to use when updating the field.
1005 */
1006 public int getUpdateStrategy() {
1007 if (isVersion())
1008 return UpdateStrategies.RESTRICT;
1009 if (_upStrategy == -1)
1010 _upStrategy = UpdateStrategies.NONE;
1011 return _upStrategy;
1012 }
1013
1014 /**
1015 * Set the update strategy.
1016 */
1017 public void setUpdateStrategy(int strategy) {
1018 _upStrategy = strategy;
1019 }
1020
1021 /**
1022 * Whether this field is backed by a large result set.
1023 */
1024 public boolean isLRS() {
1025 return _lrs == Boolean.TRUE && _manage == MANAGE_PERSISTENT;
1026 }
1027
1028 /**
1029 * Whether this field is backed by a large result set.
1030 */
1031 public void setLRS(boolean lrs) {
1032 _lrs = (lrs) ? Boolean.TRUE : Boolean.FALSE;
1033 }
1034
1035 /**
1036 * Whether this field is backed by a stream.
1037 *
1038 * @since 1.1.0
1039 */
1040 public boolean isStream() {
1041 return _stream == Boolean.TRUE && _manage == MANAGE_PERSISTENT;
1042 }
1043
1044 /**
1045 * Whether this field is backed by a stream.
1046 *
1047 * @since 1.1.0
1048 */
1049 public void setStream(boolean stream) {
1050 _stream = (stream) ? Boolean.TRUE : Boolean.FALSE;
1051 }
1052
1053 /**
1054 * Whether this field uses intermediate data when loading/storing
1055 * information through a {@link OpenJPAStateManager}. Defaults to true.
1056 *
1057 * @see OpenJPAStateManager#setIntermediate(int,Object)
1058 */
1059 public boolean usesIntermediate() {
1060 return _intermediate;
1061 }
1062
1063 /**
1064 * Whether this field uses intermediate data when loading/storing
1065 * information through a {@link OpenJPAStateManager}. Defaults to true.
1066 *
1067 * @see OpenJPAStateManager#setIntermediate(int,Object)
1068 */
1069 public void setUsesIntermediate(boolean intermediate) {
1070 _intermediate = intermediate;
1071 _owner.clearExtraFieldDataTable();
1072 }
1073
1074 /**
1075 * Whether this field uses impl data in conjunction with standard
1076 * field data when acting on a {@link OpenJPAStateManager}.
1077 * Defaults to {@link Boolean#TRUE} (non-cachable impl data).
1078 *
1079 * @return {@link Boolean#FALSE} if this field does not use impl data,
1080 * {@link Boolean#TRUE} if this field uses non-cachable impl
1081 * data, or <code>null</code> if this field uses impl data that
1082 * should be cached across instances
1083 * @see OpenJPAStateManager#setImplData(int,Object)
1084 */
1085 public Boolean usesImplData() {
1086 return _implData;
1087 }
1088
1089 /**
1090 * Whether this field uses impl data in conjunction with standard
1091 * field data when acting on a {@link OpenJPAStateManager}.
1092 *
1093 * @see OpenJPAStateManager#setImplData(int,Object)
1094 * @see #usesImplData
1095 */
1096 public void setUsesImplData(Boolean implData) {
1097 _implData = implData;
1098 _owner.clearExtraFieldDataTable();
1099 }
1100
1101 /**
1102 * The orderings for this field to be applied on load, or empty array.
1103 */
1104 public Order[] getOrders() {
1105 if (_orders == null) {
1106 if (_orderDec == null)
1107 _orders = getRepository().EMPTY_ORDERS;
1108 else {
1109 String[] decs = Strings.split(_orderDec, ",", 0);
1110 Order[] orders = getRepository().newOrderArray(decs.length);
1111 int spc;
1112 boolean asc;
1113 for (int i = 0; i < decs.length; i++) {
1114 decs[i] = decs[i].trim();
1115 spc = decs[i].indexOf(' ');
1116 if (spc == -1)
1117 asc = true;
1118 else {
1119 asc = decs[i].substring(spc + 1).trim().
1120 toLowerCase().startsWith("asc");
1121 decs[i] = decs[i].substring(0, spc);
1122 }
1123 orders[i] = getRepository().newOrder(this, decs[i], asc);
1124 //set "isUsedInOrderBy" to the field
1125 ClassMetaData elemCls = getElement()
1126 .getDeclaredTypeMetaData();
1127 if (elemCls != null) {
1128 FieldMetaData fmd = elemCls.getDeclaredField(decs[i]);
1129 if (fmd != null)
1130 fmd.setUsedInOrderBy(true);
1131 }
1132 }
1133 _orders = orders;
1134 }
1135 }
1136 return _orders;
1137 }
1138
1139 /**
1140 * The orderings for this field to be applied on load.
1141 */
1142 public void setOrders(Order[] orders) {
1143 _orderDec = null;
1144 _orders = orders;
1145 }
1146
1147 /**
1148 * String declaring the orderings for this field to be applied on load,
1149 * or null. The string is of the form:<br />
1150 * <code>orderable[ asc|desc][, ...]</code><br />
1151 * The orderable <code>#element</code> is used to denote the value of
1152 * the field's elements.
1153 */
1154 public String getOrderDeclaration() {
1155 if (_orderDec == null && _orders != null) {
1156 StringBuffer buf = new StringBuffer();
1157 for (int i = 0; i < _orders.length; i++) {
1158 if (i > 0)
1159 buf.append(", ");
1160 buf.append(_orders[i].getName()).append(" ");
1161 buf.append((_orders[i].isAscending()) ? "asc" : "desc");
1162 }
1163 _orderDec = buf.toString();
1164 }
1165 return _orderDec;
1166 }
1167
1168 /**
1169 * String declaring the orderings for this field to be applied on load,
1170 * or null. The string is of the form:<br />
1171 * <code>orderable[ asc|desc][, ...]</code><br />
1172 * The orderable <code>#element</code> is used to denote the value of
1173 * the field's elements.
1174 */
1175 public void setOrderDeclaration(String dec) {
1176 _orderDec = StringUtils.trimToNull(dec);
1177 _orders = null;
1178 }
1179
1180 /**
1181 * Order this field value when it is loaded.
1182 */
1183 public Object order(Object val) {
1184 if (val == null)
1185 return null;
1186
1187 Order[] orders = getOrders();
1188 if (orders.length == 0)
1189 return val;
1190
1191 // create a comparator for the elements of the value
1192 Comparator comp;
1193 if (orders.length == 1)
1194 comp = orders[0].getComparator();
1195 else {
1196 List comps = null;
1197 Comparator curComp;
1198 for (int i = 0; i < orders.length; i++) {
1199 curComp = orders[i].getComparator();
1200 if (curComp != null) {
1201 if (comps == null)
1202 comps = new ArrayList(orders.length);
1203 if (i != comps.size())
1204 throw new MetaDataException(_loc.get
1205 ("mixed-inmem-ordering", this));
1206 comps.add(curComp);
1207 }
1208 }
1209 if (comps == null)
1210 comp = null;
1211 else
1212 comp = new ComparatorChain(comps);
1213 }
1214
1215 if (comp == null)
1216 return val;
1217
1218 // sort
1219 switch (getTypeCode()) {
1220 case JavaTypes.ARRAY:
1221 List l = JavaTypes.toList(val, _elem.getType(), true);
1222 Collections.sort(l, comp);
1223 return JavaTypes.toArray(l, _elem.getType());
1224 case JavaTypes.COLLECTION:
1225 if (val instanceof List)
1226 Collections.sort((List) val, comp);
1227 return val;
1228 default:
1229 throw new MetaDataException(_loc.get("cant-order", this));
1230 }
1231 }
1232
1233 /**
1234 * Whether the field is externalized.
1235 */
1236 public boolean isExternalized() {
1237 return getExternalizerMethod() != null
1238 || getExternalValueMap() != null;
1239 }
1240
1241 /**
1242 * Convert the given field value to its external value through the
1243 * provided externalizer, or return the value as-is if no externalizer.
1244 */
1245 public Object getExternalValue(Object val, StoreContext ctx) {
1246 Map extValues = getExternalValueMap();
1247 if (extValues != null) {
1248 Object foundVal = extValues.get(val);
1249 if (foundVal == null) {
1250 throw new UserException(_loc.get("bad-externalized-value",
1251 new Object[] { val, extValues.keySet(), this }))
1252 .setFatal(true).setFailedObject(val);
1253 } else {
1254 return foundVal;
1255 }
1256 }
1257
1258 Method externalizer = getExternalizerMethod();
1259 if (externalizer == null)
1260 return val;
1261
1262 // special case for queries: allow the given value to pass through
1263 // as-is if it is already in externalized form
1264 if (val != null && getType().isInstance(val)
1265 && (!getDeclaredType().isInstance(val)
1266 || getDeclaredType() == Object.class))
1267 return val;
1268
1269 try {
1270 // either invoke the static toExternal(val[, ctx]) method, or the
1271 // non-static val.toExternal([ctx]) method
1272 if (Modifier.isStatic(externalizer.getModifiers())) {
1273 if (externalizer.getParameterTypes().length == 1)
1274 return externalizer.invoke(null, new Object[]{ val });
1275 return externalizer.invoke(null, new Object[]{ val, ctx });
1276 }
1277 if (val == null)
1278 return null;
1279 if (externalizer.getParameterTypes().length == 0)
1280 return externalizer.invoke(val, (Object[]) null);
1281 return externalizer.invoke(val, new Object[]{ ctx });
1282 } catch (OpenJPAException ke) {
1283 throw ke;
1284 } catch (Exception e) {
1285 throw new MetaDataException(_loc.get("externalizer-err", this,
1286 Exceptions.toString(val), e.toString())).setCause(e);
1287 }
1288 }
1289
1290 /**
1291 * Return the result of passing the given external value through the
1292 * factory to get the field value. If no factory is present,
1293 * the given value is returned as-is.
1294 */
1295 public Object getFieldValue(Object val, StoreContext ctx) {
1296 Map fieldValues = getFieldValueMap();
1297 if (fieldValues != null)
1298 return fieldValues.get(val);
1299
1300 Member factory = getFactoryMethod();
1301 if (factory == null)
1302 return val;
1303
1304 try {
1305 if (val == null && getNullValue() == NULL_DEFAULT)
1306 return AccessController.doPrivileged(
1307 J2DoPrivHelper.newInstanceAction(getDeclaredType()));
1308
1309 // invoke either the constructor for the field type,
1310 // or the static type.toField(val[, ctx]) method
1311 if (factory instanceof Constructor) {
1312 if (val == null)
1313 return null;
1314 return ((Constructor) factory).newInstance
1315 (new Object[]{ val });
1316 }
1317
1318 Method meth = (Method) factory;
1319 if (meth.getParameterTypes().length == 1)
1320 return meth.invoke(null, new Object[]{ val });
1321 return meth.invoke(null, new Object[]{ val, ctx });
1322 } catch (Exception e) {
1323 // unwrap cause
1324 if (e instanceof InvocationTargetException) {
1325 Throwable t = ((InvocationTargetException) e).
1326 getTargetException();
1327 if (t instanceof Error)
1328 throw (Error) t;
1329 e = (Exception) t;
1330
1331 // allow null values to cause NPEs and illegal arg exceptions
1332 // without error
1333 if (val == null && (e instanceof NullPointerException
1334 || e instanceof IllegalArgumentException))
1335 return null;
1336 }
1337
1338 if (e instanceof OpenJPAException)
1339 throw (OpenJPAException) e;
1340 if (e instanceof PrivilegedActionException)
1341 e = ((PrivilegedActionException) e).getException();
1342 throw new MetaDataException(_loc.get("factory-err", this,
1343 Exceptions.toString(val), e.toString())).setCause(e);
1344 }
1345 }
1346
1347 /**
1348 * The name of this field's externalizer, or null if none.
1349 */
1350 public String getExternalizer() {
1351 return _extName;
1352 }
1353
1354 /**
1355 * The name of this field's externalizer, or null if none.
1356 */
1357 public void setExternalizer(String externalizer) {
1358 _extName = externalizer;
1359 _extMethod = DEFAULT_METHOD;
1360 }
1361
1362 /**
1363 * The name of this field's factory, or null if none.
1364 */
1365 public String getFactory() {
1366 return _factName;
1367 }
1368
1369 /**
1370 * The name of this field's factory, or null if none.
1371 */
1372 public void setFactory(String factory) {
1373 _factName = factory;
1374 _factMethod = DEFAULT_METHOD;
1375 }
1376
1377 /**
1378 * Properties string mapping field values to external values.
1379 */
1380 public String getExternalValues() {
1381 return _extString;
1382 }
1383
1384 /**
1385 * Properties string mapping field values to external values.
1386 */
1387 public void setExternalValues(String values) {
1388 _extString = values;
1389 _extValues = null;
1390 }
1391
1392 /**
1393 * Return the mapping of field values to external values.
1394 */
1395 public Map getExternalValueMap() {
1396 parseExternalValues();
1397 return _extValues;
1398 }
1399
1400 /**
1401 * Return the mapping of external values to field values.
1402 */
1403 public Map getFieldValueMap() {
1404 parseExternalValues();
1405 return _fieldValues;
1406 }
1407
1408 /**
1409 * Parse external values into maps.
1410 */
1411 private void parseExternalValues() {
1412 if (_extValues != Collections.EMPTY_MAP
1413 && _fieldValues != Collections.EMPTY_MAP)
1414 return;
1415
1416 if (_extString == null) {
1417 _extValues = null;
1418 _fieldValues = null;
1419 return;
1420 }
1421
1422 // parse string into options; this takes care of proper trimming etc
1423 Options values = Configurations.parseProperties(_extString);
1424 if (values.isEmpty())
1425 throw new MetaDataException(_loc.get("no-external-values", this,
1426 _extString));
1427
1428 Map extValues = new HashMap((int) (values.size() * 1.33 + 1));
1429 Map fieldValues = new HashMap((int) (values.size() * 1.33 + 1));
1430 Map.Entry entry;
1431 Object extValue, fieldValue;
1432 for (Iterator itr = values.entrySet().iterator(); itr.hasNext();) {
1433 entry = (Map.Entry) itr.next();
1434 fieldValue = transform((String) entry.getKey(),
1435 getDeclaredTypeCode());
1436 extValue = transform((String) entry.getValue(), getTypeCode());
1437
1438 extValues.put(fieldValue, extValue);
1439 fieldValues.put(extValue, fieldValue);
1440 }
1441
1442 _extValues = extValues;
1443 _fieldValues = fieldValues;
1444 }
1445
1446 /**
1447 * Return the string value converted to the given type code. The string
1448 * must be non-null and trimmed.
1449 */
1450 private Object transform(String val, int typeCode) {
1451 if ("null".equals(val))
1452 return null;
1453
1454 switch (typeCode) {
1455 case JavaTypes.BOOLEAN:
1456 case JavaTypes.BOOLEAN_OBJ:
1457 return Boolean.valueOf(val);
1458 case JavaTypes.BYTE:
1459 case JavaTypes.BYTE_OBJ:
1460 return Byte.valueOf(val);
1461 case JavaTypes.INT:
1462 case JavaTypes.INT_OBJ:
1463 return Integer.valueOf(val);
1464 case JavaTypes.LONG:
1465 case JavaTypes.LONG_OBJ:
1466 return Long.valueOf(val);
1467 case JavaTypes.SHORT:
1468 case JavaTypes.SHORT_OBJ:
1469 return Short.valueOf(val);
1470 case JavaTypes.DOUBLE:
1471 case JavaTypes.DOUBLE_OBJ:
1472 return Double.valueOf(val);
1473 case JavaTypes.FLOAT:
1474 case JavaTypes.FLOAT_OBJ:
1475 return Float.valueOf(val);
1476 case JavaTypes.CHAR:
1477 case JavaTypes.CHAR_OBJ:
1478 return new Character(val.charAt(0));
1479 case JavaTypes.STRING:
1480 return val;
1481 }
1482 throw new MetaDataException(_loc.get("bad-external-type", this));
1483 }
1484
1485 /**
1486 * The externalizer method.
1487 */
1488 public Method getExternalizerMethod() {
1489 if (_manage != MANAGE_PERSISTENT)
1490 return null;
1491 if (_extMethod == DEFAULT_METHOD) {
1492 if (_extName != null) {
1493 _extMethod = findMethod(_extName);
1494 if (_extMethod == null)
1495 throw new MetaDataException(_loc.get("bad-externalizer",
1496 this, _extName));
1497 } else
1498 _extMethod = null;
1499 }
1500 return _extMethod;
1501 }
1502
1503 /**
1504 * The factory method or constructor.
1505 */
1506 public Member getFactoryMethod() {
1507 if (_manage != MANAGE_PERSISTENT)
1508 return null;
1509 if (_factMethod == DEFAULT_METHOD) {
1510 if (getExternalizerMethod() == null)
1511 _factMethod = null;
1512 else {
1513 try {
1514 if (_factName == null)
1515 _factMethod = getDeclaredType().getConstructor
1516 (new Class[]{ getType() });
1517 else
1518 _factMethod = findMethod(_factName);
1519 } catch (OpenJPAException ke) {
1520 throw ke;
1521 } catch (Exception e) {
1522 }
1523
1524 if (!(_factMethod instanceof Constructor)
1525 && !(_factMethod instanceof Method))
1526 throw new MetaDataException(_loc.get("bad-factory", this));
1527 }
1528 }
1529 return _factMethod;
1530 }
1531
1532 /**
1533 * Find the method for the specified name. Possible forms are:
1534 * <ul>
1535 * <li>toExternalString</li>
1536 * <li>MyFactoryClass.toExternalString</li>
1537 * <li>com.company.MyFactoryClass.toExternalString</li>
1538 * </ul>
1539 *
1540 * @param method the name of the method to locate
1541 * @return the method for invocation
1542 */
1543 private Method findMethod(String method) {
1544 if (StringUtils.isEmpty(method))
1545 return null;
1546
1547 // get class name and get package name divide on the last '.', so the
1548 // names don't apply in this case, but the methods do what we want
1549 String methodName = Strings.getClassName(method);
1550 String clsName = Strings.getPackageName(method);
1551
1552 Class cls = null;
1553 Class owner = _owner.getDescribedType();
1554
1555 if (clsName.length() == 0)
1556 cls = getDeclaredType();
1557 else if (clsName.equals(owner.getName())
1558 || clsName.equals(Strings.getClassName(owner)))
1559 cls = owner;
1560 else
1561 cls = JavaTypes.classForName(clsName, this);
1562
1563 // find the named method
1564 Method[] methods = cls.getMethods();
1565 Class[] params;
1566 for (int i = 0; i < methods.length; i++) {
1567 if (methods[i].getName().equals(methodName)) {
1568 params = methods[i].getParameterTypes();
1569
1570 // static factory methods require one argument or one argument
1571 // plus a ctx; non-static methods require zero arguments or
1572 // just a ctx
1573 if (Modifier.isStatic(methods[i].getModifiers())
1574 && (params.length == 1 || (params.length == 2
1575 && isStoreContextParameter(params[1]))))
1576 return methods[i];
1577 if (!Modifier.isStatic(methods[i].getModifiers())
1578 && (params.length == 0 || (params.length == 1
1579 && isStoreContextParameter(params[0]))))
1580 return methods[i];
1581 }
1582 }
1583
1584 return null;
1585 }
1586
1587 /**
1588 * Return true if the given type is a store context type; we can't
1589 * use the standard <code>isAssignableFrom</code> because of classloader
1590 * oddness.
1591 */
1592 private static boolean isStoreContextParameter(Class type) {
1593 return StoreContext.class.getName().equals(type.getName());
1594 }
1595
1596 public boolean equals(Object other) {
1597 if (other == this)
1598 return true;
1599 if (!(other instanceof FieldMetaData))
1600 return false;
1601 return getFullName(true).equals(((FieldMetaData) other).
1602 getFullName(true));
1603 }
1604
1605 public int hashCode() {
1606 return getFullName(true).hashCode();
1607 }
1608
1609 public int compareTo(Object other) {
1610 if (other == null)
1611 return 1;
1612 return getFullName(true).compareTo(((FieldMetaData) other).
1613 getFullName(true));
1614 }
1615
1616 public String toString() {
1617 return getFullName(true);
1618 }
1619
1620 ////////////////////////
1621 // Resolve and validate
1622 ////////////////////////
1623
1624 /**
1625 * Resolve mode for this field.
1626 */
1627 public int getResolve() {
1628 return _resMode;
1629 }
1630
1631 /**
1632 * Resolve mode for this field.
1633 */
1634 public void setResolve(int mode) {
1635 _resMode = mode;
1636 }
1637
1638 /**
1639 * Resolve mode for this field.
1640 */
1641 public void setResolve(int mode, boolean on) {
1642 if (mode == MODE_NONE)
1643 _resMode = mode;
1644 else if (on)
1645 _resMode |= mode;
1646 else
1647 _resMode &= ~mode;
1648 }
1649
1650 /**
1651 * Resolve and validate metadata. Return true if already resolved.
1652 */
1653 public boolean resolve(int mode) {
1654 if ((_resMode & mode) == mode)
1655 return true;
1656 int cur = _resMode;
1657 _resMode |= mode;
1658
1659 Log log = getRepository().getLog();
1660 if (log.isTraceEnabled())
1661 log.trace(_loc.get("resolve-field", _owner + "@"
1662 + System.identityHashCode(_owner) + "." + _name));
1663
1664 // we only perform actions for metadata mode
1665 if ((mode & MODE_META) == 0 || (cur & MODE_META) != 0)
1666 return false;
1667
1668 Method externalizer = getExternalizerMethod();
1669 if (externalizer != null)
1670 setType(externalizer.getReturnType());
1671
1672 // only pass on metadata resolve mode so that metadata is always
1673 // resolved before any other resolve modes our subclasses pass along
1674 _val.resolve(MODE_META);
1675 _key.resolve(MODE_META);
1676 _elem.resolve(MODE_META);
1677
1678 MetaDataRepository repos = getRepository();
1679 int validate = repos.getValidate();
1680 if ((validate & MetaDataRepository.VALIDATE_META) != 0
1681 && (!ImplHelper.isManagedType(repos.getConfiguration(),
1682 _owner.getDescribedType())
1683 || (validate & MetaDataRepository.VALIDATE_UNENHANCED) == 0)) {
1684 validateLRS();
1685 if ((validate & repos.VALIDATE_RUNTIME) == 0)
1686 validateSupportedType();
1687 validateValue();
1688 validateExtensionKeys();
1689 }
1690 return false;
1691 }
1692
1693 /**
1694 * Validate that this field can be used for LRS.
1695 */
1696 private void validateLRS() {
1697 if (!isLRS())
1698 return;
1699
1700 // can't use lrs for arrays
1701 if (getTypeCode() == JavaTypes.ARRAY)
1702 throw new MetaDataException(_loc.get("bad-lrs-array", this));
1703
1704 // can't use lrs for extranalized vals
1705 if (getExternalizerMethod() != null)
1706 throw new MetaDataException(_loc.get("bad-lrs-extern", this));
1707
1708 // can't use lrs for concrete types
1709 if (getType() != Collection.class && getType() != Map.class
1710 && getType() != Set.class)
1711 throw new MetaDataException(_loc.get("bad-lrs-concrete", this));
1712 }
1713
1714 /**
1715 * Validate that this field is supported by the runtime.
1716 */
1717 private void validateSupportedType() {
1718 // log warnings about things we don't handle
1719 OpenJPAConfiguration conf = getRepository().getConfiguration();
1720 Collection opts = conf.supportedOptions();
1721 Log log = conf.getLog(conf.LOG_METADATA);
1722 switch (getTypeCode()) {
1723 case JavaTypes.PC:
1724 if (isEmbedded() &&
1725 !opts.contains(conf.OPTION_EMBEDDED_RELATION)) {
1726 setEmbedded(false);
1727 if (log.isWarnEnabled())
1728 log.warn(_loc.get("cant-embed", this));
1729 } else
1730 if (isEmbedded() && getDeclaredTypeCode() != JavaTypes.PC) {
1731 setEmbedded(false);
1732 if (log.isWarnEnabled())
1733 log.warn(_loc.get("cant-embed-extern", this));
1734 }
1735 break;
1736 case JavaTypes.COLLECTION:
1737 if (!opts.contains(conf.OPTION_TYPE_COLLECTION))
1738 throw new UnsupportedException(
1739 _loc.get("type-not-supported",
1740 "Collection", this));
1741 if (_elem.isEmbeddedPC()
1742 && !opts.contains(conf.OPTION_EMBEDDED_COLLECTION_RELATION))
1743 {
1744 _elem.setEmbedded(false);
1745 if (log.isWarnEnabled())
1746 log.warn(_loc.get("cant-embed-element", this));
1747 }
1748 break;
1749 case JavaTypes.ARRAY:
1750 if (!opts.contains(conf.OPTION_TYPE_ARRAY))
1751 throw new UnsupportedException(
1752 _loc.get("type-not-supported",
1753 "Array", this));
1754 if (_elem.isEmbeddedPC()
1755 && !opts.contains(conf.OPTION_EMBEDDED_COLLECTION_RELATION))
1756 {
1757 _elem.setEmbedded(false);
1758 if (log.isWarnEnabled())
1759 log.warn(_loc.get("cant-embed-element", this));
1760 }
1761 break;
1762 case JavaTypes.MAP:
1763 if (!opts.contains(conf.OPTION_TYPE_MAP))
1764 throw new UnsupportedException(
1765 _loc.get("type-not-supported",
1766 "Map", this));
1767 if (_elem.isEmbeddedPC()
1768 && !opts.contains(conf.OPTION_EMBEDDED_MAP_RELATION)) {
1769 _elem.setEmbedded(false);
1770 if (log.isWarnEnabled())
1771 log.warn(_loc.get("cant-embed-element", this));
1772 }
1773 if (_key.isEmbeddedPC()
1774 && !opts.contains(conf.OPTION_EMBEDDED_MAP_RELATION)) {
1775 _key.setEmbedded(false);
1776 if (log.isWarnEnabled())
1777 log.warn(_loc.get("cant-embed-key", this));
1778 }
1779 break;
1780 }
1781 }
1782
1783 /**
1784 * Validate our value strategy.
1785 */
1786 private void validateValue() {
1787 if (getExternalizerMethod() != null && getExternalValueMap() != null)
1788 throw new MetaDataException(_loc.get("extern-externvalues", this));
1789 if (getValueStrategy() == ValueStrategies.SEQUENCE
1790 && getValueSequenceName() == null)
1791 throw new MetaDataException(_loc.get("no-seq-name", this));
1792 ValueStrategies.assertSupported(getValueStrategy(), this,
1793 "value strategy");
1794 }
1795
1796 /**
1797 * Copy state from the given field to this one. Do not copy mapping
1798 * information.
1799 */
1800 public void copy(FieldMetaData field) {
1801 super.copy(field);
1802
1803 _intermediate = field.usesIntermediate();
1804 _implData = field.usesImplData();
1805
1806 // copy field-level info; use get methods to force resolution of
1807 // lazy data
1808 _proxyClass = field.getProxyType();
1809 _initializer = field.getInitializer();
1810 _transient = field.isTransient();
1811 _nullValue = field.getNullValue();
1812 _manage = field.getManagement();
1813 _explicit = field.isExplicit();
1814 _extName = field.getExternalizer();
1815 _extMethod = DEFAULT_METHOD;
1816 _factName = field.getFactory();
1817 _factMethod = DEFAULT_METHOD;
1818 _extString = field.getExternalValues();
1819 _extValues = Collections.EMPTY_MAP;
1820 _fieldValues = Collections.EMPTY_MAP;
1821 _primKey = field.isPrimaryKey();
1822 _backingMember = field._backingMember;
1823 _enumField = field._enumField;
1824 _lobField = field._lobField;
1825 _serializableField = field._serializableField;
1826 _generated = field._generated;
1827
1828 // embedded fields can't be versions
1829 if (_owner.getEmbeddingMetaData() == null && _version == null)
1830 _version = (field.isVersion()) ? Boolean.TRUE : Boolean.FALSE;
1831
1832 // only copy this data if not already set explicitly in this instance
1833 if (_dfg == 0) {
1834 _dfg = (field.isInDefaultFetchGroup()) ? DFG_TRUE : DFG_FALSE;
1835 if (field.isDefaultFetchGroupExplicit())
1836 _dfg |= DFG_EXPLICIT;
1837 }
1838 if (_fgSet == null && field._fgSet != null)
1839 _fgSet = new HashSet(field._fgSet);
1840 if (_lfg == null)
1841 _lfg = field.getLoadFetchGroup();
1842 if (_lrs == null)
1843 _lrs = (field.isLRS()) ? Boolean.TRUE : Boolean.FALSE;
1844 if (_valStrategy == -1)
1845 _valStrategy = field.getValueStrategy();
1846 if (_upStrategy == -1)
1847 _upStrategy = field.getUpdateStrategy();
1848 if (ClassMetaData.DEFAULT_STRING.equals(_seqName)) {
1849 _seqName = field.getValueSequenceName();
1850 _seqMeta = null;
1851 }
1852 if (ClassMetaData.DEFAULT_STRING.equals(_inverse))
1853 _inverse = field.getInverse();
1854
1855 // copy value metadata
1856 _val.copy(field);
1857 _key.copy(field.getKey());
1858 _elem.copy(field.getElement());
1859 }
1860
1861 protected void addExtensionKeys(Collection exts) {
1862 getRepository().getMetaDataFactory().addFieldExtensionKeys(exts);
1863 }
1864
1865 ///////////////
1866 // Commentable
1867 ///////////////
1868
1869 public String[] getComments() {
1870 return (_comments == null) ? EMPTY_COMMENTS : _comments;
1871 }
1872
1873 public void setComments(String[] comments) {
1874 _comments = comments;
1875 }
1876
1877 ////////////////////////////////
1878 // ValueMetaData implementation
1879 ////////////////////////////////
1880
1881 public FieldMetaData getFieldMetaData() {
1882 return this;
1883 }
1884
1885 public Class getType() {
1886 return _val.getType();
1887 }
1888
1889 public void setType(Class type) {
1890 _val.setType(type);
1891 if (type.isArray())
1892 _elem.setType(type.getComponentType());
1893 else if (type == Properties.class) {
1894 _key.setType(String.class);
1895 _elem.setType(String.class);
1896 }
1897 }
1898
1899 public int getTypeCode() {
1900 return _val.getTypeCode();
1901 }
1902
1903 public void setTypeCode(int code) {
1904 _val.setTypeCode(code);
1905 }
1906
1907 public boolean isTypePC() {
1908 return _val.isTypePC();
1909 }
1910
1911 public ClassMetaData getTypeMetaData() {
1912 return _val.getTypeMetaData();
1913 }
1914
1915 public Class getDeclaredType() {
1916 return _val.getDeclaredType();
1917 }
1918
1919 public void setDeclaredType(Class type) {
1920 _val.setDeclaredType(type);
1921 if (type.isArray())
1922 _elem.setDeclaredType(type.getComponentType());
1923 else if (type == Properties.class) {
1924 _key.setDeclaredType(String.class);
1925 _elem.setDeclaredType(String.class);
1926 }
1927 }
1928
1929 public int getDeclaredTypeCode() {
1930 return _val.getDeclaredTypeCode();
1931 }
1932
1933 public void setDeclaredTypeCode(int type) {
1934 _val.setDeclaredTypeCode(type);
1935 }
1936
1937 public boolean isDeclaredTypePC() {
1938 return _val.isDeclaredTypePC();
1939 }
1940
1941 public ClassMetaData getDeclaredTypeMetaData() {
1942 return _val.getDeclaredTypeMetaData();
1943 }
1944
1945 public boolean isEmbedded() {
1946 return _val.isEmbedded();
1947 }
1948
1949 public void setEmbedded(boolean embedded) {
1950 _val.setEmbedded(embedded);
1951 }
1952
1953 public boolean isEmbeddedPC() {
1954 return _val.isEmbeddedPC();
1955 }
1956
1957 public ClassMetaData getEmbeddedMetaData() {
1958 return _val.getEmbeddedMetaData();
1959 }
1960
1961 public ClassMetaData addEmbeddedMetaData() {
1962 return _val.addEmbeddedMetaData();
1963 }
1964
1965 public int getCascadeDelete() {
1966 return _val.getCascadeDelete();
1967 }
1968
1969 public void setCascadeDelete(int delete) {
1970 _val.setCascadeDelete(delete);
1971 }
1972
1973 public int getCascadePersist() {
1974 return _val.getCascadePersist();
1975 }
1976
1977 public void setCascadePersist(int persist) {
1978 _val.setCascadePersist(persist);
1979 }
1980
1981 public int getCascadeAttach() {
1982 return _val.getCascadeAttach();
1983 }
1984
1985 public void setCascadeAttach(int attach) {
1986 _val.setCascadeAttach(attach);
1987 }
1988
1989 public int getCascadeRefresh() {
1990 return _val.getCascadeRefresh();
1991 }
1992
1993 public void setCascadeRefresh(int refresh) {
1994 _val.setCascadeRefresh(refresh);
1995 }
1996
1997 public boolean isSerialized() {
1998 return _val.isSerialized();
1999 }
2000
2001 public void setSerialized(boolean serialized) {
2002 _val.setSerialized(serialized);
2003 }
2004
2005 public String getValueMappedBy() {
2006 return _val.getValueMappedBy();
2007 }
2008
2009 public void setValueMappedBy(String mapped) {
2010 _val.setValueMappedBy(mapped);
2011 }
2012
2013 public FieldMetaData getValueMappedByMetaData ()
2014 {
2015 return _val.getValueMappedByMetaData ();
2016 }
2017
2018 public Class getTypeOverride ()
2019 {
2020 return _val.getTypeOverride ();
2021 }
2022
2023 public void setTypeOverride (Class type)
2024 {
2025 _val.setTypeOverride (type);
2026 }
2027
2028 public void copy (ValueMetaData vmd)
2029 {
2030 _val.copy (vmd);
2031 }
2032
2033 /**
2034 * Check if this field is used by other field as "order by" value.
2035 *
2036 * @since 1.1.0
2037 */
2038 public boolean isUsedInOrderBy() {
2039 return _usedInOrderBy;
2040 }
2041
2042 /**
2043 * Whether this field is used by other field as "order by" value .
2044 *
2045 * @since 1.1.0
2046 */
2047 public void setUsedInOrderBy(boolean isUsed) {
2048 _usedInOrderBy = isUsed;
2049 }
2050
2051 /**
2052 * Serializable wrapper around a {@link Method} or {@link Field}. For
2053 * space considerations, this does not support {@link Constructor}s.
2054 */
2055 public static class MemberProvider
2056 implements Externalizable {
2057
2058 private transient Member _member;
2059
2060 public MemberProvider() {
2061 // for externalization
2062 }
2063
2064 MemberProvider(Member member) {
2065 if (member instanceof Constructor)
2066 throw new IllegalArgumentException();
2067
2068 _member = member;
2069 }
2070
2071 public Member getMember() {
2072 return _member;
2073 }
2074
2075 public void readExternal(ObjectInput in)
2076 throws IOException, ClassNotFoundException {
2077 boolean isField = in.readBoolean();
2078 Class cls = (Class) in.readObject();
2079 String memberName = (String) in.readObject();
2080 try {
2081 if (isField)
2082 _member = (Field) AccessController.doPrivileged(
2083 J2DoPrivHelper.getDeclaredFieldAction(
2084 cls, memberName));
2085 else {
2086 Class[] parameterTypes = (Class[]) in.readObject();
2087 _member = (Method) AccessController.doPrivileged(
2088 J2DoPrivHelper.getDeclaredMethodAction(
2089 cls, memberName, parameterTypes));
2090 }
2091 } catch (SecurityException e) {
2092 IOException ioe = new IOException(e.getMessage());
2093 ioe.initCause(e);
2094 throw ioe;
2095 } catch (PrivilegedActionException pae) {
2096 IOException ioe = new IOException(
2097 pae.getException().getMessage());
2098 ioe.initCause(pae);
2099 throw ioe;
2100 }
2101 }
2102
2103 public void writeExternal(ObjectOutput out)
2104 throws IOException {
2105 boolean isField = _member instanceof Field;
2106 out.writeBoolean(isField);
2107 out.writeObject(_member.getDeclaringClass());
2108 out.writeObject(_member.getName());
2109 if (!isField)
2110 out.writeObject(((Method) _member).getParameterTypes());
2111 }
2112 }
2113
2114 public boolean isValueGenerated() {
2115 return _generated;
2116 }
2117
2118 public void setValueGenerated(boolean generated) {
2119 this._generated = generated;
2120 }
2121 }