001: /**********************************************************************
002: Copyright (c) 2004 Andy Jefferson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015:
016: Contributors:
017: 2004 Kikuchi Kousuke - org.jpox.enhancer.conf.JDOConfigMap
018: 2004 Erik Bengtson - add dependent keys and values
019: ...
020: **********************************************************************/package org.jpox.metadata;
021:
022: import java.util.List;
023: import java.util.Set;
024:
025: import org.jpox.ClassLoaderResolver;
026: import org.jpox.api.ApiAdapter;
027: import org.jpox.exceptions.ClassNotResolvedException;
028: import org.jpox.exceptions.JPOXUserException;
029: import org.jpox.util.ClassUtils;
030: import org.jpox.util.JPOXLogger;
031: import org.jpox.util.StringUtils;
032:
033: /**
034: * Representation of the Meta-Data for a Map.
035: *
036: * @since 1.1
037: * @version $Revision: 1.39 $
038: */
039: public class MapMetaData extends ContainerMetaData {
040: /** Representation of the key of the map. */
041: protected ContainerComponent key;
042:
043: /** Representation of the value of the map. */
044: protected ContainerComponent value;
045:
046: /**
047: * Constructor to create a copy of the passed metadata using the passed parent.
048: * @param parent The parent
049: * @param mapmd The metadata to copy
050: */
051: public MapMetaData(AbstractMemberMetaData parent, MapMetaData mapmd) {
052: super (parent);
053: key = new ContainerComponent();
054: key.embedded = mapmd.key.embedded;
055: key.serialized = mapmd.key.serialized;
056: key.dependent = mapmd.key.dependent;
057: key.type = mapmd.key.type;
058: key.classMetaData = mapmd.key.classMetaData;
059:
060: value = new ContainerComponent();
061: value.embedded = mapmd.value.embedded;
062: value.serialized = mapmd.value.serialized;
063: value.dependent = mapmd.value.dependent;
064: value.type = mapmd.value.type;
065: value.classMetaData = mapmd.value.classMetaData;
066: }
067:
068: /**
069: * Constructor.
070: * @param parent parent Field
071: * @param keyType key-type tag value
072: * @param embeddedKey embedded-key tag value
073: * @param dependentKey dependent-key tag value
074: * @param serializedKey serialized-key tag value
075: * @param valueType value-type tag value
076: * @param embeddedValue embedded-value tag value
077: * @param dependentValue dependent-value tag value
078: * @param serializedValue serialized-value tag value
079: */
080: public MapMetaData(AbstractMemberMetaData parent, String keyType,
081: String embeddedKey, String dependentKey,
082: String serializedKey, String valueType,
083: String embeddedValue, String dependentValue,
084: String serializedValue) {
085: super (parent);
086:
087: if (!StringUtils.isWhitespace(keyType)
088: && keyType.indexOf(',') > 0) {
089: throw new InvalidMetaDataException(LOCALISER, "044143",
090: parent.getName(), parent.getClassName());
091: }
092: if (!StringUtils.isWhitespace(valueType)
093: && valueType.indexOf(',') > 0) {
094: throw new InvalidMetaDataException(LOCALISER, "044144",
095: parent.getName(), parent.getClassName());
096: }
097: key = new ContainerComponent(parent.getAbstractClassMetaData()
098: .getPackageName(), keyType, embeddedKey, serializedKey,
099: dependentKey);
100: value = new ContainerComponent(parent
101: .getAbstractClassMetaData().getPackageName(),
102: valueType, embeddedValue, serializedValue,
103: dependentValue);
104: }
105:
106: /**
107: * Method to populate any defaults, and check the validity of the MetaData.
108: * @param clr ClassLoaderResolver to use for loading any key/value types
109: * @param primary the primary ClassLoader to use (or null)
110: */
111: public void populate(ClassLoaderResolver clr, ClassLoader primary) {
112: ApiAdapter api = getMetaDataManager().getApiAdapter();
113:
114: // Check the field type and see if it is castable to a Map
115: Class field_type = getMemberMetaData().getType();
116: if (!java.util.Map.class.isAssignableFrom(field_type)) {
117: throw new InvalidMetaDataException(LOCALISER, "044145",
118: getFieldName(), getMemberMetaData().getClassName(
119: false));
120: }
121:
122: // "key-type"
123: if (key.type == null) {
124: throw new InvalidMetaDataException(LOCALISER, "044146",
125: getFieldName(), getMemberMetaData().getClassName(
126: false));
127: }
128:
129: // Check that the key type exists
130: Class keyTypeClass = null;
131: try {
132: keyTypeClass = clr.classForName(key.type, primary);
133: } catch (ClassNotResolvedException cnre) {
134: try {
135: // Maybe the user specified a java.lang class without fully-qualifying it
136: // This is beyond the scope of the JDO spec which expects java.lang cases to be fully-qualified
137: keyTypeClass = clr.classForName(ClassUtils
138: .getJavaLangClassForType(key.type), primary);
139: } catch (ClassNotResolvedException cnre2) {
140: throw new InvalidMetaDataException(LOCALISER, "044147",
141: getFieldName(), getMemberMetaData()
142: .getClassName(false), key.type);
143: }
144: }
145:
146: if (!keyTypeClass.getName().equals(key.type)) {
147: // The value-type has been resolved from what was specified in the MetaData - update to the fully-qualified name
148: JPOXLogger.METADATA.info(LOCALISER.msg("044148",
149: getFieldName(), getMemberMetaData().getClassName(
150: false), key.type, keyTypeClass.getName()));
151: key.type = keyTypeClass.getName();
152: }
153:
154: // "embedded-key"
155: if (key.embedded == null) {
156: // Assign default for "embedded-key" based on 18.13.2 of JDO 2 spec
157: if (getMetaDataManager().getOMFContext().getTypeManager()
158: .isDefaultEmbeddedType(keyTypeClass)) {
159: key.embedded = Boolean.TRUE;
160: } else if (api.isPersistable(keyTypeClass)
161: || Object.class.isAssignableFrom(keyTypeClass)
162: || keyTypeClass.isInterface()) {
163: key.embedded = Boolean.FALSE;
164: } else {
165: key.embedded = Boolean.TRUE;
166: }
167: }
168: if (key.embedded == Boolean.FALSE) {
169: // If the user has set a non-PC/non-Interface as not embedded, correct it since not supported.
170: // Note : this fails when using in the enhancer since not yet PC
171: if (!api.isPersistable(keyTypeClass)
172: && !keyTypeClass.isInterface()
173: && keyTypeClass != java.lang.Object.class) {
174: key.embedded = Boolean.TRUE;
175: }
176: }
177: KeyMetaData keymd = ((AbstractMemberMetaData) parent)
178: .getKeyMetaData();
179: if (keymd != null && keymd.getEmbeddedMetaData() != null) {
180: // If the user has specified <embedded>, set to true
181: key.embedded = Boolean.TRUE;
182: }
183:
184: // "value-type"
185: if (value.type == null) {
186: throw new InvalidMetaDataException(LOCALISER, "044149",
187: getFieldName(), getMemberMetaData().getClassName(
188: false));
189: }
190:
191: // Check that the value-type exists
192: Class valueTypeClass = null;
193: try {
194: valueTypeClass = clr.classForName(value.type);
195: } catch (ClassNotResolvedException cnre) {
196: try {
197: // Maybe the user specified a java.lang class without fully-qualifying it
198: // This is beyond the scope of the JDO spec which expects java.lang cases to be fully-qualified
199: valueTypeClass = clr.classForName(ClassUtils
200: .getJavaLangClassForType(value.type));
201: } catch (ClassNotResolvedException cnre2) {
202: throw new InvalidMetaDataException(LOCALISER, "044150",
203: getFieldName(), getMemberMetaData()
204: .getClassName(false), value.type);
205: }
206: }
207:
208: if (!valueTypeClass.getName().equals(value.type)) {
209: // The value-type has been resolved from what was specified in the MetaData - update to the fully-qualified name
210: JPOXLogger.METADATA.info(LOCALISER.msg("044151",
211: getFieldName(), getMemberMetaData().getClassName(
212: false), value.type, valueTypeClass
213: .getName()));
214: value.type = valueTypeClass.getName();
215: }
216:
217: // "embedded-value"
218: if (value.embedded == null) {
219: // Assign default for "embedded-value" based on 18.13.2 of JDO 2 spec
220: if (getMetaDataManager().getOMFContext().getTypeManager()
221: .isDefaultEmbeddedType(valueTypeClass)) {
222: value.embedded = Boolean.TRUE;
223: } else if (api.isPersistable(valueTypeClass)
224: || Object.class.isAssignableFrom(valueTypeClass)
225: || valueTypeClass.isInterface()) {
226: value.embedded = Boolean.FALSE;
227: } else {
228: value.embedded = Boolean.TRUE;
229: }
230: }
231: if (value.embedded == Boolean.FALSE) {
232: // If the user has set a non-PC/non-Interface as not embedded, correct it since not supported.
233: // Note : this fails when using in the enhancer since not yet PC
234: if (!api.isPersistable(valueTypeClass)
235: && !valueTypeClass.isInterface()
236: && valueTypeClass != java.lang.Object.class) {
237: value.embedded = Boolean.TRUE;
238: }
239: }
240: ValueMetaData valuemd = ((AbstractMemberMetaData) parent)
241: .getValueMetaData();
242: if (valuemd != null && valuemd.getEmbeddedMetaData() != null) {
243: // If the user has specified <embedded>, set to true
244: value.embedded = Boolean.TRUE;
245: }
246:
247: key.classMetaData = getMemberMetaData()
248: .getAbstractClassMetaData().getMetaDataManager()
249: .getMetaDataForClassInternal(keyTypeClass, clr);
250: value.classMetaData = getMemberMetaData()
251: .getAbstractClassMetaData().getMetaDataManager()
252: .getMetaDataForClassInternal(valueTypeClass, clr);
253:
254: // Cater for Key with mapped-by needing to be PK (for JPA)
255: if (keymd != null && keymd.mappedBy != null
256: && keymd.mappedBy.equals("#PK")) // Special value set by JPAMetaDataHandler
257: {
258: // Need to set the mapped-by of <key> to be the PK of the <value>
259: if (value.classMetaData.getNoOfPrimaryKeyMembers() != 1) {
260: // TODO Localise this
261: throw new JPOXUserException(
262: "JPOX does not support use of <map-key> with no name field when the"
263: + " value class has a composite primary key");
264: }
265: int[] valuePkFieldNums = value.classMetaData
266: .getPKMemberPositions();
267: keymd.mappedBy = value.classMetaData
268: .getMetaDataForManagedMemberAtAbsolutePosition(valuePkFieldNums[0]).name;
269: }
270:
271: setPopulated();
272: }
273:
274: // ----------------------------- Accessors ---------------------------------
275:
276: /**
277: * Accessor for the key-type tag value.
278: * May be comma-separated if several key types are possible.
279: * @return key-type tag value
280: */
281: public String getKeyType() {
282: return key.type;
283: }
284:
285: /**
286: * Accessor for the Key ClassMetaData
287: * @return key ClassMetaData
288: */
289: public AbstractClassMetaData getKeyClassMetaData() {
290: if (key.classMetaData != null
291: && !key.classMetaData.isInitialised()) {
292: key.classMetaData.initialise();
293: }
294: return key.classMetaData;
295: }
296:
297: /**
298: * Accessor for the value-type tag value.
299: * May be comma-separated if several value types are possible.
300: * @return value-type tag value
301: */
302: public String getValueType() {
303: return value.type;
304: }
305:
306: /**
307: * Accessor for the Value ClassMetaData
308: * @return value ClassMetaData
309: */
310: public AbstractClassMetaData getValueClassMetaData() {
311: if (value.classMetaData != null
312: && !value.classMetaData.isInitialised()) {
313: value.classMetaData.initialise();
314: }
315: return value.classMetaData;
316: }
317:
318: /**
319: * Accessor for the embedded-key tag value.
320: * @return embedded-key tag value
321: */
322: public boolean isEmbeddedKey() {
323: if (key.embedded == null) {
324: return false;
325: } else {
326: return key.embedded.booleanValue();
327: }
328: }
329:
330: /**
331: * Accessor for the embedded-value tag value.
332: * @return embedded-value tag value
333: */
334: public boolean isEmbeddedValue() {
335: if (value.embedded == null) {
336: return false;
337: } else {
338: return value.embedded.booleanValue();
339: }
340: }
341:
342: /**
343: * Accessor for the serialized-key tag value.
344: * @return serialized-key tag value
345: */
346: public boolean isSerializedKey() {
347: if (key.serialized == null) {
348: return false;
349: } else {
350: return key.serialized.booleanValue();
351: }
352: }
353:
354: /**
355: * Accessor for the serialized-value tag value.
356: * @return serialized-value tag value
357: */
358: public boolean isSerializedValue() {
359: if (value.serialized == null) {
360: return false;
361: } else {
362: return value.serialized.booleanValue();
363: }
364: }
365:
366: /**
367: * Accessor for the dependent-key attribute indicates that the map's
368: * key contains references that are to be deleted if the referring instance
369: * is deleted.
370: * @return dependent-key tag value
371: */
372: public boolean isDependentKey() {
373: if (key.dependent == null) {
374: return false;
375: } else if (key.classMetaData == null) {
376: return false;
377: } else {
378: return key.dependent.booleanValue();
379: }
380: }
381:
382: /**
383: * Accessor for the dependent-value attribute indicates that the
384: * map's value contains references that are to be deleted if the
385: * referring instance is deleted.
386: * @return dependent-value tag value
387: */
388: public boolean isDependentValue() {
389: if (value.dependent == null) {
390: return false;
391: } else if (value.classMetaData == null) {
392: return false;
393: } else {
394: return value.dependent.booleanValue();
395: }
396: }
397:
398: // ----------------------------- Utilities ---------------------------------
399:
400: /**
401: * Accessor for all ClassMetaData referenced by this array.
402: * @param orderedCMDs List of ordered ClassMetaData objects (added to).
403: * @param referencedCMDs Set of all ClassMetaData objects (added to).
404: * @param dba_vendor_id Vendor ID of the DBA. Used for view addition.
405: * @param clr the ClassLoaderResolver
406: **/
407: void getReferencedClassMetaData(final List orderedCMDs,
408: final Set referencedCMDs, final String dba_vendor_id,
409: final ClassLoaderResolver clr) {
410: AbstractClassMetaData key_cmd = getMetaDataManager()
411: .getMetaDataForClass(key.type, clr);
412: if (key_cmd != null) {
413: key_cmd.getReferencedClassMetaData(orderedCMDs,
414: referencedCMDs, dba_vendor_id, clr);
415: }
416:
417: AbstractClassMetaData value_cmd = getMetaDataManager()
418: .getMetaDataForClass(value.type, clr);
419: if (value_cmd != null) {
420: value_cmd.getReferencedClassMetaData(orderedCMDs,
421: referencedCMDs, dba_vendor_id, clr);
422: }
423: }
424:
425: /**
426: * Returns a string representation of the object.
427: * @param prefix prefix string
428: * @param indent indent string
429: * @return a string representation of the object.
430: */
431: public String toString(String prefix, String indent) {
432: StringBuffer sb = new StringBuffer();
433: sb.append(prefix).append("<map key-type=\"").append(key.type)
434: .append("\" value-type=\"").append(value.type).append(
435: "\"");
436: if (key.embedded != null) {
437: sb.append(" embedded-key=\"").append(key.embedded).append(
438: "\"");
439: }
440: if (value.embedded != null) {
441: sb.append(" embedded-value=\"").append(value.embedded)
442: .append("\"");
443: }
444: if (key.dependent != null) {
445: sb.append(" dependent-key=\"").append(key.dependent)
446: .append("\"");
447: }
448: if (value.dependent != null) {
449: sb.append(" dependent-value=\"").append(value.dependent)
450: .append("\"");
451: }
452: if (key.serialized != null) {
453: sb.append(" serialized-key=\"").append(key.serialized)
454: .append("\"");
455: }
456: if (value.serialized != null) {
457: sb.append(" serialized-value=\"").append(value.serialized)
458: .append("\"");
459: }
460: sb.append(">\n");
461:
462: // Add extensions
463: sb.append(super .toString(prefix + indent, indent));
464:
465: sb.append(prefix).append("</map>\n");
466: return sb.toString();
467: }
468: }
|