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.JDOConfigArray
018: 2007 Xuan Baldauf - Support for the ability of users to explictly state that an array whose component type is not PersistenceCapable may still have PersistenceCapable elements. See http://www.jpox.org/servlet/jira/browse/CORE-3274
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.ClassNameConstants;
027: import org.jpox.api.ApiAdapter;
028: import org.jpox.util.StringUtils;
029:
030: /**
031: * Representation of the Meta-Data for an Array.
032: *
033: * @since 1.1
034: * @version $Revision: 1.36 $
035: */
036: public class ArrayMetaData extends ContainerMetaData {
037: /** Representation of the element of the array. */
038: protected ContainerComponent element;
039:
040: /** wether this array may contain PersistenceCapable elements */
041: protected boolean mayContainPersistenceCapableElements;
042:
043: /**
044: * Constructor to create a copy of the passed metadata using the passed parent.
045: * @param parent The parent
046: * @param arrmd The metadata to copy
047: */
048: public ArrayMetaData(AbstractMemberMetaData parent,
049: ArrayMetaData arrmd) {
050: super (parent);
051: element = new ContainerComponent();
052: element.embedded = arrmd.element.embedded;
053: element.serialized = arrmd.element.serialized;
054: element.dependent = arrmd.element.dependent;
055: element.type = arrmd.element.type;
056: element.classMetaData = arrmd.element.classMetaData;
057: }
058:
059: /**
060: * Constructor.
061: * @param parent The parent Field
062: * @param elementType Implementation type(s) of element possible (when it is a reference type).
063: * @param embeddedElement Whether the element is embedded into the join table
064: * @param dependentElement Whether the element is dependent for deletion purposes
065: * @param serializedElement Whether the element is serialised into the join table
066: */
067: public ArrayMetaData(AbstractMemberMetaData parent,
068: String elementType, String embeddedElement,
069: String dependentElement, String serializedElement) {
070: super (parent);
071:
072: if (!StringUtils.isWhitespace(elementType)
073: && elementType.indexOf(',') > 0) {
074: throw new InvalidMetaDataException(LOCALISER, "044140",
075: parent.getName(), parent.getClassName());
076: }
077: element = new ContainerComponent(parent
078: .getAbstractClassMetaData().getPackageName(),
079: elementType, embeddedElement, serializedElement,
080: dependentElement);
081: if (StringUtils.isWhitespace(elementType)) {
082: // Arrays dont default to Object
083: element.type = null;
084: }
085: }
086:
087: /**
088: * Method to populate any defaults, and check the validity of the MetaData.
089: * @param clr ClassLoaderResolver to use in loading any classes
090: * @param primary the primary ClassLoader to use (or null)
091: */
092: public void populate(ClassLoaderResolver clr, ClassLoader primary) {
093: ApiAdapter api = getMetaDataManager().getApiAdapter();
094:
095: // Check the field type and see if it is an array type
096: Class field_type = getMemberMetaData().getType();
097: if (!field_type.isArray()) {
098: throw new InvalidMetaDataException(LOCALISER, "044141",
099: getFieldName(), getMemberMetaData().getClassName(
100: false));
101: }
102:
103: // "embedded-element"
104: if (element.embedded == null) {
105: // Assign default for "embedded-element" based on 18.13.1 of JDO 2 spec
106: // Note : this fails when using in the enhancer since not yet PC
107: Class component_type = field_type.getComponentType();
108: if (getMetaDataManager().getOMFContext().getTypeManager()
109: .isDefaultEmbeddedType(component_type)) {
110: element.embedded = Boolean.TRUE;
111: } else if (api.isPersistable(component_type)
112: || Object.class.isAssignableFrom(component_type)
113: || component_type.isInterface()) {
114: element.embedded = Boolean.FALSE;
115: } else {
116: element.embedded = Boolean.TRUE;
117: }
118: }
119: if (element.embedded == Boolean.FALSE) {
120: // If the user has set a non-PC/non-Interface as not embedded, correct it since not supported.
121: // Note : this fails when using in the enhancer since not yet PC
122: Class component_type = field_type.getComponentType();
123: if (!api.isPersistable(component_type)
124: && !component_type.isInterface()
125: && component_type != java.lang.Object.class) {
126: element.embedded = Boolean.TRUE;
127: }
128: }
129:
130: if (!getMemberMetaData().getAbstractClassMetaData()
131: .getMetaDataManager().isEnhancing()
132: && !getMemberMetaData().isSerialized()) {
133: // Catch situations that JPOX doesnt support
134: if (getMemberMetaData().getJoinMetaData() == null
135: && !api.isPersistable(getMemberMetaData().getType()
136: .getComponentType())
137: && getMetaDataManager().supportsORM()) {
138: // JPOX only supports persisting particular array types as byte-streams (non-Java-serialised)
139: String arrayComponentType = getMemberMetaData()
140: .getType().getComponentType().getName();
141: if (!arrayComponentType
142: .equals(ClassNameConstants.BOOLEAN)
143: && !arrayComponentType
144: .equals(ClassNameConstants.BYTE)
145: && !arrayComponentType
146: .equals(ClassNameConstants.CHAR)
147: && !arrayComponentType
148: .equals(ClassNameConstants.DOUBLE)
149: && !arrayComponentType
150: .equals(ClassNameConstants.FLOAT)
151: && !arrayComponentType
152: .equals(ClassNameConstants.INT)
153: && !arrayComponentType
154: .equals(ClassNameConstants.LONG)
155: && !arrayComponentType
156: .equals(ClassNameConstants.SHORT)
157: && !arrayComponentType
158: .equals(ClassNameConstants.JAVA_LANG_BOOLEAN)
159: && !arrayComponentType
160: .equals(ClassNameConstants.JAVA_LANG_BYTE)
161: && !arrayComponentType
162: .equals(ClassNameConstants.JAVA_LANG_CHARACTER)
163: && !arrayComponentType
164: .equals(ClassNameConstants.JAVA_LANG_DOUBLE)
165: && !arrayComponentType
166: .equals(ClassNameConstants.JAVA_LANG_FLOAT)
167: && !arrayComponentType
168: .equals(ClassNameConstants.JAVA_LANG_INTEGER)
169: && !arrayComponentType
170: .equals(ClassNameConstants.JAVA_LANG_LONG)
171: && !arrayComponentType
172: .equals(ClassNameConstants.JAVA_LANG_SHORT)
173: && !arrayComponentType.equals("BigDecimal")
174: && !arrayComponentType.equals("BigInteger")) {
175: // Impossible to persist an array of a non-PC element without a join table or without serialising the array
176: throw new InvalidMetaDataException(LOCALISER,
177: "044142", getFieldName(),
178: getMemberMetaData().getType()
179: .getComponentType().getName());
180: }
181: }
182: }
183:
184: // Keep a reference to the MetaData for the element
185: MetaDataManager mmgr = getMemberMetaData()
186: .getAbstractClassMetaData().getMetaDataManager();
187: if (element.type != null) {
188: Class elementCls = clr.classForName(element.type, primary);
189:
190: if (api.isPersistable(elementCls)) {
191: mayContainPersistenceCapableElements = true;
192: }
193:
194: element.classMetaData = mmgr.getMetaDataForClassInternal(
195: elementCls, clr);
196: } else {
197: element.type = field_type.getComponentType().getName();
198: element.classMetaData = mmgr.getMetaDataForClassInternal(
199: field_type.getComponentType(), clr);
200: }
201:
202: if (element.classMetaData != null) {
203: mayContainPersistenceCapableElements = true;
204: }
205:
206: setPopulated();
207: }
208:
209: /**
210: * Accessor for the element implementation types (when element is a reference type).
211: * The return can contain comma-separated values.
212: * @return element implementation types
213: */
214: public String getElementType() {
215: return element.type;
216: }
217:
218: /**
219: * Accessor for the Element ClassMetaData
220: * @return element ClassMetaData
221: */
222: public AbstractClassMetaData getElementClassMetaData() {
223: if (element.classMetaData != null
224: && !element.classMetaData.isInitialised()) {
225: element.classMetaData.initialise();
226: }
227: return element.classMetaData;
228: }
229:
230: /**
231: * Returns whether this array may contain PersistenceCapable elements (as indicated by the user).
232: * TODO Remove this. The element-type of the array defines such things and this is not the solution
233: * @return whether this array may contain PersistenceCapable elements
234: */
235: public boolean mayContainPersistenceCapableElements() {
236: return mayContainPersistenceCapableElements;
237: }
238:
239: /**
240: * Accessor for the embedded-element value
241: * @return embedded-element value
242: */
243: public boolean isEmbeddedElement() {
244: if (element.embedded == null) {
245: return false;
246: } else {
247: return element.embedded.booleanValue();
248: }
249: }
250:
251: /**
252: * Accessor for the serialized-element tag value
253: * @return serialized-element tag value
254: */
255: public boolean isSerializedElement() {
256: if (element.serialized == null) {
257: return false;
258: } else {
259: return element.serialized.booleanValue();
260: }
261: }
262:
263: /**
264: * Accessor for The dependent-element attribute indicates that the
265: * collection's element contains a reference that is to be deleted if the
266: * referring instance is deleted.
267: *
268: * @return dependent-element tag value
269: */
270: public boolean isDependentElement() {
271: if (element.dependent == null) {
272: return false;
273: } else {
274: return element.dependent.booleanValue();
275: }
276: }
277:
278: /**
279: * Accessor for all AbstractClassMetaData referenced by this array.
280: * @param orderedCMDs List of ordered AbstractClassMetaData objects (added to).
281: * @param referencedCMDs Set of all AbstractClassMetaData objects (added to).
282: * @param dba_vendor_id Vendor ID of the DBA. Used for view addition.
283: * @param clr the ClassLoaderResolver
284: **/
285: void getReferencedClassMetaData(final List orderedCMDs,
286: final Set referencedCMDs, final String dba_vendor_id,
287: final ClassLoaderResolver clr) {
288: AbstractClassMetaData element_cmd = getMetaDataManager()
289: .getMetaDataForClass(
290: getMemberMetaData().getType()
291: .getComponentType(), clr);
292: if (element_cmd != null) {
293: element_cmd.getReferencedClassMetaData(orderedCMDs,
294: referencedCMDs, dba_vendor_id, clr);
295: }
296: }
297:
298: /**
299: * Returns a string representation of the object.
300: * @param prefix The prefix string
301: * @param indent The indent string
302: * @return a string representation of the object.
303: */
304: public String toString(String prefix, String indent) {
305: StringBuffer sb = new StringBuffer();
306: sb.append(prefix).append("<array");
307: if (element.type != null) {
308: sb.append(" element-type=\"").append(element.type).append(
309: "\"");
310: }
311: if (element.embedded != null) {
312: sb.append(" embedded-element=\"").append(element.embedded)
313: .append("\"");
314: }
315: if (element.serialized != null) {
316: sb.append(" serialized-element=\"").append(
317: element.serialized).append("\"");
318: }
319: if (element.dependent != null) {
320: sb.append(" dependent-element=\"")
321: .append(element.dependent).append("\"");
322: }
323:
324: if (getNoOfExtensions() > 0) {
325: sb.append(">\n");
326:
327: // Add extensions
328: sb.append(super .toString(prefix + indent, indent));
329:
330: sb.append(prefix).append("</array>\n");
331: } else {
332: sb.append(prefix).append("/>\n");
333: }
334:
335: return sb.toString();
336: }
337: }
|