001: package org.apache.torque.om;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.util.ArrayList;
023: import org.apache.commons.lang.ObjectUtils;
024:
025: /**
026: * This class can be used as an ObjectKey to uniquely identify an
027: * object within an application where the key consists of multiple
028: * entities (such a String[] representing a multi-column primary key).
029: *
030: * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
031: * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
032: * @author <a href="mailto:drfish@cox.net">J. Russell Smyth</a>
033: * @version $Id: ComboKey.java 473821 2006-11-11 22:37:25Z tv $
034: */
035: public class ComboKey extends ObjectKey {
036: /**
037: * Serial version
038: */
039: private static final long serialVersionUID = -264927663211141894L;
040:
041: // might want to shift these to TR.props
042:
043: /** The single character used to separate key values in a string. */
044: public static final char SEPARATOR = ':';
045:
046: /** The single character used to separate key values in a string. */
047: public static final String SEPARATOR_STRING = ":";
048:
049: /** The array of the keys */
050: private SimpleKey[] key;
051:
052: /**
053: * Creates an ComboKey whose internal representation will be
054: * set later, through a set method
055: */
056: public ComboKey() {
057: }
058:
059: /**
060: * Creates a ComboKey whose internal representation is an
061: * array of SimpleKeys.
062: *
063: * @param keys the key values
064: */
065: public ComboKey(SimpleKey[] keys) {
066: setValue(keys);
067: }
068:
069: /**
070: * Sets the internal representation to a String array.
071: *
072: * @param keys the key values
073: * @see #toString()
074: */
075: public ComboKey(String keys) {
076: setValue(keys);
077: }
078:
079: /**
080: * Sets the internal representation using a SimpleKey array.
081: *
082: * @param keys the key values
083: */
084: public void setValue(SimpleKey[] keys) {
085: this .key = keys;
086: }
087:
088: /**
089: * Sets the internal representation using a String of the
090: * form produced by the toString method.
091: *
092: * @param keys the key values
093: */
094: public void setValue(String keys) {
095: int startPtr = 0;
096: int indexOfSep = keys.indexOf(SEPARATOR);
097: ArrayList tmpKeys = new ArrayList();
098: while (indexOfSep != -1) {
099: if (indexOfSep == startPtr) {
100: tmpKeys.add(null);
101: } else {
102: char keyType = keys.charAt(startPtr);
103: String keyString = keys.substring(startPtr + 1,
104: indexOfSep);
105:
106: SimpleKey newKey = null;
107: switch (keyType) {
108: case 'N':
109: newKey = new NumberKey(keyString);
110: break;
111: case 'S':
112: newKey = new StringKey(keyString);
113: break;
114: case 'D':
115: try {
116: newKey = new DateKey(keyString);
117: } catch (NumberFormatException nfe) {
118: newKey = new DateKey();
119: }
120: break;
121: default:
122: // unextepcted key type
123: }
124: tmpKeys.add(newKey);
125: }
126: startPtr = indexOfSep + 1;
127: indexOfSep = keys.indexOf(SEPARATOR, startPtr);
128: }
129:
130: this .key = new SimpleKey[tmpKeys.size()];
131: for (int i = 0; i < this .key.length; i++) {
132: this .key[i] = (SimpleKey) tmpKeys.get(i);
133: }
134: }
135:
136: /**
137: * Sets the internal representation using a ComboKey.
138: *
139: * @param keys the key values
140: */
141: public void setValue(ComboKey keys) {
142: setValue((SimpleKey[]) keys.getValue());
143: }
144:
145: /**
146: * Get the underlying object.
147: *
148: * @return the underlying object
149: */
150: public Object getValue() {
151: return key;
152: }
153:
154: /**
155: * This method will return true if the conditions for a looseEquals
156: * are met and in addition no parts of the keys are null.
157: *
158: * @param keyObj the comparison value
159: * @return whether the two objects are equal
160: */
161: public boolean equals(Object keyObj) {
162: boolean isEqual = false;
163:
164: if (key != null) {
165: // check that all keys are not null
166: isEqual = true;
167: SimpleKey[] keys = key;
168: for (int i = 0; i < keys.length && isEqual; i++) {
169: isEqual &= keys[i] != null
170: && keys[i].getValue() != null;
171: }
172:
173: isEqual &= looseEquals(keyObj);
174: }
175:
176: return isEqual;
177: }
178:
179: /**
180: * keyObj is equal to this ComboKey if keyObj is a ComboKey, String,
181: * ObjectKey[], or String[] that contains the same information this key
182: * contains.
183: * For example A String[] might be equal to this key, if this key was
184: * instantiated with a String[] and the arrays contain equal Strings.
185: * Another example, would be if keyObj is an ComboKey that was
186: * instantiated with a ObjectKey[] and this ComboKey was instantiated with
187: * a String[], but the ObjectKeys in the ObjectKey[] were instantiated
188: * with Strings that equal the Strings in this KeyObject's String[]
189: * This method is not as strict as the equals method which does not
190: * allow any null keys parts, while the internal key may not be null
191: * portions may be, and the two object will be considered equal if
192: * their null portions match.
193: *
194: * @param keyObj the comparison value
195: * @return whether the two objects are equal
196: */
197: public boolean looseEquals(Object keyObj) {
198: boolean isEqual = false;
199:
200: if (key != null) {
201: // Checks a compound key (ObjectKey[] or String[]
202: // based) with the delimited String created by the
203: // toString() method. Slightly expensive, but should be less
204: // than parsing the String into its constituents.
205: if (keyObj instanceof String) {
206: isEqual = toString().equals(keyObj);
207: }
208: // check against a ObjectKey. Two keys are equal, if their
209: // internal keys equivalent.
210: else if (keyObj instanceof ComboKey) {
211: SimpleKey[] obj = (SimpleKey[]) ((ComboKey) keyObj)
212: .getValue();
213:
214: SimpleKey[] keys1 = key;
215: SimpleKey[] keys2 = obj;
216: isEqual = keys1.length == keys2.length;
217: for (int i = 0; i < keys1.length && isEqual; i++) {
218: isEqual &= ObjectUtils.equals(keys1[i], keys2[i]);
219: }
220: } else if (keyObj instanceof SimpleKey[]) {
221: SimpleKey[] keys1 = key;
222: SimpleKey[] keys2 = (SimpleKey[]) keyObj;
223: isEqual = keys1.length == keys2.length;
224: for (int i = 0; i < keys1.length && isEqual; i++) {
225: isEqual &= ObjectUtils.equals(keys1[i], keys2[i]);
226: }
227: }
228: }
229: return isEqual;
230: }
231:
232: /**
233: *
234: * @param sb the StringBuffer to append
235: * @see #toString()
236: */
237: public void appendTo(StringBuffer sb) {
238: if (key != null) {
239: SimpleKey[] keys = key;
240: for (int i = 0; i < keys.length; i++) {
241: if (keys[i] != null) {
242: if (keys[i] instanceof StringKey) {
243: sb.append("S");
244: } else if (keys[i] instanceof NumberKey) {
245: sb.append("N");
246: } else if (keys[i] instanceof DateKey) {
247: sb.append("D");
248: } else {
249: // unknown type
250: sb.append("U");
251: }
252: keys[i].appendTo(sb);
253: }
254: // MUST BE ADDED AFTER EACH KEY, IN CASE OF NULL KEY!
255: sb.append(SEPARATOR);
256: }
257: }
258: }
259:
260: /**
261: * if the underlying key array is not null and the first element is
262: * not null this method returns the hashcode of the first element
263: * in the key. Otherwise calls ObjectKey.hashCode()
264: *
265: * @return an <code>int</code> value
266: */
267: public int hashCode() {
268: if (key == null) {
269: return super .hashCode();
270: }
271:
272: SimpleKey sk = key[0];
273: if (sk == null) {
274: return super .hashCode();
275: }
276:
277: return sk.hashCode();
278: }
279:
280: /**
281: * A String that may consist of one section or multiple sections
282: * separated by a colon. <br/>
283: * Each Key is represented by <code>[type N|S|D][value][:]</code>. <p/>
284: * Example: <br/>
285: * the ComboKey(StringKey("key1"), NumberKey(2)) is represented as
286: * <code><b>Skey1:N2:</b></code>
287: *
288: * @return a String representation
289: */
290: public String toString() {
291: StringBuffer sbuf = new StringBuffer();
292: appendTo(sbuf);
293: return sbuf.toString();
294: }
295: }
|