001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010:
011: package org.mmbase.bridge.util;
012:
013: import java.util.*;
014: import java.io.*;
015:
016: import java.text.Collator;
017:
018: import org.mmbase.bridge.*;
019: import org.mmbase.bridge.implementation.BasicFieldValue;
020: import org.mmbase.datatypes.DataType;
021: import org.mmbase.util.functions.*;
022: import org.mmbase.util.logging.*;
023: import org.mmbase.util.*;
024:
025: import org.w3c.dom.Element;
026: import org.w3c.dom.Document;
027:
028: /**
029: * Abstract implementation of Node.
030: * All methods which are based on other methods are implemented
031: * here, to minimalize the implementation effort of fully implemented Nodes.
032: *
033: * @author Michiel Meeuwissen
034: * @version $Id: AbstractNode.java,v 1.22 2007/10/17 12:48:14 michiel Exp $
035: * @see org.mmbase.bridge.Node
036: * @since MMBase-1.8
037: */
038: public abstract class AbstractNode implements Node {
039: private static final Logger log = Logging
040: .getLoggerInstance(AbstractNode.class);
041:
042: public boolean isRelation() {
043: return false;
044: }
045:
046: public Relation toRelation() {
047: throw new ClassCastException("The node " + this
048: + " is not a relation, (but a " + getClass() + ")");
049: }
050:
051: public boolean isNodeManager() {
052: return false;
053: }
054:
055: public NodeManager toNodeManager() {
056: throw new ClassCastException("The node " + this
057: + " is not a node manager , (but a " + getClass() + ")");
058: }
059:
060: public boolean isRelationManager() {
061: return false;
062: }
063:
064: public RelationManager toRelationManager() {
065: throw new ClassCastException("The node " + this
066: + " is not a relation manager , (but a " + getClass()
067: + ")");
068: }
069:
070: public boolean isNull(String fieldName) {
071: return getValueWithoutProcess(fieldName) == null;
072: }
073:
074: public int getNumber() {
075: return Casting.toInt(getValueWithoutProcess("number"));
076: }
077:
078: /**
079: * Setting value with default method (depending on field's type)
080: * @param fieldName name of the field
081: * @param value set value
082: */
083: public final void setValue(String fieldName, Object value) {
084: Field field = getNodeManager().getField(fieldName);
085: if (value == null) {
086: setValueWithoutProcess(fieldName, value);
087: } else {
088: value = field.getDataType().cast(value, this , field);
089: switch (field.getDataType().getBaseType()) {
090: case Field.TYPE_STRING:
091: setStringValue(fieldName, (String) value);
092: break;
093: case Field.TYPE_INTEGER:
094: setIntValue(fieldName, Casting.toInt(value));
095: break;
096: case Field.TYPE_BINARY: {
097: long length = getSize(fieldName);
098: setInputStreamValue(fieldName, Casting
099: .toInputStream(value), length);
100: break;
101: }
102: case Field.TYPE_FLOAT:
103: setFloatValue(fieldName, Casting.toFloat(value));
104: break;
105: case Field.TYPE_DOUBLE:
106: setDoubleValue(fieldName, Casting.toDouble(value));
107: break;
108: case Field.TYPE_LONG:
109: setLongValue(fieldName, Casting.toLong(value));
110: break;
111: case Field.TYPE_XML:
112: setXMLValue(fieldName, (Document) value);
113: break;
114: case Field.TYPE_NODE:
115: setNodeValue(fieldName, Casting.toNode(value,
116: getCloud()));
117: break;
118: case Field.TYPE_DATETIME:
119: setDateValue(fieldName, (Date) value);
120: break;
121: case Field.TYPE_BOOLEAN:
122: setBooleanValue(fieldName, Casting.toBoolean(value));
123: break;
124: case Field.TYPE_LIST:
125: setListValue(fieldName, (List) value);
126: break;
127: default:
128: setObjectValue(fieldName, value);
129: }
130: }
131: }
132:
133: /**
134: * Throws exception if may not write current node
135: * @since MMBase-1.9
136: */
137: protected void checkWrite() {
138: }
139:
140: /**
141: * Like setObjectValue, but without processing, this is called by the other set-values.
142: * @param fieldName name of field
143: * @param value new value of the field
144: * @todo setting certain specific fields (i.e. snumber) should be directed to a dedicated
145: * method such as setSource(), where applicable.
146: * @since MMBase-1.7
147: */
148: public void setValueWithoutProcess(String fieldName, Object value) {
149: checkWrite();
150: if ("owner".equals(fieldName)) {
151: setContext(Casting.toString(value));
152: return;
153: }
154: if ("number".equals(fieldName) || "otype".equals(fieldName)) {
155: throw new BridgeException("Not allowed to change field '"
156: + fieldName + "'.");
157: }
158: setValueWithoutChecks(fieldName, value);
159: }
160:
161: protected abstract void setValueWithoutChecks(String fieldName,
162: Object value);
163:
164: public final void setObjectValue(String fieldName, Object value) {
165: Field field = getNodeManager().getField(fieldName);
166: value = field.getDataType().getProcessor(DataType.PROCESS_SET,
167: Field.TYPE_UNKNOWN).process(this , field, value);
168: setValueWithoutProcess(fieldName, value);
169: }
170:
171: public final void setBooleanValue(String fieldName,
172: final boolean value) {
173: Field field = getNodeManager().getField(fieldName);
174: Object v = field.getDataType().getProcessor(
175: DataType.PROCESS_SET, Field.TYPE_BOOLEAN).process(this ,
176: field, Boolean.valueOf(value));
177: setValueWithoutProcess(fieldName, v);
178: }
179:
180: public final void setDateValue(String fieldName, final Date value) {
181: Field field = getNodeManager().getField(fieldName);
182: Object v = field.getDataType().getProcessor(
183: DataType.PROCESS_SET, Field.TYPE_DATETIME).process(
184: this , field, value);
185: setValueWithoutProcess(fieldName, v);
186: }
187:
188: public final void setListValue(String fieldName, final List value) {
189: Field field = getNodeManager().getField(fieldName);
190: Object v = field.getDataType().getProcessor(
191: DataType.PROCESS_SET, Field.TYPE_LIST).process(this ,
192: field, value);
193: setValueWithoutProcess(fieldName, v);
194: }
195:
196: /**
197: * A method to convert an object to an node number.
198: * Default impelmentation is reasonable, but does not support core objects.
199: */
200: protected Integer toNodeNumber(Object v) {
201: if (v == null) {
202: return null;
203: } else if (v instanceof Node) {
204: return Integer.valueOf(((Node) v).getNumber());
205: } else {
206: // giving up
207: return Integer.valueOf(getCloud().getNode(v.toString())
208: .getNumber());
209: }
210: }
211:
212: public final void setNodeValue(String fieldName, final Node value) {
213: Field field = getNodeManager().getField(fieldName);
214: Object v = field.getDataType().getProcessor(
215: DataType.PROCESS_SET, Field.TYPE_NODE).process(this ,
216: field, value);
217: setValueWithoutProcess(fieldName, toNodeNumber(v));
218: }
219:
220: public final void setIntValue(String fieldName, final int value) {
221: Field field = getNodeManager().getField(fieldName);
222: Object v = field.getDataType().getProcessor(
223: DataType.PROCESS_SET, Field.TYPE_INTEGER).process(this ,
224: field, Integer.valueOf(value));
225: setValueWithoutProcess(fieldName, v);
226: }
227:
228: public final void setLongValue(String fieldName, final long value) {
229: Field field = getNodeManager().getField(fieldName);
230: Object v = field.getDataType().getProcessor(
231: DataType.PROCESS_SET, Field.TYPE_LONG).process(this ,
232: field, Long.valueOf(value));
233: setValueWithoutProcess(fieldName, v);
234: }
235:
236: public final void setFloatValue(String fieldName, final float value) {
237: Field field = getNodeManager().getField(fieldName);
238: Object v = field.getDataType().getProcessor(
239: DataType.PROCESS_SET, Field.TYPE_FLOAT).process(this ,
240: field, Float.valueOf(value));
241: setValueWithoutProcess(fieldName, v);
242: }
243:
244: public final void setDoubleValue(String fieldName,
245: final double value) {
246: Field field = getNodeManager().getField(fieldName);
247: Object v = field.getDataType().getProcessor(
248: DataType.PROCESS_SET, Field.TYPE_DOUBLE).process(this ,
249: field, Double.valueOf(value));
250: setValueWithoutProcess(fieldName, v);
251: }
252:
253: public final void setByteValue(String fieldName, final byte[] value) {
254: Field field = getNodeManager().getField(fieldName);
255: Object v = field.getDataType().getProcessor(
256: DataType.PROCESS_SET, Field.TYPE_BINARY).process(this ,
257: field, value);
258: setValueWithoutProcess(fieldName, v);
259: }
260:
261: protected abstract void setSize(String fieldName, long size);
262:
263: private static final int readLimit = 10 * 1024 * 1024;
264:
265: public final void setInputStreamValue(String fieldName,
266: final InputStream value, long size) {
267: setSize(fieldName, size);
268: Field field = getNodeManager().getField(fieldName);
269: if (log.isDebugEnabled()) {
270: log.debug("Setting binary value for " + field);
271: }
272: Object v = value;
273: try {
274: if (value.markSupported() && size < readLimit) {
275: if (log.isDebugEnabled()) {
276: log.debug("Mark supported and using "
277: + field.getDataType().getProcessor(
278: DataType.PROCESS_SET,
279: Field.TYPE_BINARY));
280: }
281: value.mark(readLimit);
282: v = field.getDataType().getProcessor(
283: DataType.PROCESS_SET, Field.TYPE_BINARY)
284: .process(this , field, value);
285: value.reset();
286: } else {
287: if (field.getDataType().getProcessor(
288: DataType.PROCESS_SET, Field.TYPE_BINARY) != null) {
289: if (log.isDebugEnabled()) {
290: log.debug("Mark not supported but using "
291: + field.getDataType().getProcessor(
292: DataType.PROCESS_SET,
293: Field.TYPE_BINARY));
294: }
295: // well, we must read it to byte-array then, first.
296: ByteArrayOutputStream b = new ByteArrayOutputStream(
297: (int) size);
298: int c;
299: while ((c = value.read()) > -1) {
300: b.write(c);
301: }
302: byte[] byteArray = b.toByteArray();
303: v = field.getDataType().getProcessor(
304: DataType.PROCESS_SET, Field.TYPE_BINARY)
305: .process(this , field, byteArray);
306: } else {
307: log
308: .debug("Mark not support but no need for processing");
309: v = value;
310: }
311: }
312: } catch (IOException ioe) {
313: log.error(ioe);
314: }
315: setValueWithoutProcess(fieldName, v);
316: }
317:
318: public final void setStringValue(final String fieldName,
319: final String value) {
320: Field field = getNodeManager().getField(fieldName);
321: Object setValue = field.getDataType().preCast(value, this ,
322: field); // to resolve enumerations
323: Object v = field.getDataType().getProcessor(
324: DataType.PROCESS_SET, Field.TYPE_STRING).process(this ,
325: field, setValue);
326: setValueWithoutProcess(fieldName, v);
327: }
328:
329: public final void setXMLValue(String fieldName, final Document value) {
330: Field field = getNodeManager().getField(fieldName);
331: Object v = field.getDataType().getProcessor(
332: DataType.PROCESS_SET, Field.TYPE_XML).process(this ,
333: field, value);
334: setValueWithoutProcess(fieldName, v);
335: }
336:
337: /**
338: * @since MMBase-1.8.5
339: */
340: protected Object processNull(int type, Field field) {
341: return field.getDataType().getProcessor(DataType.PROCESS_GET,
342: type).process(this , field, null);
343: }
344:
345: public final Object getValue(String fieldName) {
346: Object value = getValueWithoutProcess(fieldName);
347: NodeManager nm = getNodeManager();
348: if (nm.hasField(fieldName)) {
349: int type = nm.getField(fieldName).getType();
350: if (value == null) {
351: return processNull(type, nm.getField(fieldName));
352: }
353: switch (type) {
354: case Field.TYPE_STRING:
355: return getStringValue(fieldName);
356: case Field.TYPE_BINARY:
357: return getByteValue(fieldName);
358: case Field.TYPE_INTEGER:
359: return Integer.valueOf(getIntValue(fieldName));
360: case Field.TYPE_FLOAT:
361: return Float.valueOf(getFloatValue(fieldName));
362: case Field.TYPE_DOUBLE:
363: return Double.valueOf(getDoubleValue(fieldName));
364: case Field.TYPE_LONG:
365: return Long.valueOf(getLongValue(fieldName));
366: case Field.TYPE_XML:
367: return getXMLValue(fieldName);
368: case Field.TYPE_NODE: {
369: // number is a NODE field, but should be returned as
370: // a number (in this case, a long)
371: // in the future, we may change the basic MMBase type for the number field to ID
372: if ("number".equals(fieldName)) {
373: return Long.valueOf(getLongValue(fieldName));
374: } else {
375: return getNodeValue(fieldName);
376: }
377: }
378: case Field.TYPE_BOOLEAN:
379: return Boolean.valueOf(getBooleanValue(fieldName));
380: case Field.TYPE_DATETIME:
381: return getDateValue(fieldName);
382: case Field.TYPE_LIST:
383: return getListValue(fieldName);
384: default:
385: log.error("Unknown fieldtype '" + type + "'");
386: return value;
387: }
388: } else {
389: //log.warn("Requesting value of unknown field '" + fieldName + "')");
390: return value;
391: }
392:
393: }
394:
395: public final Object getObjectValue(String fieldName) {
396: Object result = getValueWithoutProcess(fieldName);
397: NodeManager nodeManager = getNodeManager();
398: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
399: Field field = nodeManager.getField(fieldName);
400: Object r = field.getDataType().getProcessor(
401: DataType.PROCESS_GET, Field.TYPE_UNKNOWN).process(
402: this , field, result);
403: if ((result != null && (!result.equals(r)))) {
404: log.info("getObjectvalue was processed! " + result
405: + " != " + r);
406: result = r;
407: }
408: }
409: return result;
410: }
411:
412: public boolean getBooleanValue(String fieldName) {
413: Boolean result = Casting
414: .toBoolean(getValueWithoutProcess(fieldName)) ? Boolean.TRUE
415: : Boolean.FALSE; // odd.
416: NodeManager nodeManager = getNodeManager();
417: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
418: Field field = getNodeManager().getField(fieldName);
419: result = (Boolean) field.getDataType().getProcessor(
420: DataType.PROCESS_GET, Field.TYPE_BOOLEAN).process(
421: this , field, result);
422: }
423: return result.booleanValue();
424: }
425:
426: public Date getDateValue(String fieldName) {
427: Date result = Casting.toDate(getValueWithoutProcess(fieldName));
428: NodeManager nodeManager = getNodeManager();
429: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
430: Field field = nodeManager.getField(fieldName);
431: result = (Date) field.getDataType().getProcessor(
432: DataType.PROCESS_GET, Field.TYPE_DATETIME).process(
433: this , field, result);
434: }
435: return result;
436: }
437:
438: public List getListValue(String fieldName) {
439: List result = Casting.toList(getValueWithoutProcess(fieldName));
440: NodeManager nodeManager = getNodeManager();
441: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
442: Field field = nodeManager.getField(fieldName);
443: result = (List) field.getDataType().getProcessor(
444: DataType.PROCESS_GET, Field.TYPE_LIST).process(
445: this , field, result);
446: }
447: return result;
448: }
449:
450: public int getIntValue(String fieldName) {
451: Integer result = Casting
452: .toInteger(getValueWithoutProcess(fieldName));
453: NodeManager nodeManager = getNodeManager();
454: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
455: Field field = nodeManager.getField(fieldName);
456: result = (Integer) field.getDataType().getProcessor(
457: DataType.PROCESS_GET, Field.TYPE_INTEGER).process(
458: this , field, result);
459: }
460: return result.intValue();
461: }
462:
463: public float getFloatValue(String fieldName) {
464: Float result = Float.valueOf(Casting
465: .toFloat(getValueWithoutProcess(fieldName)));
466: NodeManager nodeManager = getNodeManager();
467: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
468: Field field = nodeManager.getField(fieldName);
469: result = (Float) field.getDataType().getProcessor(
470: DataType.PROCESS_GET, Field.TYPE_FLOAT).process(
471: this , field, result);
472: }
473: return result.floatValue();
474: }
475:
476: public long getLongValue(String fieldName) {
477: Long result = Long.valueOf(Casting
478: .toLong(getValueWithoutProcess(fieldName)));
479: NodeManager nodeManager = getNodeManager();
480: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
481: Field field = nodeManager.getField(fieldName);
482: result = (Long) field.getDataType().getProcessor(
483: DataType.PROCESS_GET, Field.TYPE_LONG).process(
484: this , field, result);
485: }
486: return result.longValue();
487: }
488:
489: public double getDoubleValue(String fieldName) {
490: Double result = Double.valueOf(Casting
491: .toDouble(getValueWithoutProcess(fieldName)));
492: NodeManager nodeManager = getNodeManager();
493: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
494: Field field = nodeManager.getField(fieldName);
495: result = (Double) field.getDataType().getProcessor(
496: DataType.PROCESS_GET, Field.TYPE_DOUBLE).process(
497: this , field, result);
498: }
499: return result.doubleValue();
500: }
501:
502: public byte[] getByteValue(String fieldName) {
503: byte[] result = Casting
504: .toByte(getValueWithoutProcess(fieldName));
505: NodeManager nodeManager = getNodeManager();
506: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
507: Field field = nodeManager.getField(fieldName);
508: result = (byte[]) field.getDataType().getProcessor(
509: DataType.PROCESS_GET, Field.TYPE_BINARY).process(
510: this , field, result);
511: }
512: return result;
513: }
514:
515: public java.io.InputStream getInputStreamValue(String fieldName) {
516: java.io.InputStream result = Casting
517: .toInputStream(getValueWithoutProcess(fieldName));
518: NodeManager nodeManager = getNodeManager();
519: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
520: Field field = nodeManager.getField(fieldName);
521: result = (java.io.InputStream) field.getDataType()
522: .getProcessor(DataType.PROCESS_GET,
523: Field.TYPE_BINARY).process(this , field,
524: result);
525: }
526: return result;
527: }
528:
529: public String getStringValue(String fieldName) {
530: String result = Casting
531: .toString(getValueWithoutProcess(fieldName));
532: NodeManager nodeManager = getNodeManager();
533: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
534: Field field = nodeManager.getField(fieldName);
535: result = (String) field.getDataType().getProcessor(
536: DataType.PROCESS_GET, Field.TYPE_STRING).process(
537: this , field, result);
538: }
539: return result;
540: }
541:
542: public Document getXMLValue(String fieldName) {
543: Document result = Casting
544: .toXML(getValueWithoutProcess(fieldName));
545: NodeManager nodeManager = getNodeManager();
546: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
547: Field field = nodeManager.getField(fieldName);
548: result = (Document) field.getDataType().getProcessor(
549: DataType.PROCESS_GET, Field.TYPE_XML).process(this ,
550: field, result);
551: }
552: return result;
553: }
554:
555: public Node getNodeValue(String fieldName) {
556: Node result = Casting.toNode(getValueWithoutProcess(fieldName),
557: getCloud());
558: NodeManager nodeManager = getNodeManager();
559: if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
560: Field field = nodeManager.getField(fieldName);
561: result = (Node) field.getDataType().getProcessor(
562: DataType.PROCESS_GET, Field.TYPE_NODE).process(
563: this , field, result);
564: }
565: return result;
566: }
567:
568: public final FieldValue getFieldValue(String fieldName)
569: throws NotFoundException {
570: return new BasicFieldValue(this , getNodeManager().getField(
571: fieldName));
572: }
573:
574: public final FieldValue getFieldValue(Field field) {
575: return new BasicFieldValue(this , field);
576: }
577:
578: public final Element getXMLValue(String fieldName, Document tree) {
579: Document doc = getXMLValue(fieldName);
580: if (doc == null) {
581: return null;
582: }
583: return (Element) tree
584: .importNode(doc.getDocumentElement(), true);
585: }
586:
587: public final void processCommit() {
588: FieldIterator fi = getNodeManager().getFields().fieldIterator();
589: while (fi.hasNext()) {
590: Field field = fi.nextField();
591: field.getDataType().getCommitProcessor()
592: .commit(this , field);
593: }
594: }
595:
596: public Collection<String> validate() {
597: List<String> errors = new ArrayList<String>();
598: FieldIterator fi = getNodeManager().getFields().fieldIterator();
599: Locale locale = getCloud().getLocale();
600: while (fi.hasNext()) {
601: Field field = fi.nextField();
602: // don't validate read-only (cannot be changed) or virtual fields (are not stored).
603: // Specifically, the 'number' field must not be validated, because for new nodes it does not yet
604: // point to an existing node...
605: // TODO: the number field should not be a NODE field
606: // TODO: possibly virtual fields DO need validation? How about temporary fields?
607: if (!field.isReadOnly() && !field.isVirtual()) {
608: // Only change a field if the enforcestrength of the restrictions is
609: // applicable to the change.
610: int enforceStrength = field.getDataType()
611: .getEnforceStrength();
612: if ((enforceStrength > DataType.ENFORCE_ONCHANGE)
613: || (isChanged(field.getName()) && (enforceStrength >= DataType.ENFORCE_ONCREATE))
614: || (isNew() && (enforceStrength >= DataType.ENFORCE_NEVER))) {
615: Object value = getValueWithoutProcess(field
616: .getName());
617: Collection<LocalizedString> fieldErrors = field
618: .getDataType().validate(value, this , field);
619: for (LocalizedString error : fieldErrors) {
620: errors.add("field '" + field.getName()
621: + "' with value '" + value + "': " + // TODO need to i18n this intro too
622: error.get(locale));
623: }
624: }
625: }
626: }
627: return errors;
628: }
629:
630: public final void delete() {
631: delete(false);
632: }
633:
634: public final void deleteRelations() {
635: deleteRelations("object");
636: }
637:
638: public final RelationList getRelations() {
639: return getRelations(null, (String) null);
640: }
641:
642: public final RelationList getRelations(String role) {
643: return getRelations(role, (String) null);
644: }
645:
646: public final RelationList getRelations(String role,
647: NodeManager nodeManager) {
648: if (nodeManager == null) {
649: return getRelations(role);
650: } else {
651: return getRelations(role, nodeManager.getName());
652: }
653: }
654:
655: public final int countRelations() {
656: return countRelatedNodes(getCloud().getNodeManager("object"),
657: null, "BOTH");
658: }
659:
660: public final int countRelations(String type) {
661: //err
662: return countRelatedNodes(getCloud().getNodeManager("object"),
663: type, "BOTH");
664: }
665:
666: public final NodeList getRelatedNodes() {
667: return getRelatedNodes("object", null, null);
668: }
669:
670: public final NodeList getRelatedNodes(String type) {
671: return getRelatedNodes(type, null, null);
672: }
673:
674: public final NodeList getRelatedNodes(NodeManager nodeManager) {
675: return getRelatedNodes(nodeManager, null, null);
676: }
677:
678: public final NodeList getRelatedNodes(String type, String role,
679: String searchDir) {
680: return getRelatedNodes(getCloud().getNodeManager(type), role,
681: searchDir);
682: }
683:
684: public Relation createRelation(Node destinationNode,
685: RelationManager relationManager) {
686: Relation relation = relationManager.createRelation(this ,
687: destinationNode);
688: return relation;
689: }
690:
691: /**
692: * Compares this node to the passed object.
693: * Returns 0 if they are equal, -1 if the object passed is a NodeManager and larger than this manager,
694: * and +1 if the object passed is a NodeManager and smaller than this manager.
695: * This is used to sort Nodes.
696: * A node is 'larger' than another node if its GUI() result is larger (alphabetically, case sensitive)
697: * than that of the other node. If the GUI() results are the same, the nodes are compared on number,
698: * and (if needed) on their owning clouds.
699: *
700: * @param o the object to compare it with
701: * @return 0 if they are equal, -1 if the object passed is a NodeManager and larger than this manager,
702: * and +1 if the object passed is a NodeManager and smaller than this manager.
703: */
704: public final int compareTo(Node o) {
705: Node n = o;
706: String s1 = "";
707: if (this instanceof NodeManager) {
708: s1 = ((NodeManager) this ).getGUIName();
709: } else {
710: s1 = getFunctionValue("gui", null).toString();
711: }
712: String s2 = "";
713: if (n instanceof NodeManager) {
714: s2 = ((NodeManager) n).getGUIName();
715: } else {
716: s2 = n.getFunctionValue("gui", null).toString();
717: }
718: Collator col = Collator.getInstance(getCloud().getLocale());
719: col.setStrength(Collator.PRIMARY);
720: int res = col.compare(s1, s2);
721: if (res != 0) {
722: return res;
723: } else {
724: res = s1.compareTo(s2);
725: if (res != 0)
726: return res;
727:
728: int n1 = getNumber();
729: int n2 = n.getNumber();
730: if (n2 > n1) {
731: return -1;
732: } else if (n2 < n1) {
733: return -1;
734: } else {
735: Cloud c = getCloud();
736: if (c instanceof Comparable) {
737: return ((Comparable<Cloud>) c).compareTo(n
738: .getCloud());
739: } else {
740: return 0;
741: }
742: }
743: }
744: }
745:
746: public boolean isNew() {
747: return false;
748: }
749:
750: public boolean isChanged(String fieldName) {
751: return false;
752: }
753:
754: public boolean isChanged() {
755: return false;
756: }
757:
758: public Set<String> getChanged() {
759: return Collections.emptySet();
760: }
761:
762: public void commit() {
763: throw new UnsupportedOperationException(
764: "Cannot edit virtual node");
765: }
766:
767: public void cancel() {
768: }
769:
770: public void delete(boolean deleteRelations) {
771: throw new UnsupportedOperationException(
772: "Cannot edit virtual node");
773: }
774:
775: public void deleteRelations(String type) throws NotFoundException {
776: }
777:
778: public RelationList getRelations(String role,
779: NodeManager nodeManager, String searchDir)
780: throws NotFoundException {
781: return BridgeCollections.EMPTY_RELATIONLIST;
782: }
783:
784: public RelationList getRelations(String role, String nodeManager)
785: throws NotFoundException {
786: return BridgeCollections.EMPTY_RELATIONLIST;
787: }
788:
789: public boolean hasRelations() {
790: return false;
791: }
792:
793: public int countRelatedNodes(NodeManager otherNodeManager,
794: String role, String direction) {
795: return 0;
796: }
797:
798: public int countRelatedNodes(String type) {
799: return 0;
800: }
801:
802: public NodeList getRelatedNodes(NodeManager nodeManager,
803: String role, String searchDir) {
804: return BridgeCollections.EMPTY_NODELIST;
805: }
806:
807: public StringList getAliases() {
808: return BridgeCollections.EMPTY_STRINGLIST;
809: }
810:
811: public void createAlias(String aliasName) {
812: throw new UnsupportedOperationException(
813: "Virtual nodes have no aliases");
814: }
815:
816: public void deleteAlias(String aliasName) {
817: throw new UnsupportedOperationException(
818: "Virtual nodes have no aliases");
819: }
820:
821: public void setContext(String context) {
822: throw new UnsupportedOperationException(
823: "Virtual nodes have no security context");
824: }
825:
826: // javadoc inherited (from Node)
827: public String getContext() {
828: throw new UnsupportedOperationException(
829: "Virtual nodes have no security context");
830: }
831:
832: // javadoc inherited (from Node)
833: public StringList getPossibleContexts() {
834: return BridgeCollections.EMPTY_STRINGLIST;
835: }
836:
837: public boolean mayWrite() {
838: return false;
839: }
840:
841: public boolean mayDelete() {
842: return false;
843: }
844:
845: public boolean mayChangeContext() {
846: return false;
847: }
848:
849: /**
850: * Compares two nodes, and returns true if they are equal.
851: * This effectively means that both objects are nodes, and they both have the same number and cloud
852: * @param o the object to compare it with
853: *
854: * @see java.lang.Object#equals(java.lang.Object)
855: */
856: @Override
857: public final boolean equals(Object o) {
858: return (o instanceof Node)
859: && getNumber() == ((Node) o).getNumber()
860: && getCloud().equals(((Node) o).getCloud());
861: }
862:
863: @Override
864: public final int hashCode() {
865: return 127 * getNumber();
866: }
867:
868: public Parameters createParameters(String functionName) {
869: return getNodeFunction(functionName).createParameters();
870: }
871:
872: protected FieldValue createFunctionValue(final Object result) {
873: return new AbstractFieldValue(this , getCloud()) {
874: @Override
875: public Object get() {
876: return result;
877: }
878: };
879: }
880:
881: public FieldValue getFunctionValue(String functionName,
882: List parameters) {
883: Function function = getFunction(functionName);
884: Parameters params = function.createParameters();
885: params.setAll(parameters);
886: return createFunctionValue(function.getFunctionValue(params));
887: }
888:
889: protected Function<?> getNodeFunction(String functionName) {
890: return null;
891: }
892:
893: public final Function<?> getFunction(String functionName) {
894: Function<?> function = getNodeFunction(functionName);
895: if (function == null) {
896: throw new NotFoundException("Function with name "
897: + functionName + " does not exist on node "
898: + getNumber() + " of type "
899: + getNodeManager().getName() + "(known are "
900: + getFunctions() + ")");
901: }
902: return new WrappedFunction(function) {
903: @Override
904: public final Object getFunctionValue(Parameters params) {
905: if (params == null)
906: params = createParameters();
907: params.setIfDefined(Parameter.NODE, AbstractNode.this);
908: params.setIfDefined(Parameter.CLOUD, AbstractNode.this
909: .getCloud());
910: return AbstractNode.this.createFunctionValue(
911: super.getFunctionValue(params)).get();
912: }
913: };
914: }
915:
916: }
|