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: ...
018: **********************************************************************/package org.jpox.metadata;
019:
020: import org.jpox.ClassLoaderResolver;
021: import org.jpox.ClassNameConstants;
022: import org.jpox.exceptions.JPOXUserException;
023: import org.jpox.store.DatastoreField;
024: import org.jpox.util.Localiser;
025: import org.jpox.util.StringUtils;
026:
027: /**
028: * Utilities needed for the processing of MetaData.
029: *
030: * @version $Revision: 1.49 $
031: */
032: public class MetaDataUtils {
033: protected static final Localiser LOCALISER = Localiser
034: .getInstance("org.jpox.metadata.Localisation");
035:
036: private static MetaDataUtils instance;
037:
038: /**
039: * Gets an instance of MetaDataUtils
040: * @return a singleton instance of MetaDataUtils
041: */
042: public static synchronized MetaDataUtils getInstance() {
043: if (instance == null) {
044: instance = new MetaDataUtils();
045: }
046: return instance;
047: }
048:
049: /**
050: * Protected constructor to prevent outside instantiation
051: */
052: protected MetaDataUtils() {
053: }
054:
055: /**
056: * Convenience method to determine if an array is storable in a single column as a byte
057: * array.
058: * @param fmd The field
059: * @return Whether this is an array that can be stored in a single column as non-serialised
060: */
061: public boolean arrayStorableAsByteArrayInSingleColumn(
062: AbstractMemberMetaData fmd) {
063: if (fmd == null || !fmd.hasArray()) {
064: return false;
065: }
066:
067: String arrayComponentType = fmd.getType().getComponentType()
068: .getName();
069: if (arrayComponentType.equals(ClassNameConstants.BOOLEAN)
070: || arrayComponentType.equals(ClassNameConstants.BYTE)
071: || arrayComponentType.equals(ClassNameConstants.CHAR)
072: || arrayComponentType.equals(ClassNameConstants.DOUBLE)
073: || arrayComponentType.equals(ClassNameConstants.FLOAT)
074: || arrayComponentType.equals(ClassNameConstants.INT)
075: || arrayComponentType.equals(ClassNameConstants.LONG)
076: || arrayComponentType.equals(ClassNameConstants.SHORT)
077: || arrayComponentType
078: .equals(ClassNameConstants.JAVA_LANG_BOOLEAN)
079: || arrayComponentType
080: .equals(ClassNameConstants.JAVA_LANG_BYTE)
081: || arrayComponentType
082: .equals(ClassNameConstants.JAVA_LANG_CHARACTER)
083: || arrayComponentType
084: .equals(ClassNameConstants.JAVA_LANG_DOUBLE)
085: || arrayComponentType
086: .equals(ClassNameConstants.JAVA_LANG_FLOAT)
087: || arrayComponentType
088: .equals(ClassNameConstants.JAVA_LANG_INTEGER)
089: || arrayComponentType
090: .equals(ClassNameConstants.JAVA_LANG_LONG)
091: || arrayComponentType
092: .equals(ClassNameConstants.JAVA_LANG_SHORT)
093: || arrayComponentType
094: .equals(ClassNameConstants.JAVA_MATH_BIGDECIMAL)
095: || arrayComponentType
096: .equals(ClassNameConstants.JAVA_MATH_BIGINTEGER)) {
097: // These types can be stored as a single-column but setting the bytes only
098: return true;
099: }
100:
101: // All other arrays must be serialised into a single column
102: return false;
103: }
104:
105: /**
106: * Convenience method that returns if a field stores a First-Class object (FCO).
107: * @param fmd MetaData for the field
108: * @param clr ClassLoaderResolver resolver
109: * @return Whether it stores a FCO
110: */
111: public boolean storesFCO(AbstractMemberMetaData fmd,
112: ClassLoaderResolver clr) {
113: if (fmd == null) {
114: return false;
115: }
116:
117: MetaDataManager mgr = fmd.getAbstractClassMetaData()
118: .getMetaDataManager();
119: if (fmd.isSerialized() || fmd.isEmbedded()) {
120: // Serialised or embedded fields have no FCO
121: return false;
122: } else if (fmd.hasCollection()
123: && !fmd.getCollection().isSerializedElement()
124: && !fmd.getCollection().isEmbeddedElement()) {
125: if (fmd.getCollection().getElementClassMetaData() != null) {
126: // Collection of PC elements
127: return true;
128: } else {
129: String elementType = fmd.getCollection()
130: .getElementType();
131: Class elementCls = clr.classForName(elementType);
132: if (elementCls != null
133: && mgr.getOMFContext().getTypeManager()
134: .isReferenceType(elementCls)
135: && mgr.getMetaDataForImplementationOfReference(
136: elementCls, null, clr) != null) {
137: // Collection of reference type for FCOs
138: return true;
139: }
140: }
141: } else if (fmd.hasMap()) {
142: if (fmd.getMap().getKeyClassMetaData() != null
143: && !fmd.getMap().isEmbeddedKey()
144: && !fmd.getMap().isSerializedKey()) {
145: // Map of PC keys
146: return true;
147: } else {
148: String keyType = fmd.getMap().getKeyType();
149: Class keyCls = clr.classForName(keyType);
150: if (keyCls != null
151: && mgr.getOMFContext().getTypeManager()
152: .isReferenceType(keyCls)
153: && mgr.getMetaDataForImplementationOfReference(
154: keyCls, null, clr) != null) {
155: // Map with keys of reference type for FCOs
156: return true;
157: }
158: }
159:
160: if (fmd.getMap().getValueClassMetaData() != null
161: && !fmd.getMap().isEmbeddedValue()
162: && !fmd.getMap().isSerializedValue()) {
163: // Map of PC values
164: return true;
165: } else {
166: String valueType = fmd.getMap().getValueType();
167: Class valueCls = clr.classForName(valueType);
168: if (valueCls != null
169: && mgr.getOMFContext().getTypeManager()
170: .isReferenceType(valueCls)
171: && mgr.getMetaDataForImplementationOfReference(
172: valueCls, null, clr) != null) {
173: // Map with values of reference type for FCOs
174: return true;
175: }
176: }
177: } else if (fmd.hasArray()
178: && !fmd.getArray().isSerializedElement()
179: && !fmd.getArray().isEmbeddedElement()) {
180: if (fmd.getMetaDataManager().getApiAdapter().isPersistable(
181: fmd.getType().getComponentType())) {
182: // PersistenceCapable[]
183: return true;
184: }
185: } else {
186: // 1-1 relation with PC
187: if (mgr.getOMFContext().getTypeManager().isReferenceType(
188: fmd.getType())
189: && mgr.getMetaDataForImplementationOfReference(fmd
190: .getType(), null, clr) != null) {
191: // Reference type for an FCO
192: return true;
193: }
194: if (mgr.getMetaDataForClass(fmd.getType(), clr) != null) {
195: return true;
196: }
197: }
198: return false;
199: }
200:
201: /**
202: * Convenience method that splits a comma-separated list of values into a String array (removing whitespace).
203: * @param attr The attribute value
204: * @return The string components
205: */
206: public String[] getValuesForCommaSeparatedAttribute(String attr) {
207: if (attr == null || attr.length() == 0) {
208: return null;
209: }
210:
211: String[] values = StringUtils.split(attr, ",");
212:
213: // Remove any whitespace around the values
214: if (values != null) {
215: for (int i = 0; i < values.length; i++) {
216: values[i] = values[i].trim();
217: }
218: }
219: return values;
220: }
221:
222: /**
223: * Convenience method to return the class names of the available implementation types for
224: * an interface/Object field, given its required role. Removes all duplicates from the list.
225: * @param fmd MetaData for the field
226: * @param fieldRole The role of the field
227: * @param clr the ClassLoaderResolver
228: * @return Names of the classes of the possible implementations of this interface/Object
229: * @throws JPOXUserException if no implementation types are found for the reference type field
230: */
231: public String[] getImplementationNamesForReferenceField(
232: AbstractMemberMetaData fmd, int fieldRole,
233: ClassLoaderResolver clr) {
234: // Check the JDO2 standard attribute for implementation types
235: String[] implTypes = null;
236: // TODO Support specification of collection/map implementation types using element-type,key-type,value-type
237: /*if (DatastoreField.ROLE_COLLECTION_ELEMENT == role)
238: {
239: implTypeStr = fmd.getCollection().getElementType();
240: }
241: else if (DatastoreField.ROLE_MAP_KEY == role)
242: {
243: implTypeStr = fmd.getMap().getKeyImplementationType();
244: }
245: else if (DatastoreField.ROLE_MAP_VALUE == role)
246: {
247: implTypeStr = fmd.getMap().getValueImplementationType();
248: }
249: else */
250: if (DatastoreField.ROLE_ARRAY_ELEMENT == fieldRole) {
251: // TODO Change ElementMetaData to have elementTypes
252: String implTypeStr = fmd.getArray().getElementType();
253: if (implTypeStr != null) {
254: implTypes = getValuesForCommaSeparatedAttribute(implTypeStr);
255: }
256: } else {
257: implTypes = fmd.getFieldTypes();
258: }
259:
260: // Check if the user has defined an interface type being "implemented by" an interface ("persistent-interface")
261: if (implTypes != null && implTypes.length == 1) {
262: // Single class/interface name specified
263: Class implCls = clr.classForName(implTypes[0].trim());
264: if (implCls.isInterface()) {
265: // The specified "implementation" is itself an interface so assume it is a "persistent-interface"
266: implTypes = fmd.getAbstractClassMetaData()
267: .getMetaDataManager()
268: .getClassesImplementingInterface(implTypes[0],
269: clr);
270: }
271: }
272:
273: if (implTypes == null) {
274: // No implementation(s) specified using JDO2 mechanism, so fallback to JPOX 1.1 extensions
275: if (DatastoreField.ROLE_COLLECTION_ELEMENT == fieldRole) {
276: implTypes = fmd
277: .getValuesForExtension("implementation-classes");
278: } else if (DatastoreField.ROLE_MAP_KEY == fieldRole) {
279: implTypes = fmd
280: .getValuesForExtension("key-implementation-classes");
281: } else if (DatastoreField.ROLE_MAP_VALUE == fieldRole) {
282: implTypes = fmd
283: .getValuesForExtension("value-implementation-classes");
284: } else if (DatastoreField.ROLE_ARRAY_ELEMENT == fieldRole) {
285: implTypes = fmd
286: .getValuesForExtension("implementation-classes");
287: } else {
288: implTypes = fmd
289: .getValuesForExtension("implementation-classes");
290: }
291: }
292:
293: if (implTypes == null) {
294: // Nothing specified, so check if it is an interface and if so use the <implements> definition to get some types
295: String type = null;
296: if (fmd.hasCollection()
297: && fieldRole == DatastoreField.ROLE_COLLECTION_ELEMENT) {
298: type = fmd.getCollection().getElementType();
299: } else if (fmd.hasMap()
300: && fieldRole == DatastoreField.ROLE_MAP_KEY) {
301: type = fmd.getMap().getKeyType();
302: } else if (fmd.hasMap()
303: && fieldRole == DatastoreField.ROLE_MAP_VALUE) {
304: type = fmd.getMap().getValueType();
305: } else if (fmd.hasArray()
306: && fieldRole == DatastoreField.ROLE_ARRAY_ELEMENT) {
307: type = fmd.getType().getComponentType().getName();
308: } else {
309: type = fmd.getTypeName();
310: }
311:
312: if (!type.equals(ClassNameConstants.Object)) {
313: implTypes = fmd.getAbstractClassMetaData()
314: .getMetaDataManager()
315: .getClassesImplementingInterface(type, clr);
316: }
317:
318: if (implTypes == null) {
319: // Generate error since no implementations available
320: throw new JPOXUserException(LOCALISER.msg("044161", fmd
321: .getFullFieldName(), type));
322: }
323: }
324:
325: // Remove all duplicates from the list but retain the original ordering
326: int noOfDups = 0;
327: for (int i = 0; i < implTypes.length; i++) {
328: for (int j = 0; j < i; j++) {
329: if (implTypes[j].equals(implTypes[i])) {
330: noOfDups++;
331: break;
332: }
333: }
334: }
335: String[] impls = new String[implTypes.length - noOfDups];
336: int n = 0;
337: for (int i = 0; i < implTypes.length; i++) {
338: boolean dup = false;
339: for (int j = 0; j < i; j++) {
340: if (implTypes[j].equals(implTypes[i])) {
341: dup = true;
342: break;
343: }
344: }
345: if (!dup) {
346: impls[n++] = implTypes[i];
347: }
348: }
349:
350: return impls;
351: }
352: }
|