001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.common;
012:
013: import com.versant.core.metadata.ClassMetaData;
014: import com.versant.core.metadata.FieldMetaData;
015: import com.versant.core.metadata.MDStatics;
016: import com.versant.core.metadata.ModelMetaData;
017: import com.versant.core.util.FastParser;
018: import com.versant.core.util.OIDObjectInput;
019: import com.versant.core.util.OIDObjectOutput;
020:
021: import java.io.*;
022: import java.math.BigDecimal;
023: import java.math.BigInteger;
024: import java.util.Date;
025: import java.util.Locale;
026:
027: /**
028: * This is an OID implementation suitable for use with any PC class. It is
029: * intended for use during development when the OID class generating code is
030: * broken. The meta data OID factory method can just return these for all
031: * PC classes. Performance is not important for this class as it will not
032: * be used in a production release.
033: */
034: public abstract class GenericOID implements OID, Externalizable
035:
036: {
037:
038: protected ClassMetaData cmd;
039: protected boolean resolved;
040: protected Object[] pk;
041:
042: public GenericOID() {
043: }
044:
045: public GenericOID(ClassMetaData cmd, boolean resolved) {
046: if (cmd == null) {
047: throw BindingSupportImpl.getInstance().internal(
048: "The supplied cmd is null");
049: }
050: this .cmd = cmd;
051: this .resolved = resolved;
052: pk = new Object[cmd.pkFields == null ? 1 : cmd.pkFields.length];
053: }
054:
055: /**
056: * This is for the GenericState implementation.
057: */
058: public Object[] getPk() {
059: return pk;
060: }
061:
062: /**
063: * Do we know the actual class of the object we are referencing? An
064: * OID may be created from a reference to a base class. The actual
065: * class of the object referenced might only be detirmined when its
066: * State is fetched from the store.
067: *
068: * @see #resolve
069: */
070: public boolean isResolved() {
071: return resolved;
072: }
073:
074: /**
075: * Resolve this OID from the state. This will update our class index
076: * to reflect the state. It is a NOP to call this on an already
077: * resolved OID.
078: *
079: * @see #isResolved
080: */
081: public void resolve(State state) {
082: if (!resolved) {
083: cmd = state.getClassMetaData();
084: resolved = true;
085: }
086: }
087:
088: public int getClassIndex() {
089: return cmd.index;
090: }
091:
092: public ClassMetaData getClassMetaData() {
093: if (!resolved) {
094: throw BindingSupportImpl.getInstance().internal(
095: "Called 'getClassMetaData()' on unresolved oid");
096: }
097: return cmd;
098: }
099:
100: public ClassMetaData getAvailableClassMetaData() {
101: return cmd;
102: }
103:
104: public ClassMetaData getBaseClassMetaData() {
105: return cmd.top;
106: }
107:
108: public int getIdentityType() {
109: return cmd.top.identityType;
110: }
111:
112: public void copyKeyFields(Object[] data) {
113: for (int i = 0; i < pk.length; i++)
114: pk[i] = data[i];
115: }
116:
117: /**
118: * Return a copy of the oid.
119: */
120: public OID copy() {
121: GenericOID copy = newInstance();
122: copy.resolved = resolved;
123: copy.cmd = cmd;
124: copy.pk = pk;
125: return copy;
126: }
127:
128: protected abstract GenericOID newInstance();
129:
130: public void fillFromPK(Object pk) {
131: FieldMetaData[] pkFields = cmd.pkFields;
132: Object[] pkValues = new Object[pkFields.length];
133: for (int i = 0; i < pkFields.length; i++) {
134: try {
135: pkValues[i] = pk.getClass().getField(
136: pkFields[i].getPkFieldName()).get(pk);
137: } catch (Exception e) {
138: throw BindingSupportImpl.getInstance().internal(
139: e.getMessage(), e);
140: }
141: }
142: copyKeyFields(pkValues);
143: }
144:
145: public OID fillFromIDObject(Object id) {
146: pk = new Object[cmd.pkFields.length];
147: if (cmd.objectIdClass == null) {
148: if (cmd.pkFields.length != 1) {
149: throw BindingSupportImpl
150: .getInstance()
151: .invalidOperation(
152: "Classes with application can only have a single pk "
153: + "field if a ApplicationId class is not specified");
154: }
155: pk[0] = id;
156: } else {
157: FieldMetaData[] pkFields = cmd.pkFields;
158: Object[] pkValues = pk;
159: for (int i = 0; i < pkFields.length; i++) {
160: try {
161: pkValues[i] = pk.getClass().getField(
162: pkFields[i].getPkFieldName()).get(pk);
163: } catch (Exception e) {
164: throw BindingSupportImpl.getInstance().internal(
165: e.getMessage(), e);
166: }
167: }
168: }
169: return this ;
170: }
171:
172: /**
173: * Fill this OID from its toString. The classid will have already been
174: * parsed out with index indicating the first character after the
175: * separator.
176: */
177: public void fillFromIDString(String s, int i) {
178: try {
179: // There will always be only one column for datastore identity.
180: // Composite pk is only supported for application identity which
181: // will use the code in the objectid-class.
182: switch (cmd.datastoreIdentityTypeCode) {
183: case MDStatics.INTW:
184: case MDStatics.INT:
185: pk[0] = new Integer(FastParser.parseInt(s, i));
186: break;
187: case MDStatics.SHORTW:
188: case MDStatics.SHORT:
189: pk[0] = new Short((short) FastParser.parseInt(s, i));
190: break;
191: case MDStatics.STRING:
192: pk[0] = s.substring(i);
193: break;
194: case MDStatics.BOOLEANW:
195: case MDStatics.BOOLEAN:
196: pk[0] = new Boolean(s.substring(i));
197: break;
198: case MDStatics.BYTEW:
199: case MDStatics.BYTE:
200: pk[0] = new Byte((byte) FastParser.parseInt(s, i));
201: break;
202: case MDStatics.BIGDECIMAL:
203: pk[0] = new BigDecimal(s.substring(i));
204: break;
205: case MDStatics.BIGINTEGER:
206: pk[0] = new BigInteger(s.substring(i));
207: break;
208: case MDStatics.DOUBLEW:
209: case MDStatics.DOUBLE:
210: pk[0] = new Double(s.substring(i));
211: break;
212: case MDStatics.FLOATW:
213: case MDStatics.FLOAT:
214: pk[0] = new Float(s.substring(i));
215: break;
216: case MDStatics.LONGW:
217: case MDStatics.LONG:
218: pk[0] = new Long(FastParser.parseLong(s, i));
219: break;
220: default:
221: throw BindingSupportImpl.getInstance().internal(
222: "Unable to create id from the string '" + s
223: + "' type code "
224: + cmd.datastoreIdentityTypeCode);
225: }
226: } catch (Exception e) {
227: throw BindingSupportImpl.getInstance().invalidOperation(
228: "invalid OID String: '" + s + "'", e);
229: }
230: }
231:
232: public int hashCode() {
233: if (pk == null)
234: return super .hashCode();
235: int hc = pk[0].hashCode() + cmd.top.index;
236: for (int i = 1; i < pk.length; i++)
237: hc = hc * 17 + pk[i].hashCode();
238: return hc;
239: }
240:
241: public boolean equals(Object obj) {
242: if (obj == null)
243: return false;
244: if (obj instanceof GenericOID) {
245: GenericOID o = (GenericOID) obj;
246: if (o.cmd.top != cmd.top)
247: return false;
248: for (int i = pk.length - 1; i >= 0; i--) {
249: if (pk[i] == null) {
250: if (o.pk[i] != null)
251: return false;
252: } else if (!pk[i].equals(o.pk[i])) {
253: return false;
254: }
255: }
256: return true;
257: } else {
258: return obj.equals(this );
259: }
260: }
261:
262: public String toString() {
263: if (!resolved) {
264: throw BindingSupportImpl.getInstance().internal(
265: "This oid is not resolved to its actual type");
266: }
267: return toStringImp();
268: }
269:
270: /**
271: * Get the toString of this OID even if it has not been resolved.
272: */
273: public String toStringImp() {
274: if (pk == null)
275: return "null pk";
276: StringBuffer s = new StringBuffer();
277: s.append(cmd.classId);
278: s.append(MDStatics.OID_STRING_SEPERATOR);
279: int len = pk.length;
280: s.append(pk[0]);
281: for (int i = 1; i < len; i++) {
282: s.append(MDStatics.OID_STRING_SEPERATOR);
283: s.append(pk[i]);
284: }
285: return s.toString();
286: }
287:
288: public abstract String toSString();
289:
290: /**
291: * Encode the 'primary key' of this OID as a String. This is used for
292: * pretty printing OIDs in the workbench.
293: */
294: public String toPkString() {
295: if (pk == null)
296: return "null pk";
297: StringBuffer s = new StringBuffer();
298: int len = pk.length;
299: s.append(pk[0]);
300: for (int i = 1; i < len; i++) {
301: s.append(',');
302: s.append(' ');
303: s.append(pk[i]);
304: }
305: return s.toString();
306: }
307:
308: public int compareTo(Object o) {
309: OID oo = (OID) o;
310: int diff = cmd.top.index - oo.getBaseClassMetaData().index;
311: if (diff != 0)
312: return diff;
313: if (oo.isNew()) {
314: return +1; // we are always after new OIDs
315: } else {
316: GenericOID oid = (GenericOID) oo;
317: for (int i = 0; i < pk.length; i++) {
318: Object a = pk[i];
319: Object b = oid.pk[i];
320: if (a instanceof Comparable) {
321: diff = ((Comparable) a).compareTo(b);
322: } else {
323: // yuck but I do not know any other way to do this
324: diff = a.toString().compareTo(b.toString());
325: }
326: if (diff != 0)
327: return diff;
328: }
329: return 0;
330: }
331: }
332:
333: /**
334: * Return the primary key stored in this OID as an long. This will only
335: * be called for datastore identity classes.
336: */
337: public long getLongPrimaryKey() {
338: return ((Number) pk[0]).longValue();
339: }
340:
341: /**
342: * Set the primary key stored in this OID as an long. This will only be
343: * called for datastore identity classes.
344: */
345: public void setLongPrimaryKey(long pki) {
346: switch (cmd.datastoreIdentityTypeCode) {
347: case MDStatics.INTW:
348: case MDStatics.INT:
349: pk[0] = new Integer((int) pki);
350: break;
351: case MDStatics.SHORTW:
352: case MDStatics.SHORT:
353: pk[0] = new Short((short) pki);
354: break;
355: case MDStatics.BYTEW:
356: case MDStatics.BYTE:
357: pk[0] = new Byte((byte) pki);
358: break;
359: case MDStatics.LONGW:
360: case MDStatics.LONG:
361: pk[0] = new Long(pki);
362: break;
363: default:
364: throw BindingSupportImpl.getInstance().internal(
365: "Unhandled java type code "
366: + cmd.datastoreIdentityTypeCode);
367: }
368: }
369:
370: public void populateObjectIdClassInstance(Object o) {
371: ClassMetaData cmd = getAvailableClassMetaData();
372: if (cmd.identityType != MDStatics.IDENTITY_TYPE_APPLICATION) {
373: throw BindingSupportImpl.getInstance().internal(
374: "not an app identity class: " + cmd.qname);
375: }
376: FieldMetaData[] a = cmd.pkFields;
377: for (int i = 0; i < a.length; i++) {
378: try {
379: a[i].getObjectidClassField().set(o, pk[i]);
380: } catch (Exception e) {
381: throw BindingSupportImpl.getInstance().internal(
382: e.toString(), e);
383: }
384: }
385: }
386:
387: public boolean isNew() {
388: return false;
389: }
390:
391: public OID getMappedOID() {
392: return this ;
393: }
394:
395: public OID getRealOID() {
396: return this ;
397: }
398:
399: public OID getAvailableOID() {
400: return this ;
401: }
402:
403: public int getAvailableClassId() {
404: return getAvailableClassMetaData().classId;
405: }
406:
407: public void setCmd(ClassMetaData cmd) {
408: this .cmd = cmd;
409: }
410:
411: public void writeExternal(OIDObjectOutput os) throws IOException {
412: writeExternalImp(os);
413: }
414:
415: public void writeExternal(ObjectOutput os) throws IOException {
416: os.writeShort(getClassIndex());
417: os.writeBoolean(resolved);
418: writeExternalImp(os);
419: }
420:
421: private void writeExternalImp(ObjectOutput os) throws IOException {
422: if (cmd.pkFields == null) {
423: writeObjectByTypeCode(pk[0], cmd.datastoreIdentityTypeCode,
424: os);
425: } else {
426: int n = cmd.pkFields.length;
427: for (int i = 0; i < n; i++) {
428: writeObjectByTypeCode(pk[i], cmd.pkFields[i].typeCode,
429: os);
430: }
431: }
432: }
433:
434: public void readExternal(OIDObjectInput is)
435: throws ClassNotFoundException, IOException {
436: readExternalImp(is);
437: }
438:
439: public void readExternal(ObjectInput is)
440: throws ClassNotFoundException, IOException {
441: cmd = ModelMetaData.getThreadMetaData().classes[is.readShort()];
442: resolved = is.readBoolean();
443: readExternalImp(is);
444: }
445:
446: private void readExternalImp(ObjectInput is) throws IOException {
447: // cmd and resolved will have already been set by the constructor so
448: // no need to read them here
449: if (cmd.pkFields == null) {
450: pk = new Object[] { readObjectByTypeCode(
451: cmd.datastoreIdentityTypeCode, is) };
452: } else {
453: int n = cmd.pkFields.length;
454: pk = new Object[n];
455: for (int i = 0; i < n; i++) {
456: pk[i] = readObjectByTypeCode(cmd.pkFields[i].typeCode,
457: is);
458: }
459: }
460: }
461:
462: private void writeObjectByTypeCode(Object value, int typeCode,
463: ObjectOutput os) throws IOException {
464: if (value != null) {
465: //write a non-null indicator
466: os.writeBoolean(false);
467: switch (typeCode) {
468: case MDStatics.INTW:
469: case MDStatics.INT:
470: os.writeInt(((Integer) value).intValue());
471: break;
472: case MDStatics.CHARW:
473: case MDStatics.CHAR:
474: os.writeInt(((Character) value).charValue());
475: break;
476: case MDStatics.SHORTW:
477: case MDStatics.SHORT:
478: os.writeShort(((Short) value).intValue());
479: break;
480: case MDStatics.STRING:
481: os.writeUTF((String) value);
482: break;
483: case MDStatics.BOOLEANW:
484: case MDStatics.BOOLEAN:
485: os.writeBoolean(((Boolean) value).booleanValue());
486: break;
487: case MDStatics.BYTEW:
488: case MDStatics.BYTE:
489: os.writeByte(((Byte) value).byteValue());
490: break;
491: case MDStatics.DOUBLEW:
492: case MDStatics.DOUBLE:
493: os.writeDouble(((Double) value).doubleValue());
494: break;
495: case MDStatics.FLOATW:
496: case MDStatics.FLOAT:
497: os.writeFloat(((Float) value).floatValue());
498: break;
499: case MDStatics.LONGW:
500: case MDStatics.LONG:
501: os.writeLong(((Long) value).longValue());
502: break;
503: case MDStatics.LOCALE:
504: final Locale l = (Locale) value;
505: os.writeUTF(l.getLanguage());
506: os.writeUTF(l.getCountry());
507: os.writeUTF(l.getVariant());
508: break;
509: case MDStatics.BIGDECIMAL:
510: os.writeUTF(value.toString());
511: break;
512: case MDStatics.BIGINTEGER:
513: os.writeUTF(value.toString());
514: break;
515: case MDStatics.DATE:
516: os.writeLong(((Date) value).getTime());
517: break;
518: default:
519: throw BindingSupportImpl.getInstance().internal(
520: "Unable to write type code " + typeCode);
521: }
522: } else {
523: //write a null indicator
524: os.writeBoolean(true);
525: }
526: }
527:
528: private Object readObjectByTypeCode(int typeCode, ObjectInput is)
529: throws IOException {
530: if (is.readBoolean()) {
531: return null;
532: } else {
533: switch (typeCode) {
534: case MDStatics.INTW:
535: case MDStatics.INT:
536: return new Integer(is.readInt());
537: case MDStatics.CHARW:
538: case MDStatics.CHAR:
539: return new Character(is.readChar());
540: case MDStatics.SHORTW:
541: case MDStatics.SHORT:
542: return new Short(is.readShort());
543: case MDStatics.STRING:
544: return is.readUTF();
545: case MDStatics.BOOLEANW:
546: case MDStatics.BOOLEAN:
547: return new Boolean(is.readBoolean());
548: case MDStatics.BYTEW:
549: case MDStatics.BYTE:
550: return new Byte(is.readByte());
551: case MDStatics.DOUBLEW:
552: case MDStatics.DOUBLE:
553: return new Double(is.readDouble());
554: case MDStatics.FLOATW:
555: case MDStatics.FLOAT:
556: return new Float(is.readFloat());
557: case MDStatics.LONGW:
558: case MDStatics.LONG:
559: return new Long(is.readLong());
560: case MDStatics.LOCALE:
561: return new Locale(is.readUTF(), is.readUTF(), is
562: .readUTF());
563: case MDStatics.BIGDECIMAL:
564: return new BigDecimal(is.readUTF());
565: case MDStatics.BIGINTEGER:
566: return new BigInteger(is.readUTF());
567: case MDStatics.DATE:
568: return new Date(is.readLong());
569: default:
570: throw BindingSupportImpl.getInstance().internal(
571: "Unable to read java type code " + typeCode);
572: }
573: }
574: }
575:
576: }
|