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