KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > module > corebuilders > TypeRel


1 /*
2
3  This software is OSI Certified Open Source Software.
4  OSI Certified is a certification mark of the Open Source Initiative.
5
6  The license (Mozilla version 1.0) can be read at the MMBase site.
7  See http://www.MMBase.org/license
8
9  */

10 package org.mmbase.module.corebuilders;
11
12 import java.util.*;
13
14 import org.mmbase.bridge.Field;
15 import org.mmbase.util.*;
16 import org.mmbase.module.core.*;
17 import org.mmbase.core.CoreField;
18 import org.mmbase.core.event.NodeEvent;
19 import org.mmbase.core.util.Fields;
20 import org.mmbase.storage.search.implementation.BasicRelationStep;
21 import org.mmbase.storage.search.RelationStep;
22
23 import org.mmbase.util.logging.Logger;
24 import org.mmbase.util.logging.Logging;
25
26 /**
27  * TypeRel defines the allowed relations between two object types. Every relations also specifies a
28  * 'role', which is a reference to the RelDef table.
29  *
30  * Relations do principally have a 'source' and a 'destination' object type, but most functions of
31  * this class do ignore this distinction.
32  *
33  * TypeRel is a 'core' MMBase builder. You can get a reference to it via the MMBase instance.
34  *
35  * @author Daniel Ockeloen
36  * @author Pierre van Rooden
37  * @author Michiel Meeuwissen
38  * @version $Id: TypeRel.java,v 1.72.2.2 2006/10/20 07:51:53 nklasens Exp $
39  * @see RelDef
40  * @see InsRel
41  * @see org.mmbase.module.core.MMBase
42  */

43 public class TypeRel extends MMObjectBuilder implements MMBaseObserver {
44
45     private static final Logger log = Logging.getLoggerInstance(TypeRel.class);
46
47     /**
48      * Constant for {@link #contains}: return only typerels that exactly match.
49      */

50     public static final int STRICT = 0;
51
52     /**
53      * Constant for {@link #contains}: return typerels where source/destination match with a
54      * builder or its descendants
55      */

56     public static final int INCLUDE_DESCENDANTS = 1;
57
58     /**
59      * Constant for {@link #contains}: return typerels where source/destination match with a
60      * builder or its parents
61      */

62     public static final int INCLUDE_PARENTS = 2;
63
64     /**
65      * Constant for {@link #contains}: return typerels where source/destination match with a
66      * builder, its descendants, or its parents
67      */

68     public static final int INCLUDE_PARENTS_AND_DESCENDANTS = 3;
69
70     /**
71      * TypeRel should contain only a limited amount of nodes, so we can simply cache them all, and
72      * avoid all further querying.
73      */

74     protected TypeRelSet typeRelNodes; // for searching destinations
75

76     protected TypeRelSet parentTypeRelNodes; // for caching typerels for 'parent'
77

78     // builders
79

80     public InverseTypeRelSet inverseTypeRelNodes; // for searching sources
81

82     public boolean init() {
83         if (oType != -1) return true;
84         super.init();
85         // during init not yet all builder are available so inhertiance is not
86
// yet possible
87
// This means that calls to getAllowedRelations do not consider
88
// inheritance during initializion of MMBase.
89
// This occurs e.g. in one of the Community-builders.
90
readCache(false);
91         return true;
92     }
93
94     /**
95      * The TypeRel cache contains all TypeRels MMObjectNodes. Called after init by MMBase, and when
96      * something changes.
97      * @since MMBase-1.6.2
98      */

99     public void readCache() {
100         readCache(true);
101     }
102
103     /**
104      * @since MMBase-1.6.2
105      */

106     private void readCache(boolean buildersInitialized) {
107         log.debug("Reading in typerels");
108         typeRelNodes = new TypeRelSet();
109         parentTypeRelNodes = new TypeRelSet();
110         inverseTypeRelNodes = new InverseTypeRelSet();
111
112         TypeDef typeDef = mmb.getTypeDef();
113         typeDef.init();
114         // Find all typerel nodes
115
List alltypes = getNodes();
116         for (Iterator iter = alltypes.iterator(); iter.hasNext();) {
117             // For every reltype node :
118
MMObjectNode typerel = (MMObjectNode) iter.next();
119             addCacheEntry(typerel, buildersInitialized);
120         }
121         log.debug("Done reading typerel cache " + (buildersInitialized ? "(considered inheritance)" : "") + ": " + typeRelNodes);
122     }
123
124     /**
125      * Addes one typerel cache entries, plus inherited relations (if builder are initialized)
126      * @return A Set with the added entries, which can be used for logging or so, or can be
127      * disregarded
128      * @since MMBase-1.6.2
129      */

130     protected TypeRelSet addCacheEntry(MMObjectNode typeRel, boolean buildersInitialized) {
131
132         TypeRelSet added = new TypeRelSet(); // store temporary, which will enable nice logging of what happened
133

134         // Start to add the actual definition, this is then afterwards again,
135
// except if one of the builders could not be found
136
added.add(typeRel);
137
138         RelDef reldef = mmb.getRelDef();
139
140         MMObjectNode reldefNode = reldef.getNode(typeRel.getIntValue("rnumber"));
141         if (reldefNode == null) { throw new RuntimeException JavaDoc("Could not find reldef-node for rnumber= "
142             + typeRel.getIntValue("rnumber")); }
143
144         boolean bidirectional = (!InsRel.usesdir) || (reldefNode.getIntValue("dir") > 1);
145
146         inheritance: if (buildersInitialized) { // handle inheritance, which is
147
// not possible during
148
// initialization of MMBase.
149

150             TypeDef typeDef = mmb.getTypeDef();
151
152             String JavaDoc sourceBuilderName = typeDef.getValue(typeRel.getIntValue("snumber"));
153             MMObjectBuilder sourceBuilder = sourceBuilderName != null ? mmb.getBuilder(sourceBuilderName) : null;
154
155             String JavaDoc destinationBuilderName = typeDef.getValue(typeRel.getIntValue("dnumber"));
156             MMObjectBuilder destinationBuilder = destinationBuilderName != null ? mmb.getBuilder(destinationBuilderName) : null;
157
158             if (sourceBuilder == null) {
159                 if (destinationBuilder == null) {
160                     log.warn("Both source and destination of " + typeRel
161                              + " are not active builders. Cannot follow descendants.");
162                 } else {
163                     log.warn("The source of relation type " + typeRel
164                              + " is not an active builder. Cannot follow descendants.");
165                 }
166                 break inheritance;
167             }
168
169             if (destinationBuilder == null) {
170                 log.warn("The destination of relation type " + typeRel
171                          + " is not an active builder. Cannot follow descendants.");
172                 break inheritance;
173             }
174
175             int rnumber = typeRel.getIntValue("rnumber");
176
177             List sources = sourceBuilder.getDescendants();
178             sources.add(sourceBuilder);
179
180             List destinations = destinationBuilder.getDescendants();
181             destinations.add(destinationBuilder);
182
183             Iterator i = sources.iterator();
184             while (i.hasNext()) {
185                 MMObjectBuilder s = (MMObjectBuilder) i.next();
186                 Iterator j = destinations.iterator();
187                 while (j.hasNext()) {
188                     MMObjectBuilder d = (MMObjectBuilder) j.next();
189                     MMObjectNode vnode = new VirtualTypeRelNode(s.getNumber(), d.getNumber(), rnumber);
190                     added.add(vnode);
191                 }
192             }
193
194             // seek all parents and store typerels for them
195
// this cache is used by contains(INCLUDE_PARENTS /
196
// INCLUDE_PARENTS_AND_DESCENDANTS));
197
MMObjectBuilder sourceParent = sourceBuilder;
198             while (sourceParent != null) {
199                 MMObjectBuilder destinationParent = destinationBuilder;
200                 while (destinationParent != null) {
201                     MMObjectNode vnode = new VirtualTypeRelNode(sourceParent.getNumber(), destinationParent.getNumber(), rnumber);
202                     parentTypeRelNodes.add(vnode);
203                     destinationParent = destinationParent.getParentBuilder();
204                 }
205                 sourceParent = sourceParent.getParentBuilder();
206             }
207             added.add(typeRel); // replaces the ones added in the 'inheritance'
208
// loop (so now not any more Virtual)
209
}
210         Iterator i = added.iterator();
211         while (i.hasNext()) {
212             MMObjectNode node = (MMObjectNode) i.next();
213             if (! node.isVirtual()) {
214                 // make sure 'real' nodes replace virtual nodes. (real and virtual nodes are equal, so will not be added to set otherwise)
215
// This is especially essential whey you use STRICT in contains
216
typeRelNodes.remove(node);
217                 if (bidirectional) inverseTypeRelNodes.remove(node);
218             }
219             typeRelNodes.add(node);
220             if (bidirectional) inverseTypeRelNodes.add(node);
221         }
222         if (log.isDebugEnabled()) {
223             log.debug("Added to typerelcache: " + added);
224         }
225         return added;
226     }
227
228     /**
229      * Insert a new object (content provided) in the cloud, including an entry for the object alias
230      * (if provided). This method indirectly calls {@link #preCommit}. If the typerel node
231      * specified already exists (i.e. same snumber, dnumber,a nd rnumber fielfds), the typerel
232      * creation fails and returns -1.
233      * @param owner The administrator creating the node
234      * @param node The object to insert. The object need be of the same type as the current builder.
235      * @return An <code>int</code> value which is the new object's unique number, -1 if the insert
236      * failed.
237      */

238     public int insert(String JavaDoc owner, MMObjectNode node) {
239         int snumber = node.getIntValue("snumber");
240         int dnumber = node.getIntValue("dnumber");
241         int rnumber = node.getIntValue("rnumber");
242         if (contains(snumber, dnumber, rnumber, STRICT)) {
243             log.error("The typerel with snumber=" + snumber + ", dnumber=" + dnumber + ", rnumber=" + rnumber + " already exists");
244             throw new RuntimeException JavaDoc("The typerel with snumber=" + snumber + ", dnumber=" + dnumber + ", rnumber="
245                                        + rnumber + " already exists");
246         }
247         int res = super.insert(owner, node);
248         return res;
249     }
250
251     /**
252      * Remove a node from the cloud.
253      * @param node The node to remove.
254      */

255     public void removeNode(MMObjectNode node) {
256         super.removeNode(node);
257     }
258
259     /**
260      * Retrieves all relations which are 'allowed' for a specified node, that is, where the node is
261      * either allowed to be the source, or to be the destination (but where the corresponing
262      * relation definition is bidirectional). The allowed relations are determined by the type of
263      * the node
264      * @param node The node to retrieve the allowed relations of.
265      * @return An <code>Enumeration</code> of nodes containing the typerel relation data
266      */

267     public Enumeration getAllowedRelations(MMObjectNode node) {
268         return getAllowedRelations(node.getBuilder().getNumber());
269     }
270
271     public Enumeration getAllowedRelations(int otype) {
272         Set res = getAllowedRelations(otype, 0, 0, RelationStep.DIRECTIONS_BOTH);
273         return Collections.enumeration(res);
274     }
275
276     /**
277      * Retrieves all relations which are 'allowed' between two specified nodes. No distinction
278      * between source / destination.
279      * @param node1 The first objectnode
280      * @param node2 The second objectnode
281      * @return An <code>Enumeration</code> of nodes containing the typerel relation data
282      */

283     public Enumeration getAllowedRelations(MMObjectNode node1, MMObjectNode node2) {
284         return getAllowedRelations(node1.getOType(), node2.getOType());
285     }
286
287     /**
288      * An enumeration of all allowed relations between two builders. No distinction is made between
289      * source and destination.
290      *
291      */

292     public Enumeration getAllowedRelations(int builder1, int builder2) {
293         Set res = getAllowedRelations(builder1, builder2, 0, RelationStep.DIRECTIONS_BOTH);
294         return Collections.enumeration(res);
295     }
296
297     /**
298      * A Set of all allowed relations of a certain role between two builders. No distinction between
299      * source and destination.
300      *
301      * @since MMBase-1.6.2
302      */

303     public Set getAllowedRelations(int builder1, int builder2, int role) {
304         return getAllowedRelations(builder1, builder2, role, RelationStep.DIRECTIONS_BOTH);
305     }
306
307     /**
308      * A Set of all allowed relations of a certain role between two builders. Distinction is made between
309      * source and destination depending on passed directionality.
310      *
311      * @since MMBase-1.6.2
312      */

313     public Set getAllowedRelations(int builder1, int builder2, int role, int directionality) {
314         Set res = new HashSet();
315         if (directionality != RelationStep.DIRECTIONS_SOURCE) {
316             res.addAll(typeRelNodes.getBySourceDestinationRole(builder1, builder2, role));
317         }
318         if (directionality != RelationStep.DIRECTIONS_DESTINATION && (directionality != RelationStep.DIRECTIONS_EITHER || res.isEmpty())) {
319             res.addAll(inverseTypeRelNodes.getByDestinationSourceRole(builder2, builder1, role));
320         }
321         return res;
322     }
323
324     /**
325      * Retrieves all relations which are 'allowed' between two specified nodes.
326      * @param snum The first objectnode type (the source)
327      * @param dnum The second objectnode type (the destination)
328      * @return An <code>Enumeration</code> of nodes containing the reldef (not typerel!) sname
329      * field
330      */

331     protected Vector getAllowedRelationsNames(int snum, int dnum) {
332         Vector results = new Vector();
333         for (Enumeration e = getAllowedRelations(snum, dnum); e.hasMoreElements();) {
334             MMObjectNode node = (MMObjectNode) e.nextElement();
335             int rnumber = node.getIntValue("rnumber");
336             MMObjectNode snode = mmb.getRelDef().getNode(rnumber);
337             results.addElement(snode.getStringValue("sname"));
338         }
339         return results;
340     }
341
342     /**
343      * Retrieves the identifying number of the relation definition that is 'allowed' between two
344      * specified node types. The results are dependent on there being only one type of relation
345      * between two node types (not enforced, thus unpredictable). Makes use of a typeRelNodes.
346      * @param snum The first objectnode type (the source)
347      * @param dnum The second objectnode type (the destination)
348      * @return the number of the found relation, or -1 if either no relation was found, or more than
349      * one was found.
350      */

351     public int getAllowedRelationType(int snum, int dnum) {
352         Set set = new HashSet(typeRelNodes.getBySourceDestination(snum, dnum));
353         set.addAll(inverseTypeRelNodes.getByDestinationSource(dnum, snum));
354
355         if (set.size() != 1) {
356             return -1;
357         } else {
358             MMObjectNode n = (MMObjectNode) set.iterator().next();
359             return n.getIntValue("rnumber");
360         }
361     }
362
363     /**
364      * Returns the display string for this node It returns a commbination of objecttypes and
365      * rolename : "source->destination (role)".
366      * @param node Node from which to retrieve the data
367      * @return A <code>String</code> describing the content of the node
368      */

369     public String JavaDoc getGUIIndicator(MMObjectNode node) {
370         try {
371             String JavaDoc source = mmb.getTypeDef().getValue(node.getIntValue("snumber"));
372             String JavaDoc destination = mmb.getTypeDef().getValue(node.getIntValue("dnumber"));
373             MMObjectNode role = mmb.getRelDef().getNode(node.getIntValue("rnumber"));
374             return source + "->" + destination + " (" + (role != null ? role.getGUIIndicator() : "???") + ")";
375         } catch (Exception JavaDoc e) {
376             log.warn(e);
377         }
378         return null;
379     }
380
381     /**
382      * Returns the display string for a specified field. Returns, for snumber and dnumber, the name
383      * of the objecttype they represent, and for rnumber the display (GUI) string for the indicated
384      * relation definition.
385      * @param field The name of the field to retrieve
386      * @param node Node from which to retrieve the data
387      * @return A <code>String</code> describing the content of the field
388      */

389     public String JavaDoc getGUIIndicator(String JavaDoc field, MMObjectNode node) {
390         try {
391             if (field.equals("snumber")) {
392                 return mmb.getTypeDef().getValue(node.getIntValue("snumber"));
393             } else if (field.equals("dnumber")) {
394                 return mmb.getTypeDef().getValue(node.getIntValue("dnumber"));
395             } else if (field.equals("rnumber")) {
396                 MMObjectNode reldef = mmb.getRelDef().getNode(node.getIntValue("rnumber"));
397                 return (reldef != null ? reldef.getGUIIndicator() : "???");
398             }
399         } catch (Exception JavaDoc e) {}
400         return null;
401     }
402
403     /**
404      * Processes the BUILDER-typerel-ALLOWEDRELATIONSNAMES in the LIST command, and (possibly)
405      * returns a Vector containing requested data (based on the content of TYPE and NODE, which can
406      * be retrieved through tagger).
407      * @javadoc parameters
408      */

409     public Vector getList(PageInfo sp, StringTagger tagger, StringTokenizer tok) {
410         if (tok.hasMoreTokens()) {
411             String JavaDoc cmd = tok.nextToken(); //Retrieving command.
412
if (cmd.equals("ALLOWEDRELATIONSNAMES")) {
413                 try {
414                     String JavaDoc tmp = tagger.Value("TYPE");
415                     int number1 = mmb.getTypeDef().getIntValue(tmp);
416                     tmp = tagger.Value("NODE");
417                     int number2 = Integer.parseInt(tmp);
418                     MMObjectNode node = getNode(number2);
419                     return getAllowedRelationsNames(number1, node.getOType());
420                 } catch (Exception JavaDoc e) {
421                     log.error(e);
422                 }
423             }
424         }
425         return null;
426     }
427
428     /**
429      * Tests if a specific relation type is defined.
430      * <p>
431      * Note that this routine returns false both when a snumber/dnumber are swapped, and when a
432      * typecombo does not exist - it is not possible to derive whether one or the other has
433      * occurred.
434      * </p>
435      * @deprecated use {@link #contains}instead
436      * @param n1 The source type number.
437      * @param n2 The destination type number.
438      * @param r The relation definition (role) number, or -1 if the role does not matter
439      * @return <code>true</code> when the relation exists, false otherwise.
440      *
441      */

442     public boolean reldefCorrect(int n1, int n2, int r) {
443         return contains(n1, n2, r);
444     }
445
446     /**
447      * Tests if a specific relation type is defined. The method also returns true if the typerel
448      * occurs as a virtual (derived) node.
449      * <p>
450      * Note that this routine returns false both when a snumber/dnumber are swapped, and when a
451      * typecombo does not exist - it is not possible to derive whether one or the other has
452      * occurred.
453      * <p>
454      *
455      * @param n1 The source type number.
456      * @param n2 The destination type number.
457      * @param r The relation definition (role) number, or -1 if the role does not matter
458      * @return <code>true</code> when the relation exists, false otherwise.
459      *
460      * @since MMBase-1.6.2
461      */

462     public boolean contains(int n1, int n2, int r) {
463         return contains(n1, n2, r, INCLUDE_DESCENDANTS);
464     }
465
466     /**
467      * Tests if a specific relation type is defined.
468      * <p>
469      * Note that this routine returns false both when a snumber/dnumber are swapped, and when a
470      * typecombo does not exist - it is not possible to derive whether one or the other has
471      * occurred.
472      * <p>
473      *
474      * @param n1 The source type number.
475      * @param n2 The destination type number.
476      * @param r The relation definition (role) number, or -1 if the role does not matter r can only
477      * be -1 if virtual is <code>true</code>
478      * @param restriction if {@link #STRICT}, contains only returns true if the typerel occurs
479      * as-is in the database. if {@link #INCLUDE_DESCENDANTS}, contains returns true if the typerel
480      * occurs as a virtual (derived) node, where source or destination may also be descendants of
481      * the specified type. if {@link #INCLUDE_PARENTS}, contains returns true if the typerel occurs
482      * as a virtual (derived) node, where source or destination may also be parents of the specified
483      * type. if {@link #INCLUDE_PARENTS_AND_DESCENDANTS}, contains returns true if the typerel
484      * occurs as a virtual (derived) node, where source or destination may also be descendants or
485      * parents of the specified type.
486      * @return <code>true</code> when the relation exists, false otherwise.
487      *
488      * @since MMBase-1.6.2
489      */

490     public boolean contains(int n1, int n2, int r, int restriction) {
491         switch (restriction) {
492         case INCLUDE_DESCENDANTS:
493             return typeRelNodes.contains(new VirtualTypeRelNode(n1, n2, r));
494         case INCLUDE_PARENTS:
495             return parentTypeRelNodes.contains(new VirtualTypeRelNode(n1, n2, r));
496         case INCLUDE_PARENTS_AND_DESCENDANTS:
497             return typeRelNodes.contains(new VirtualTypeRelNode(n1, n2, r))
498                 || parentTypeRelNodes.contains(new VirtualTypeRelNode(n1, n2, r));
499         case STRICT:
500             SortedSet existingNodes = typeRelNodes.getBySourceDestinationRole(n1, n2, r);
501             return (existingNodes.size() > 0 && !((MMObjectNode) existingNodes.first()).isVirtual());
502         default:
503             log.error("Unknown restriction " + restriction);
504             return false;
505         }
506     }
507
508     /**
509      * Watch for changes on relation types and adjust our memory table accordingly
510      * @todo Should update artCache en relDefCorrectCache as wel
511      */

512     /*
513      * (non-Javadoc)
514      * @see org.mmbase.module.core.MMObjectBuilder#notify(org.mmbase.core.event.NodeEvent)
515      */

516     public void notify(NodeEvent event) {
517         if (log.isDebugEnabled()) {
518             log.debug("Changed " + event.getMachine() + " " + event.getNodeNumber() + " "
519                 + event.getBuilderName() + " " + NodeEvent.newTypeToOldType(event.getType()));
520         }
521         if (tableName.equals(event.getBuilderName())) {
522             if (event.getType() == NodeEvent.TYPE_NEW) {
523                 Set newTypeRels = addCacheEntry(getNode(event.getNodeNumber()), true);
524                 log.service("Added to typerelcache: " + newTypeRels);
525             } else {
526                 //something else changed in a typerel node? reread the complete typeRelNodes Set
527
readCache();
528             }
529         }
530         super.notify(event);
531     }
532
533     /**
534      * Optimize as relation step by considering restrictions of TypeRel. TypeRel defines which type
535      * of relations may be created, ergo can exist.
536      *
537      * @since MMBase-1.7
538      */

539     public boolean optimizeRelationStep(BasicRelationStep relationStep, int sourceType, int destinationType, int roleInt, int searchDir) {
540         // Determine in what direction(s) this relation can be followed:
541

542         // Check directionality is requested and supported.
543
if (searchDir != RelationStep.DIRECTIONS_ALL && InsRel.usesdir) {
544             relationStep.setCheckedDirectionality(true);
545         }
546
547         // this is a bit confusing, can the simple cases like explicit 'source'
548
// or 'destination' not be handled first?
549

550         boolean sourceToDestination =
551             searchDir != RelationStep.DIRECTIONS_SOURCE
552             && contains(sourceType, destinationType, roleInt, INCLUDE_PARENTS_AND_DESCENDANTS);
553         boolean destinationToSource =
554             searchDir != RelationStep.DIRECTIONS_DESTINATION
555             && contains(destinationType, sourceType, roleInt, INCLUDE_PARENTS_AND_DESCENDANTS);
556
557         if (destinationToSource && sourceToDestination && (searchDir == RelationStep.DIRECTIONS_EITHER)) {
558             // support old
559
destinationToSource = false;
560         }
561
562         if (destinationToSource) {
563             // there is a typed relation from destination to src
564
if (sourceToDestination) {
565                 // there is ALSO a typed relation from src to destination - make
566
// a more complex query
567
relationStep.setDirectionality(RelationStep.DIRECTIONS_BOTH);
568             } else {
569                 // there is ONLY a typed relation from destination to src -
570
// optimized query
571
relationStep.setDirectionality(RelationStep.DIRECTIONS_SOURCE);
572             }
573         } else {
574             if (sourceToDestination) {
575                 // there is no typed relation from destination to src (assume a
576
// relation between src and destination) - optimized query
577
relationStep.setDirectionality(RelationStep.DIRECTIONS_DESTINATION);
578             } else {
579                 // no results possible, do something any way
580
if (searchDir == RelationStep.DIRECTIONS_SOURCE) {
581                     // explicitely asked for source, it would be silly to try destination now
582
relationStep.setDirectionality(RelationStep.DIRECTIONS_SOURCE);
583                 } else {
584                     // the 'normal' way
585
relationStep.setDirectionality(RelationStep.DIRECTIONS_DESTINATION);
586                 }
587                 return false;
588             }
589         }
590         return true;
591     }
592
593     /**
594      * Implements equals for a typerel node. Two nodes are equal if the snumber and dnumber fields
595      * are the same, and the rnumber fields are the same, or one of these is '-1' (don't care).
596      * @since MMBase-1.6.2
597      */

598     public boolean equals(MMObjectNode o1, MMObjectNode o2) {
599         if (o2.getBuilder() instanceof TypeRel) {
600             int r1 = o1.getIntValue("rnumber");
601             int r2 = o2.getIntValue("rnumber");
602             return o1.getIntValue("snumber") == o2.getIntValue("snumber")
603                 && o1.getIntValue("dnumber") == o2.getIntValue("dnumber") && (r1 == -1 || r2 == -1 || r1 == r2);
604         }
605         return false;
606     }
607
608     /**
609      * Implements for MMObjectNode
610      * @since MMBase-1.6.2
611      */

612     public int hashCode(MMObjectNode o) {
613         int result = 0;
614         result = HashCodeUtil.hashCode(result, o.getIntValue("snumber"));
615         result = HashCodeUtil.hashCode(result, o.getIntValue("dnumber"));
616         result = HashCodeUtil.hashCode(result, o.getIntValue("rnumber"));
617         return result;
618     }
619
620     public String JavaDoc toString(MMObjectNode n) {
621         try {
622             int snumber = n.getIntValue("snumber");
623             int dnumber = n.getIntValue("dnumber");
624             int rnumber = n.getIntValue("rnumber");
625
626             String JavaDoc sourceName = mmb.getTypeDef().getValue(snumber);
627             String JavaDoc destName = mmb.getTypeDef().getValue(dnumber);
628
629             if (sourceName == null) sourceName = "unknown builder '" + snumber + "'";
630             if (destName == null) destName = "unknown builder '" + dnumber + "'";
631
632             // unfilled should only happen during creation of the node.
633
String JavaDoc source = snumber > -1 ? sourceName : "[unfilled]";
634             String JavaDoc destination = dnumber > -1 ? destName : "[unfilled]";
635             MMObjectNode role = rnumber > -1 ? mmb.getRelDef().getNode(rnumber) : null;
636             return source + "->" + destination + " (" + (role != null ? role.getStringValue("sname") : "???") + ") " + (isVirtual() ? "(virtual)" : "");
637         } catch (Exception JavaDoc e) {
638             log.warn(e);
639         }
640         return "typerel-node";
641     }
642
643     /**
644      * Of course, virtual typerel nodes need a virtual typerel builder. Well 'of course', the reason
645      * is not quite obvious to me, it has to do with the bridge/temporarynodemanager which sometimes
646      * needs to know it.
647      *
648      * @since MMBase-1.6.2
649      */

650     static class VirtualTypeRel extends TypeRel {
651         static VirtualTypeRel virtualTypeRel = null;
652
653         VirtualTypeRel(TypeRel t) {
654             mmb = t.getMMBase();
655             CoreField field = Fields.createField("snumber", Field.TYPE_NODE, Field.TYPE_UNKNOWN, Field.STATE_VIRTUAL, null);
656             field.finish();
657             addField(field);
658             field = Fields.createField("dnumber", Field.TYPE_NODE, Field.TYPE_UNKNOWN, Field.STATE_VIRTUAL, null);
659             field.finish();
660             addField(field);
661             field = Fields.createField("rnumber", Field.TYPE_NODE, Field.TYPE_UNKNOWN, Field.STATE_VIRTUAL, null);
662             field.finish();
663             addField(field);
664             tableName = "virtual_typerel";
665             virtual = true;
666         }
667
668         static VirtualTypeRel getVirtualTypeRel(TypeRel t) {
669             if (virtualTypeRel == null) virtualTypeRel = new VirtualTypeRel(t);
670             return virtualTypeRel;
671         }
672     }
673
674     /**
675      * A TypeRelSet is a Set of typerel nodes. The TypeRel builder maintains such a Set of all
676      * typerel nodes for quick reference. TypeRelSets are also instantiated when doing queries on
677      * TypeRel like getAllowedRelations(MMObjectBuilder) etc.
678      *
679      * @since MMBase-1.6.2
680      */

681     protected class TypeRelSet extends TreeSet {
682         protected TypeRelSet() {
683             super(new Comparator() {
684                 // sorted by source, destination, role
685
public int compare(Object JavaDoc o1, Object JavaDoc o2) {
686                     MMObjectNode n1 = (MMObjectNode) o1;
687                     MMObjectNode n2 = (MMObjectNode) o2;
688
689                     int i1 = n1.getIntValue("snumber");
690                     int i2 = n2.getIntValue("snumber");
691                     if (i1 != i2) return i1 - i2;
692
693                     i1 = n1.getIntValue("dnumber");
694                     i2 = n2.getIntValue("dnumber");
695                     if (i1 != i2) return i1 - i2;
696
697                     i1 = n1.getIntValue("rnumber");
698                     i2 = n2.getIntValue("rnumber");
699                     if (i1 > 0 && i2 > 0 && i1 != i2) return i1 - i2;
700
701                     return 0;
702                 }
703             });
704         }
705
706         // make sure only MMObjectNode's are added
707
public boolean add(Object JavaDoc object) {
708             MMObjectNode node = (MMObjectNode) object;
709             return super.add(node);
710         }
711
712         // find some subsets:
713
SortedSet getBySource(MMObjectBuilder source) {
714             return getBySourceDestinationRole(source.getNumber(), 0, 0);
715         }
716
717         SortedSet getBySource(int source) {
718             return getBySourceDestinationRole(source, 0, 0);
719         }
720
721         SortedSet getBySourceDestination(MMObjectBuilder source, MMObjectBuilder destination) {
722             return getBySourceDestinationRole(source.getNumber(), destination.getNumber(), 0);
723         }
724
725         SortedSet getBySourceDestination(int source, int destination) {
726             return getBySourceDestinationRole(source, destination, 0);
727         }
728
729         SortedSet getBySourceDestinationRole(int source, int destination, int role) {
730             // determine minimum value - corrects in case '-1' (common MMBase value for N.A.) is passed
731
int roleMin = role <= 0 ? 0 : role;
732             int destinationMin = destination <= 0 ? 0 : destination;
733             int sourceMin = source <= 0 ? 0 : source;
734
735             // determine maximum value
736
int roleMax = role <= 0 ? 0 : role + 1; // i.e. source, destination, role
737
int destinationMax = role <= 0 ? destination + 1 : destination; // i.e. source, destination, 0
738
int sourceMax = (destination <= 0 && role <= 0) ? (source <= 0 ? 0 : source + 1) : source; // i.e. source, 0, 0
739

740             VirtualTypeRelNode fromTypeRelNode = new VirtualTypeRelNode(sourceMin, destinationMin, roleMin);
741             VirtualTypeRelNode toTypeRelNode = new VirtualTypeRelNode(sourceMax, destinationMax, roleMax);
742
743             SortedSet allowed = subSet(fromTypeRelNode, toTypeRelNode);
744             return Collections.unmodifiableSortedSet(allowed);
745         }
746
747     }
748
749     /**
750      * An InverseTypeRelSet is a Set of typerel nodes. The TypeRel builder maintains such a Set of
751      * all typerel nodes for quick reference.
752      *
753      * @since MMBase-1.6.2
754      */

755     protected class InverseTypeRelSet extends TreeSet {
756
757         protected InverseTypeRelSet() {
758             super(new Comparator() {
759                 // sorted by destination, source, role
760
public int compare(Object JavaDoc o1, Object JavaDoc o2) {
761                     MMObjectNode n1 = (MMObjectNode) o1;
762                     MMObjectNode n2 = (MMObjectNode) o2;
763
764                     int i1 = n1.getIntValue("dnumber");
765                     int i2 = n2.getIntValue("dnumber");
766                     if (i1 != i2) return i1 - i2;
767
768                     i1 = n1.getIntValue("snumber");
769                     i2 = n2.getIntValue("snumber");
770                     if (i1 != i2) return i1 - i2;
771
772                     i1 = n1.getIntValue("rnumber");
773                     i2 = n2.getIntValue("rnumber");
774                     if (i1 != -1 && i2 != -1 && i1 != i2) return i1 - i2;
775                     return 0;
776                 }
777             });
778         }
779
780         // make sure only MMObjectNode's are added
781
public boolean add(Object JavaDoc object) {
782             return super.add(object);
783         }
784
785         SortedSet getByDestination(MMObjectBuilder destination) {
786             return getByDestinationSourceRole(0, destination.getNumber(), 0);
787         }
788
789         SortedSet getByDestination(int destination) {
790             return getByDestinationSourceRole(0, destination, 0);
791         }
792
793         SortedSet getByDestinationSource(MMObjectBuilder source, MMObjectBuilder destination) {
794             return getByDestinationSourceRole(source.getNumber(), destination.getNumber(), 0);
795         }
796
797         SortedSet getByDestinationSource(int source, int destination) {
798             return getByDestinationSourceRole(source, destination, 0);
799         }
800
801         SortedSet getByDestinationSourceRole(int source, int destination, int role) {
802             // determine minimum value - corrects in case '-1' (common MMBase value for N.A.) is passed
803
int roleMin = role <= 0 ? 0 : role;
804             int sourceMin = source <= 0 ? 0 : source;
805             int destinationMin = destination <= 0 ? 0 : destination;
806
807             // determine maximum value
808
int roleMax = role <= 0 ? 0 : role + 1; // i.e. source, destination, role
809
int sourceMax = role <= 0 ? (source <= 0 ? 0 : source + 1) : source; // i.e. source, destination, 0
810
int destinationMax = (source <= 0 && role <= 0) ? destination + 1 : destination; // i.e. 0, destination, 0
811

812             return Collections.unmodifiableSortedSet(subSet(new VirtualTypeRelNode(sourceMin, destinationMin, roleMin),
813                                                             new VirtualTypeRelNode(sourceMax, destinationMax, roleMax)));
814         }
815
816     }
817
818     /**
819      * A VirtualTypeRelNode is a MMObjectNode which is added to the typerelset with extensions of
820      * the actual builders specified. So these entries are not in the database.
821      *
822      * @since MMBase-1.6.2
823      */

824     protected class VirtualTypeRelNode extends VirtualNode {
825
826         VirtualTypeRelNode(int snumber, int dnumber) { // only for use in lookups
827
// We don't use this-constructor because some jvm get confused then
828
super(VirtualTypeRel.getVirtualTypeRel(TypeRel.this));
829             setValue("snumber", snumber);
830             setValue("dnumber", dnumber);
831             setValue("rnumber", -1);
832         }
833
834         VirtualTypeRelNode(int snumber) { // only for use in lookups
835
// We don't use this-constructor because some jvm get confused then
836
super(VirtualTypeRel.getVirtualTypeRel(TypeRel.this));
837             setValue("snumber", snumber);
838             setValue("dnumber", -1);
839             setValue("rnumber", -1);
840         }
841         VirtualTypeRelNode(int snumber, int dnumber, int rnumber) {
842             super(VirtualTypeRel.getVirtualTypeRel(TypeRel.this));
843             setValue("snumber", snumber);
844             setValue("dnumber", dnumber);
845             setValue("rnumber", rnumber);
846             values = Collections.unmodifiableMap(values); // make sure it is not changed any more!
847
}
848     }
849
850
851 }
852
853
Popular Tags