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: 2003 Kikuchi Kousuke - org.jpox.enhancer.conf.JDOConfigField
018: ...
019: **********************************************************************/package org.jpox.metadata;
020:
021: import org.jpox.util.StringUtils;
022:
023: /**
024: * Representation of the Meta-Data for a field of a class.
025: *
026: * <H3>Lifecycle state</H3>
027: * An object of this type has 2 lifecycle states. The first is the raw
028: * constructed object which represents pure MetaData (maybe from a MetaData
029: * file). The second is a "populated" object which represents MetaData for a
030: * Field of a class with the metadata aligned to be appropriate for that Field.
031: *
032: * <H3>Containers</H3>
033: * Each field can represent a container. The container can be an array, a
034: * Collection or a Map. The field type must be of the correct type to represent
035: * these.
036: *
037: * <H3>JPOX Management</H3>
038: * Each field can be managed by JPOX or not. The class provides a method for
039: * identifying if a field is managed by JPOX (<I>isJdoField()</I>). If a field
040: * is managed by JPOX, it will have a field "id" (within its class). In a class
041: * the field "id" will start at 0 (for the first field, in alphabetical order).
042: *
043: * <H3>MetaData Element</H3>
044: * The MetaData element represented here is as follows
045: * <PRE>
046: * <!ELEMENT field (extension*, (collection|map|array|(column*))?, join?, element?,
047: * key?, value?, fetch-group*, order?, embedded?, index?, unique?, foreign-key?,
048: * delete-action?, extension*)?>
049: * <!ATTLIST field name CDATA #REQUIRED>
050: * <!ATTLIST field persistence-modifier (persistent|transactional|none)
051: * #IMPLIED>
052: * <!ATTLIST field table CDATA #IMPLIED>
053: * <!ATTLIST field null-value (exception|default|none) 'none'>
054: * <!ATTLIST field default-fetch-group (true|false) #IMPLIED>
055: * <!ATTLIST field embedded (true|false) #IMPLIED>
056: * <!ATTLIST field serialized (true|false) #IMPLIED>
057: * <!ATTLIST field dependent (true|false) #IMPLIED>
058: * <!ATTLIST field indexed (true|false|unique) #IMPLIED>
059: * <!ATTLIST field unique (true|false) #IMPLIED>
060: * <!ATTLIST field load-fetch-group CDATA #IMPLIED>
061: * <!ATTLIST field recursion-depth CDATA #IMPLIED>
062: * <!ATTLIST field primary-key (true|false) 'false'>
063: * <!ATTLIST field mapped-by CDATA #IMPLIED>
064: * <!ATTLIST field value-strategy CDATA #IMPLIED>
065: * <!ATTLIST field delete-action (restrict|cascade|null|default|none) #IMPLIED>
066: * <!ATTLIST field sequence CDATA #IMPLIED>
067: * <!ATTLIST field field-type CDATA #IMPLIED>
068: * </PRE>
069: *
070: * @since 1.1
071: * @version $Revision: 1.83 $
072: */
073: public class FieldMetaData extends AbstractMemberMetaData {
074: /**
075: * Convenience constructor taking defaults
076: * @param parent Parent component
077: * @param name Name of the field
078: */
079: public FieldMetaData(MetaData parent, final String name) {
080: super (parent, name);
081: }
082:
083: /**
084: * Convenience constructor to copy the specification from the passed field.
085: * This is used when we have an overriding field and we make a copy of the baseline
086: * field as a starting point.
087: * @param parent The parent
088: * @param fmd The field to copy
089: */
090: public FieldMetaData(MetaData parent, AbstractMemberMetaData fmd) {
091: super (parent, fmd);
092: }
093:
094: /**
095: * Constructor. Saves the MetaData with the specified values. The object is
096: * then in an "unpopulated" state. It can become "populated" by calling the
097: * <B>populate()</B> method which compares it against the field it is to
098: * represent and updates any unset attributes and flags up any errors.
099: * @param parent parent MetaData instance
100: * @param name field name
101: * @param pk attribute primary-key value
102: * @param modifier attribute persistence-modifier value
103: * @param defaultFetchGroup attribute default-fetch-group value
104: * @param nullValue attribute null-value value
105: * @param embedded attribute embedded value
106: * @param serialized attribute serialized value
107: * @param dependent attribute dependent value
108: * @param mappedBy attribute mapped-by value
109: * @param column attribute column value
110: * @param table attribute table value
111: * @param catalog attribute catalog value
112: * @param schema attribute schema value
113: * @param deleteAction attribute delete-action value
114: * @param indexed Whether this is indexed
115: * @param unique Apply a unique constraint
116: * @param recursionDepth The depth of fetch to use when recursing
117: * @param loadFetchGroup Name of the additional fetch group to use when loading
118: * @param valueStrategy attribute value-strategy value
119: * @param sequence attribute sequence value
120: * @param fieldType Implementation type(s) for field.
121: */
122: public FieldMetaData(MetaData parent, final String name,
123: final String pk, final String modifier,
124: final String defaultFetchGroup, final String nullValue,
125: final String embedded, final String serialized,
126: final String dependent, final String mappedBy,
127: final String column, final String table,
128: final String catalog, final String schema,
129: final String deleteAction, final String indexed,
130: final String unique, final String recursionDepth,
131: final String loadFetchGroup, final String valueStrategy,
132: final String sequence, final String fieldType) {
133: super (parent, name, pk, modifier, defaultFetchGroup, nullValue,
134: embedded, serialized, dependent, mappedBy, column,
135: table, catalog, schema, deleteAction, indexed, unique,
136: recursionDepth, loadFetchGroup, valueStrategy,
137: sequence, fieldType);
138: }
139:
140: /**
141: * Whether this uses getter/setter accessors (Property) or
142: * used field based access (Field)
143: * @return true if this is a property
144: */
145: public boolean isProperty() {
146: return false;
147: }
148:
149: /**
150: * Returns a string representation of the object using a prefix
151: * This can be used as part of a facility to output a MetaData file.
152: * @param prefix prefix string
153: * @param indent indent string
154: * @return a string representation of the object.
155: */
156: public String toString(String prefix, String indent) {
157: // If this field is static or final, don't bother with MetaData since JPOX will ignore it anyway.
158: if (isStatic() || isFinal()) {
159: return "";
160: }
161:
162: // Field needs outputting so generate metadata
163: StringBuffer sb = new StringBuffer();
164: sb.append(prefix).append("<field name=\"" + name + "\"");
165: if (persistenceModifier != null
166: && !StringUtils.isWhitespace(persistenceModifier
167: .toString())) {
168: sb.append("\n").append(prefix).append(
169: " persistence-modifier=\""
170: + persistenceModifier + "\"");
171: }
172: if (!StringUtils.isWhitespace(table)) {
173: sb.append("\n").append(prefix).append(
174: " table=\"" + table + "\"");
175: }
176: if (primaryKey != null && primaryKey.booleanValue()) {
177: sb.append("\n").append(prefix).append(
178: " primary-key=\"" + primaryKey + "\"");
179: }
180: sb.append("\n").append(prefix).append(
181: " null-value=\"" + nullValue + "\"");
182: if (defaultFetchGroup != null
183: && !StringUtils.isWhitespace(defaultFetchGroup
184: .toString())) {
185: sb.append("\n").append(prefix).append(
186: " default-fetch-group=\"" + defaultFetchGroup
187: + "\"");
188: }
189: if (embedded != null
190: && !StringUtils.isWhitespace(embedded.toString())) {
191: sb.append("\n").append(prefix).append(
192: " embedded=\"" + embedded + "\"");
193: }
194: if (serialized != null
195: && !StringUtils.isWhitespace(serialized.toString())) {
196: sb.append("\n").append(prefix).append(
197: " serialized=\"" + serialized + "\"");
198: }
199: if (dependent != null) {
200: sb.append("\n").append(prefix).append(
201: " dependent=\"" + dependent + "\"");
202: }
203: if (mappedBy != null) {
204: sb.append("\n").append(prefix).append(
205: " mapped-by=\"" + mappedBy + "\"");
206: }
207: if (fieldTypes != null) {
208: sb.append("\n").append(prefix).append(
209: " field-type=\"");
210: for (int i = 0; i < fieldTypes.length; i++) {
211: sb.append(fieldTypes[i]);
212: }
213: sb.append("\"");
214: }
215: if (loadFetchGroup != null) {
216: sb.append("\n").append(prefix).append(
217: " load-fetch-group=\"" + loadFetchGroup
218: + "\"");
219: }
220: if (recursionDepth != DEFAULT_RECURSION_DEPTH
221: && recursionDepth != UNDEFINED_RECURSION_DEPTH) {
222: sb.append("\n").append(prefix)
223: .append(
224: " recursion-depth=\""
225: + recursionDepth + "\"");
226: }
227: if (valueStrategy != null) {
228: sb.append("\n").append(prefix).append(
229: " value-strategy=\"" + valueStrategy + "\"");
230: }
231: if (sequence != null) {
232: sb.append("\n").append(prefix).append(
233: " sequence=\"" + sequence + "\"");
234: }
235: sb.append(">\n");
236:
237: // Add field containers
238: if (container != null) {
239: if (container instanceof CollectionMetaData) {
240: CollectionMetaData c = (CollectionMetaData) container;
241: sb.append(c.toString(prefix + indent, indent));
242: } else if (container instanceof ArrayMetaData) {
243: ArrayMetaData c = (ArrayMetaData) container;
244: sb.append(c.toString(prefix + indent, indent));
245: } else if (container instanceof MapMetaData) {
246: MapMetaData c = (MapMetaData) container;
247: sb.append(c.toString(prefix + indent, indent));
248: }
249: }
250:
251: // Add columns
252: if (columnMetaData != null) {
253: for (int i = 0; i < columnMetaData.length; i++) {
254: sb.append(columnMetaData[i].toString(prefix + indent,
255: indent));
256: }
257: }
258:
259: // Add join
260: if (joinMetaData != null) {
261: sb.append(joinMetaData.toString(prefix + indent, indent));
262: }
263:
264: // Add element
265: if (elementMetaData != null) {
266: sb
267: .append(elementMetaData.toString(prefix + indent,
268: indent));
269: }
270:
271: // Add key
272: if (keyMetaData != null) {
273: sb.append(keyMetaData.toString(prefix + indent, indent));
274: }
275:
276: // Add value
277: if (valueMetaData != null) {
278: sb.append(valueMetaData.toString(prefix + indent, indent));
279: }
280:
281: // TODO Add fetch-groups
282:
283: // Add order
284: if (orderMetaData != null) {
285: sb.append(orderMetaData.toString(prefix + indent, indent));
286: }
287:
288: // Add embedded
289: if (embeddedMetaData != null) {
290: sb.append(embeddedMetaData
291: .toString(prefix + indent, indent));
292: }
293:
294: // Add index
295: if (indexMetaData != null) {
296: sb.append(indexMetaData.toString(prefix + indent, indent));
297: }
298:
299: // Add unique
300: if (uniqueMetaData != null) {
301: sb.append(uniqueMetaData.toString(prefix + indent, indent));
302: }
303:
304: // Add foreign-key
305: if (foreignKeyMetaData != null) {
306: sb.append(foreignKeyMetaData.toString(prefix + indent,
307: indent));
308: }
309:
310: // Add extensions
311: sb.append(super .toString(prefix + indent, indent));
312:
313: sb.append(prefix).append("</field>\n");
314: return sb.toString();
315: }
316:
317: /**
318: * Comparator method. This allows the ClassMetaData to search for a
319: * FieldMetaData with a particular name.
320: * @param o The object to compare against
321: * @return The comparison result
322: */
323: public int compareTo(Object o) {
324: if (o instanceof AbstractMemberMetaData) {
325: AbstractMemberMetaData c = (AbstractMemberMetaData) o;
326: return this .name.compareTo(c.name);
327: } else if (o instanceof String) {
328: return this .name.compareTo((String) o);
329: } else if (o == null) {
330: throw new ClassCastException("object is null");
331: }
332: throw new ClassCastException(this .getClass().getName() + " != "
333: + o.getClass().getName());
334: }
335: }
|