001: /*
002: * Helma License Notice
003: *
004: * The contents of this file are subject to the Helma License
005: * Version 2.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://adele.helma.org/download/helma/license.txt
008: *
009: * Copyright 1998-2003 Helma Software. All Rights Reserved.
010: *
011: * $RCSfile$
012: * $Author: root $
013: * $Revision: 8604 $
014: * $Date: 2007-09-28 15:16:38 +0200 (Fre, 28 Sep 2007) $
015: */
016:
017: package helma.objectmodel.db;
018:
019: import helma.objectmodel.INode;
020: import helma.objectmodel.IProperty;
021:
022: import java.io.IOException;
023: import java.io.ObjectInputStream;
024: import java.io.ObjectOutputStream;
025: import java.io.Serializable;
026: import java.sql.Timestamp;
027: import java.text.SimpleDateFormat;
028: import java.util.Date;
029:
030: /**
031: * A property implementation for Nodes stored inside a database. Basically
032: * the same as for transient nodes, with a few hooks added.
033: */
034: public final class Property implements IProperty, Serializable,
035: Cloneable, Comparable {
036: static final long serialVersionUID = -1022221688349192379L;
037: private String propname;
038: private Node node;
039: private Object value;
040: private int type;
041: transient boolean dirty;
042:
043: /**
044: * Creates a new Property object.
045: *
046: * @param node ...
047: */
048: public Property(Node node) {
049: this .node = node;
050: dirty = true;
051: }
052:
053: /**
054: * Creates a new Property object.
055: *
056: * @param propname ...
057: * @param node ...
058: */
059: public Property(String propname, Node node) {
060: this .propname = propname;
061: this .node = node;
062: dirty = true;
063: }
064:
065: /**
066: * Creates a new Property object.
067: *
068: * @param propname ...
069: * @param node ...
070: * @param valueNode ...
071: */
072: public Property(String propname, Node node, Node valueNode) {
073: this (propname, node);
074: type = NODE;
075: value = (valueNode == null) ? null : valueNode.getHandle();
076: dirty = true;
077: }
078:
079: private void readObject(ObjectInputStream in) throws IOException {
080: try {
081: propname = in.readUTF();
082: node = (Node) in.readObject();
083: type = in.readInt();
084:
085: switch (type) {
086: case STRING:
087: value = in.readObject();
088:
089: break;
090:
091: case BOOLEAN:
092: value = in.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
093:
094: break;
095:
096: case INTEGER:
097: value = new Long(in.readLong());
098:
099: break;
100:
101: case DATE:
102: value = new Date(in.readLong());
103:
104: break;
105:
106: case FLOAT:
107: value = new Double(in.readDouble());
108:
109: break;
110:
111: case NODE:
112: value = in.readObject();
113:
114: break;
115:
116: case JAVAOBJECT:
117: value = in.readObject();
118:
119: break;
120: }
121: } catch (ClassNotFoundException x) {
122: throw new IOException(x.toString());
123: }
124: }
125:
126: private void writeObject(ObjectOutputStream out) throws IOException {
127: out.writeUTF(propname);
128: out.writeObject(node);
129: out.writeInt(type);
130:
131: switch (type) {
132: case STRING:
133: out.writeObject(value);
134:
135: break;
136:
137: case BOOLEAN:
138: out.writeBoolean(((Boolean) value).booleanValue());
139:
140: break;
141:
142: case INTEGER:
143: out.writeLong(((Long) value).longValue());
144:
145: break;
146:
147: case DATE:
148: out.writeLong(((Date) value).getTime());
149:
150: break;
151:
152: case FLOAT:
153: out.writeDouble(((Double) value).doubleValue());
154:
155: break;
156:
157: case NODE:
158: out.writeObject(value);
159:
160: break;
161:
162: case JAVAOBJECT:
163:
164: if ((value != null) && !(value instanceof Serializable)) {
165: out.writeObject(null);
166: } else {
167: out.writeObject(value);
168: }
169:
170: break;
171: }
172: }
173:
174: /**
175: * Get the name of the property
176: *
177: * @return this property's name
178: */
179: public String getName() {
180: return propname;
181: }
182:
183: /**
184: * Set the name of the property
185: */
186: protected void setName(String name) {
187: this .propname = name;
188: }
189:
190: /**
191: *
192: *
193: * @return the property's value in its native class
194: */
195: public Object getValue() {
196: return value;
197: }
198:
199: /**
200: *
201: *
202: * @return the property's type as defined in helma.objectmodel.IProperty.java
203: */
204: public int getType() {
205: return type;
206: }
207:
208: /**
209: * Directly set the value of this property.
210: */
211: protected void setValue(Object value, int type) {
212: this .value = value;
213: this .type = type;
214: dirty = true;
215: }
216:
217: /**
218: *
219: *
220: * @param str ...
221: */
222: public void setStringValue(String str) {
223: type = STRING;
224: value = str;
225: dirty = true;
226: }
227:
228: /**
229: *
230: *
231: * @param l ...
232: */
233: public void setIntegerValue(long l) {
234: type = INTEGER;
235: value = new Long(l);
236: dirty = true;
237: }
238:
239: /**
240: *
241: *
242: * @param d ...
243: */
244: public void setFloatValue(double d) {
245: type = FLOAT;
246: value = new Double(d);
247: dirty = true;
248: }
249:
250: /**
251: *
252: *
253: * @param date ...
254: */
255: public void setDateValue(Date date) {
256: type = DATE;
257: // normalize from java.sql.* Date subclasses
258: if (date != null && date.getClass() != Date.class) {
259: value = new Date(date.getTime());
260: } else {
261: value = date;
262: }
263: dirty = true;
264: }
265:
266: /**
267: *
268: *
269: * @param bool ...
270: */
271: public void setBooleanValue(boolean bool) {
272: type = BOOLEAN;
273: value = bool ? Boolean.TRUE : Boolean.FALSE;
274: dirty = true;
275: }
276:
277: /**
278: *
279: *
280: * @param node ...
281: */
282: public void setNodeValue(Node node) {
283: type = NODE;
284: value = (node == null) ? null : node.getHandle();
285: dirty = true;
286: }
287:
288: /**
289: *
290: *
291: * @param handle ...
292: */
293: public void setNodeHandle(NodeHandle handle) {
294: type = NODE;
295: value = handle;
296: dirty = true;
297: }
298:
299: /**
300: *
301: *
302: * @return ...
303: */
304: public NodeHandle getNodeHandle() {
305: if (type == NODE) {
306: return (NodeHandle) value;
307: }
308:
309: return null;
310: }
311:
312: /**
313: *
314: *
315: * @param rel the Relation
316: */
317: public void convertToNodeReference(Relation rel) {
318: if ((value != null) && !(value instanceof NodeHandle)) {
319: if (rel.usesPrimaryKey()) {
320: value = new NodeHandle(new DbKey(rel.otherType, value
321: .toString()));
322: } else {
323: value = new NodeHandle(new MultiKey(rel.otherType, rel
324: .getKeyParts(node)));
325: }
326: }
327:
328: type = NODE;
329: }
330:
331: /**
332: *
333: *
334: * @param obj ...
335: */
336: public void setJavaObjectValue(Object obj) {
337: type = JAVAOBJECT;
338: value = obj;
339: dirty = true;
340: }
341:
342: /**
343: *
344: *
345: * @return ...
346: */
347: public String getStringValue() {
348: if (value == null) {
349: return null;
350: }
351:
352: switch (type) {
353: case STRING:
354: case BOOLEAN:
355: case INTEGER:
356: case FLOAT:
357: case JAVAOBJECT:
358: return value.toString();
359:
360: case DATE:
361:
362: SimpleDateFormat format = new SimpleDateFormat(
363: "yyyy-MM-dd HH:mm:ss");
364:
365: return format.format((Date) value);
366:
367: case NODE:
368: return ((NodeHandle) value).getID();
369: }
370:
371: return "";
372: }
373:
374: /**
375: *
376: *
377: * @return ...
378: */
379: public String toString() {
380: return getStringValue();
381: }
382:
383: /**
384: *
385: *
386: * @return ...
387: */
388: public long getIntegerValue() {
389: if (type == INTEGER) {
390: return ((Long) value).longValue();
391: }
392:
393: if (type == FLOAT) {
394: return ((Double) value).longValue();
395: }
396:
397: if (type == BOOLEAN) {
398: return ((Boolean) value).booleanValue() ? 1 : 0;
399: }
400:
401: try {
402: return Long.parseLong(getStringValue());
403: } catch (Exception x) {
404: return 0;
405: }
406: }
407:
408: /**
409: *
410: *
411: * @return ...
412: */
413: public double getFloatValue() {
414: if (type == FLOAT) {
415: return ((Double) value).doubleValue();
416: }
417:
418: if (type == INTEGER) {
419: return ((Long) value).doubleValue();
420: }
421:
422: try {
423: return Double.parseDouble(getStringValue());
424: } catch (Exception x) {
425: return 0.0;
426: }
427: }
428:
429: /**
430: *
431: *
432: * @return ...
433: */
434: public Date getDateValue() {
435: if (type == DATE) {
436: return (Date) value;
437: }
438:
439: return null;
440: }
441:
442: /**
443: *
444: *
445: * @return ...
446: */
447: public Timestamp getTimestampValue() {
448: if ((type == DATE) && (value != null)) {
449: return new Timestamp(((Date) value).getTime());
450: }
451:
452: return null;
453: }
454:
455: /**
456: *
457: *
458: * @return ...
459: */
460: public boolean getBooleanValue() {
461: if (type == BOOLEAN) {
462: return ((Boolean) value).booleanValue();
463: }
464:
465: if (type == INTEGER || type == FLOAT) {
466: return !(0 == getIntegerValue());
467: }
468:
469: return false;
470: }
471:
472: /**
473: *
474: *
475: * @return ...
476: */
477: public INode getNodeValue() {
478: if ((type == NODE) && (value != null)) {
479: NodeHandle nhandle = (NodeHandle) value;
480:
481: return nhandle.getNode(node.nmgr);
482: }
483:
484: return null;
485: }
486:
487: /**
488: *
489: *
490: * @return ...
491: */
492: public Object getJavaObjectValue() {
493: if (type == JAVAOBJECT) {
494: return value;
495: }
496:
497: return null;
498: }
499:
500: /**
501: * @see java.lang.Comparable#compareTo(java.lang.Object)
502: *
503: * The following cases throw a ClassCastException
504: * - Properties of a different type
505: * - Properties of boolean or node type
506: */
507: public int compareTo(Object obj) {
508: Property p = (Property) obj;
509: int ptype = p.getType();
510: Object pvalue = p.getValue();
511:
512: if (type == NODE || ptype == NODE || type == BOOLEAN
513: || ptype == BOOLEAN) {
514: throw new ClassCastException("uncomparable values " + this
515: + "(" + type + ") : " + p + "(" + ptype + ")");
516: }
517: if (value == null && pvalue == null) {
518: return 0;
519: } else if (value == null) {
520: return 1;
521: }
522: if (pvalue == null) {
523: return -1;
524: }
525: if (type != ptype) {
526: // float/integer sometimes get mixed up in Rhino
527: if ((type == FLOAT && ptype == INTEGER)
528: || (type == INTEGER && ptype == FLOAT))
529: return Double.compare(((Number) value).doubleValue(),
530: ((Number) pvalue).doubleValue());
531: throw new ClassCastException("uncomparable values " + this
532: + "(" + type + ") : " + p + "(" + ptype + ")");
533:
534: }
535: if (!(value instanceof Comparable)) {
536: throw new ClassCastException("uncomparable value " + value
537: + "(" + value.getClass() + ")");
538: }
539: // System.err.println("COMPARING: " + value.getClass() + " TO " + pvalue.getClass());
540: return ((Comparable) value).compareTo(pvalue);
541: }
542:
543: /**
544: * Return true if object o is equal to this property.
545: *
546: * @param obj the object to compare to
547: * @return true if this equals obj
548: * @see java.lang.Object#equals(java.lang.Object)
549: */
550: public boolean equals(Object obj) {
551: if (obj == this )
552: return true;
553: if (obj == null)
554: return false;
555: if (!(obj instanceof Property))
556: return false;
557: Property p = (Property) obj;
558: return value == null ? p.value == null : value.equals(p.value);
559: }
560: }
|