001: /**********************************************************************
002: Copyright (c) 2004 Erik Bengtson 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 Andy Jefferson - added toString(), MetaData docs, javadocs.
018: 2004 Andy Jefferson - added multiple column options
019: ...
020: **********************************************************************/package org.jpox.metadata;
021:
022: import java.util.ArrayList;
023: import java.util.List;
024:
025: import org.jpox.util.StringUtils;
026:
027: /**
028: * Three common strategies for versioning instances are supported by standard
029: * metadata. These include state-comparison, timestamp, and version-number.
030: * <ul>
031: * <li>State-comparison involves comparing the values in specific columns to
032: * determine if the database row was changed.</li>
033: * <li>Timestamp involves comparing the value in a date-time column in the table.
034: * The first time in a transaction the row is updated, the timestamp value is updated to the current time.</li>
035: * <li>Version-number involves comparing the value in a numeric column in the table.
036: * The first time in a transaction the row is updated, the version-number column value is incremented.</li>
037: * </ul>
038: * <p>
039: * There are two forms of version storage.
040: * <ul>
041: * <li>The JDO2 method of adding a surrogate column, using the column/columns/index info in this class</li>
042: * <li>The JPA1 method of using an existing field/method. This uses the "fieldName info only</li>
043: * </ul>
044: * </p>
045: *
046: * @version $Revision: 1.13 $
047: */
048: public class VersionMetaData extends MetaData {
049: /** strategy for generating the version. */
050: private final VersionStrategy versionStrategy;
051:
052: /** column name */
053: private String columnName;
054:
055: /** ColumnMetaData for the version column. */
056: protected final List columns = new ArrayList();
057:
058: /** IndexMetaData for the version column. */
059: protected IndexMetaData indexMetaData;
060:
061: /** Indexed value. */
062: protected IndexedValue indexed = null;
063:
064: /** Name of the field that contains the version (if not generating a surrogate column). */
065: protected String fieldName = null;
066:
067: // -------------------------------------------------------------------------
068: // Fields below here are not represented in the output MetaData. They are
069: // for use internally in the operation of the JDO system. The majority are
070: // for convenience to save iterating through the fields since the fields
071: // are fixed once populated.
072:
073: /** Contains the metadata for column. */
074: protected ColumnMetaData[] columnMetaData;
075:
076: /**
077: * Constructor for a surrogate version added by JPOX (JDO).
078: * @param versionStrategy Strategy for versioning
079: * @param columnName Column name
080: * @param indexed The indexed tag
081: */
082: public VersionMetaData(final String versionStrategy,
083: final String columnName, final String indexed) {
084: super (null); // Don't bother with parent
085:
086: if (StringUtils.isWhitespace(versionStrategy)
087: || VersionStrategy.getVersionStrategy(versionStrategy) == null) {
088: throw new RuntimeException(LOCALISER.msg("044156"));
089: }
090: this .versionStrategy = VersionStrategy
091: .getVersionStrategy(versionStrategy);
092: this .columnName = (StringUtils.isWhitespace(columnName) ? null
093: : columnName);
094: this .indexed = IndexedValue.getIndexedValue(indexed);
095: }
096:
097: /**
098: * Constructor for a version stored in a field (JPA).
099: * @param versionStrategy Strategy for versioning
100: * @param fieldName name of the field/property which stores the version
101: */
102: public VersionMetaData(final String versionStrategy,
103: final String fieldName) {
104: super (null); // Don't bother with parent
105:
106: if (StringUtils.isWhitespace(versionStrategy)
107: || VersionStrategy.getVersionStrategy(versionStrategy) == null) {
108: throw new RuntimeException(LOCALISER.msg("044156"));
109: }
110: this .versionStrategy = VersionStrategy
111: .getVersionStrategy(versionStrategy);
112: this .fieldName = (StringUtils.isWhitespace(fieldName) ? null
113: : fieldName);
114: }
115:
116: /**
117: * Initialisation method. This should be called AFTER using the populate
118: * method if you are going to use populate. It creates the internal
119: * convenience arrays etc needed for normal operation.
120: */
121: public void initialise() {
122: if (hasExtension("field-name")) {
123: // User has provided JPOX extension "field-name" meaning that we store the version in the column
124: // for the specified field (like in JPA)
125: String val = getValueForExtension("field-name");
126: if (!StringUtils.isWhitespace(val)) {
127: this .fieldName = val;
128: this .columnName = null;
129: }
130: }
131:
132: if (fieldName == null) {
133: // Interpret the "indexed" value to create our IndexMetaData where it wasn't specified that way
134: if (indexMetaData == null && columnMetaData != null
135: && indexed != null && indexed != IndexedValue.FALSE) {
136: indexMetaData = new IndexMetaData(null, null,
137: (indexed == IndexedValue.UNIQUE) ? "true"
138: : "false");
139: for (int i = 0; i < columnMetaData.length; i++) {
140: indexMetaData.addColumn(columnMetaData[i]);
141: }
142: }
143: if (indexMetaData != null) {
144: indexMetaData.initialise();
145: }
146:
147: // Cater for user specifying column name, or columns
148: if (columns.size() == 0 && columnName != null) {
149: columnMetaData = new ColumnMetaData[1];
150: columnMetaData[0] = new ColumnMetaData(this , columnName);
151: } else {
152: columnMetaData = new ColumnMetaData[columns.size()];
153: for (int i = 0; i < columnMetaData.length; i++) {
154: columnMetaData[i] = (ColumnMetaData) columns.get(i);
155: columnMetaData[i].initialise();
156: }
157: }
158: }
159: }
160:
161: // ------------------------------ Accessors --------------------------------
162:
163: /**
164: * Acessor for the columns
165: * @return Returns the columnMetaData.
166: */
167: public final ColumnMetaData[] getColumnMetaData() {
168: return columnMetaData;
169: }
170:
171: /**
172: * Accessor for versionStrategy
173: * @return Returns the versionStrategy.
174: */
175: public final VersionStrategy getVersionStrategy() {
176: return versionStrategy;
177: }
178:
179: /**
180: * Accessor for indexMetaData
181: * @return Returns the indexMetaData.
182: */
183: public final IndexMetaData getIndexMetaData() {
184: return indexMetaData;
185: }
186:
187: /**
188: * Accessor for the name of the field/property that stores the version.
189: * @return Name of the field/property storing the version.
190: */
191: public final String getFieldName() {
192: return fieldName;
193: }
194:
195: // ------------------------------ Mutators ---------------------------------
196:
197: /**
198: * Add a new ColumnMetaData element
199: * @param colmd The ColumnMetaData to add
200: */
201: public void addColumn(ColumnMetaData colmd) {
202: columns.add(colmd);
203: colmd.parent = this ;
204: columnMetaData = new ColumnMetaData[columns.size()];
205: for (int i = 0; i < columnMetaData.length; i++) {
206: columnMetaData[i] = (ColumnMetaData) columns.get(i);
207: }
208: }
209:
210: /**
211: * Mutator for the index MetaData
212: * @param indexMetaData The indexMetaData to set.
213: */
214: public final void setIndexMetaData(IndexMetaData indexMetaData) {
215: this .indexMetaData = indexMetaData;
216: }
217:
218: // ------------------------------ Utilities --------------------------------
219:
220: /**
221: * Returns a string representation of the object using a prefix
222: * This can be used as part of a facility to output a MetaData file.
223: * @param prefix prefix string
224: * @param indent indent string
225: * @return a string representation of the object.
226: */
227: public String toString(String prefix, String indent) {
228: // Field needs outputting so generate metadata
229: StringBuffer sb = new StringBuffer();
230: sb.append(prefix).append(
231: "<version strategy=\""
232: + versionStrategy.toString()
233: + "\""
234: + (indexed != null ? (" indexed=\""
235: + indexed.toString() + "\"") : "")
236: + ">\n");
237:
238: // Add columns
239: for (int i = 0; i < columns.size(); i++) {
240: ColumnMetaData c = (ColumnMetaData) columns.get(i);
241: sb.append(c.toString(prefix + indent, indent));
242: }
243:
244: // Add index
245: if (indexMetaData != null) {
246: sb.append(indexMetaData.toString(prefix + indent, indent));
247: }
248:
249: // Add extensions
250: sb.append(super .toString(prefix + indent, indent));
251:
252: sb.append(prefix).append("</version>\n");
253: return sb.toString();
254: }
255: }
|