* Fix several bugs in grouped collections:
<http://helma.org/bugs/show_bug.cgi?id=597> <http://helma.org/bugs/show_bug.cgi?id=614> <http://helma.org/bugs/show_bug.cgi?id=615>
This commit is contained in:
		
							parent
							
								
									7d80ab10e0
								
							
						
					
					
						commit
						ae3c331c7d
					
				
					 2 changed files with 95 additions and 49 deletions
				
			
		|  | @ -124,7 +124,10 @@ public final class DbMapping { | ||||||
|     HashSet dependentMappings = new HashSet(); |     HashSet dependentMappings = new HashSet(); | ||||||
| 
 | 
 | ||||||
|     // does this DbMapping describe a virtual node (collection, mountpoint, groupnode)? |     // does this DbMapping describe a virtual node (collection, mountpoint, groupnode)? | ||||||
|     private boolean virtual = false; |     private boolean isVirtual = false; | ||||||
|  | 
 | ||||||
|  |     // does this Dbmapping describe a group node? | ||||||
|  |     private boolean isGroup = false; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Create an internal DbMapping used for "virtual" mappings aka collections, mountpoints etc. |      * Create an internal DbMapping used for "virtual" mappings aka collections, mountpoints etc. | ||||||
|  | @ -132,7 +135,7 @@ public final class DbMapping { | ||||||
|     public DbMapping(Application app, String parentTypeName) { |     public DbMapping(Application app, String parentTypeName) { | ||||||
|         this(app, parentTypeName, null); |         this(app, parentTypeName, null); | ||||||
|         // DbMappings created with this constructor always define virtual nodes |         // DbMappings created with this constructor always define virtual nodes | ||||||
|         virtual = true; |         isVirtual = true; | ||||||
|         if (parentTypeName != null) { |         if (parentTypeName != null) { | ||||||
|             parentMapping = app.getDbMapping(parentTypeName); |             parentMapping = app.getDbMapping(parentTypeName); | ||||||
|             if (parentMapping == null) { |             if (parentMapping == null) { | ||||||
|  | @ -756,9 +759,9 @@ public final class DbMapping { | ||||||
|      * db-mapping with the right relations to create the group-by nodes |      * db-mapping with the right relations to create the group-by nodes | ||||||
|      */ |      */ | ||||||
|     public synchronized DbMapping getGroupbyMapping() { |     public synchronized DbMapping getGroupbyMapping() { | ||||||
|         if ((subRelation == null)  && (parentMapping != null)) { |         if ((subRelation == null) && (parentMapping != null)) { | ||||||
|             return parentMapping.getGroupbyMapping(); |             return parentMapping.getGroupbyMapping(); | ||||||
|         } else if (subRelation.groupby == null) { |         } else if (subRelation == null || subRelation.groupby == null) { | ||||||
|             return null; |             return null; | ||||||
|         } else if (groupbyMapping == null) { |         } else if (groupbyMapping == null) { | ||||||
|             initGroupbyMapping(); |             initGroupbyMapping(); | ||||||
|  | @ -774,6 +777,7 @@ public final class DbMapping { | ||||||
|         // if a prototype is defined for groupby nodes, use that |         // if a prototype is defined for groupby nodes, use that | ||||||
|         // if mapping doesn' exist or isn't defined, create a new (anonymous internal) one |         // if mapping doesn' exist or isn't defined, create a new (anonymous internal) one | ||||||
|         groupbyMapping = new DbMapping(app, subRelation.groupbyPrototype); |         groupbyMapping = new DbMapping(app, subRelation.groupbyPrototype); | ||||||
|  |         groupbyMapping.isGroup = true; | ||||||
| 
 | 
 | ||||||
|         // set subnode and property relations |         // set subnode and property relations | ||||||
|         groupbyMapping.subRelation = subRelation.getGroupbySubnodeRelation(); |         groupbyMapping.subRelation = subRelation.getGroupbySubnodeRelation(); | ||||||
|  | @ -1596,6 +1600,14 @@ public final class DbMapping { | ||||||
|      * @return true if this instance describes a virtual node. |      * @return true if this instance describes a virtual node. | ||||||
|      */ |      */ | ||||||
|     public boolean isVirtual() { |     public boolean isVirtual() { | ||||||
|         return virtual; |         return isVirtual; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Find if this DbMapping describes a group node. | ||||||
|  |      * @return true if this instance describes a group node. | ||||||
|  |      */ | ||||||
|  |     public boolean isGroup() { | ||||||
|  |         return isGroup; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -869,26 +869,10 @@ public final class Node implements INode, Serializable { | ||||||
|         loadNodes(); |         loadNodes(); | ||||||
| 
 | 
 | ||||||
|         // check if this node has a group-by subnode-relation |         // check if this node has a group-by subnode-relation | ||||||
|         if (dbmap != null) { |         INode groupbyNode = getGroupbySubnode(node, true); | ||||||
|             Relation srel = dbmap.getSubnodeRelation(); |         if (groupbyNode != null) { | ||||||
| 
 |             groupbyNode.addNode(node); | ||||||
|             if ((srel != null) && (srel.groupby != null)) { |             return node; | ||||||
|                 Relation groupbyRel = srel.otherType.columnNameToRelation(srel.groupby); |  | ||||||
|                 String groupbyProp = (groupbyRel != null) ? groupbyRel.propName |  | ||||||
|                                                               : srel.groupby; |  | ||||||
|                 String groupbyValue = node.getString(groupbyProp); |  | ||||||
|                 INode groupbyNode = (INode) getChildElement(groupbyValue); |  | ||||||
| 
 |  | ||||||
|                 // if group-by node doesn't exist, we'll create it |  | ||||||
|                 if (groupbyNode == null) { |  | ||||||
|                     groupbyNode = getGroupbySubnode(groupbyValue, true); |  | ||||||
|                 } else { |  | ||||||
|                     groupbyNode.setDbMapping(dbmap.getGroupbyMapping()); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 groupbyNode.addNode(node); |  | ||||||
|                 return node; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         NodeHandle nhandle = node.getHandle(); |         NodeHandle nhandle = node.getHandle(); | ||||||
|  | @ -1198,6 +1182,38 @@ public final class Node implements INode, Serializable { | ||||||
|         return retval; |         return retval; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     protected Node getGroupbySubnode(Node node, boolean create) { | ||||||
|  |         if (node.dbmap != null && node.dbmap.isGroup()) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (dbmap != null) { | ||||||
|  |             Relation srel = dbmap.getSubnodeRelation(); | ||||||
|  | 
 | ||||||
|  |             if ((srel != null) && (srel.groupby != null)) { | ||||||
|  |                 Relation groupbyRel = srel.otherType.columnNameToRelation(srel.groupby); | ||||||
|  |                 String groupbyProp = (groupbyRel != null) ? groupbyRel.propName | ||||||
|  |                                                               : srel.groupby; | ||||||
|  |                 String groupbyValue = node.getString(groupbyProp); | ||||||
|  |                 Node groupbyNode = (Node) getChildElement(groupbyValue); | ||||||
|  | 
 | ||||||
|  |                 // if group-by node doesn't exist, we'll create it | ||||||
|  |                 if (groupbyNode == null) { | ||||||
|  |                     groupbyNode = getGroupbySubnode(groupbyValue, create); | ||||||
|  |                     // mark subnodes as changed as we have a new group node | ||||||
|  |                     if (create && groupbyNode != null) { | ||||||
|  |                         Transactor.getInstance().visitParentNode(this); | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     groupbyNode.setDbMapping(dbmap.getGroupbyMapping()); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return groupbyNode; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * |      * | ||||||
|      * |      * | ||||||
|  | @ -1211,10 +1227,7 @@ public final class Node implements INode, Serializable { | ||||||
|             throw new IllegalArgumentException("Can't create group by null"); |             throw new IllegalArgumentException("Can't create group by null"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (state == TRANSIENT) { |         boolean persistent = state != TRANSIENT; | ||||||
|             throw new RuntimeException("Can't add grouped child on transient node. "+ |  | ||||||
|                                        "Make parent persistent before adding grouped nodes."); |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         loadNodes(); |         loadNodes(); | ||||||
| 
 | 
 | ||||||
|  | @ -1228,34 +1241,40 @@ public final class Node implements INode, Serializable { | ||||||
|                 boolean relational = groupbyMapping.getSubnodeMapping().isRelational(); |                 boolean relational = groupbyMapping.getSubnodeMapping().isRelational(); | ||||||
| 
 | 
 | ||||||
|                 if (relational || create) { |                 if (relational || create) { | ||||||
|                     Node node = relational ? new Node(this, sid, nmgr, null) |                     Node node = relational && persistent ? | ||||||
|                                            : new Node(sid, null, nmgr); |                             new Node(this, sid, nmgr, null) : | ||||||
|  |                             new Node(sid, null, nmgr); | ||||||
| 
 | 
 | ||||||
|                     // set "groupname" property to value of groupby field |                     // set "groupname" property to value of groupby field | ||||||
|                     node.setString("groupname", sid); |                     node.setString("groupname", sid); | ||||||
| 
 |                     // Set the dbmapping on the group node | ||||||
|                     node.setDbMapping(groupbyMapping); |                     node.setDbMapping(groupbyMapping); | ||||||
|  |                     node.setPrototype(groupbyMapping.getTypeName()); | ||||||
| 
 | 
 | ||||||
|                     if (!relational) { |                     // if we're relational and persistent, make new node persistable | ||||||
|                         // if we're not transient, make new node persistable |                     if (!relational && persistent) { | ||||||
|                         if (state != TRANSIENT) { |                         node.makePersistable(); | ||||||
|                             node.makePersistable(); |                         node.checkWriteLock(); | ||||||
|                             node.checkWriteLock(); |                     } | ||||||
|                         } | 
 | ||||||
|                         subnodes.add(node.getHandle()); |                     // if we created a new node, check if we need to add it to subnodes | ||||||
|  |                     if (create) { | ||||||
|  |                         NodeHandle handle = node.getHandle(); | ||||||
|  |                         if (!subnodes.contains(handle)) | ||||||
|  |                             subnodes.add(handle); | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     // Set the dbmapping on the group node |  | ||||||
|                     node.setPrototype(groupbyMapping.getTypeName()); |  | ||||||
|                     // If we created the group node, we register it with the |                     // If we created the group node, we register it with the | ||||||
|                     // nodemanager. Otherwise, we just evict whatever was there before |                     // nodemanager. Otherwise, we just evict whatever was there before | ||||||
|                     if (create) { |                     if (persistent) { | ||||||
|                         // register group node with transactor |                         if (create) { | ||||||
|                         Transactor tx = Transactor.getInstanceOrFail(); |                             // register group node with transactor | ||||||
|                         tx.visitCleanNode(node); |                             Transactor tx = Transactor.getInstanceOrFail(); | ||||||
|                         nmgr.registerNode(node); |                             tx.visitCleanNode(node); | ||||||
|                     } else { |                             nmgr.registerNode(node); | ||||||
|                         nmgr.evictKey(node.getKey()); |                         } else { | ||||||
|  |                             nmgr.evictKey(node.getKey()); | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     return node; |                     return node; | ||||||
|  | @ -1299,6 +1318,13 @@ public final class Node implements INode, Serializable { | ||||||
|      * {@link #removeNode(INode)}. |      * {@link #removeNode(INode)}. | ||||||
|      */ |      */ | ||||||
|     protected void releaseNode(Node node) { |     protected void releaseNode(Node node) { | ||||||
|  | 
 | ||||||
|  |         Node groupNode = getGroupbySubnode(node, false); | ||||||
|  |         if (groupNode != null) { | ||||||
|  |             groupNode.releaseNode(node); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         INode parent = node.getParent(); |         INode parent = node.getParent(); | ||||||
| 
 | 
 | ||||||
|         checkWriteLock(); |         checkWriteLock(); | ||||||
|  | @ -1314,7 +1340,9 @@ public final class Node implements INode, Serializable { | ||||||
|             synchronized (subnodes) { |             synchronized (subnodes) { | ||||||
|                 removed = subnodes.remove(node.getHandle()); |                 removed = subnodes.remove(node.getHandle()); | ||||||
|             } |             } | ||||||
|             if (removed) { |             if (dbmap != null && dbmap.isGroup() && subnodes.size() == 0) { | ||||||
|  |                 remove(); | ||||||
|  |             } else if (removed) { | ||||||
|                 registerSubnodeChange(); |                 registerSubnodeChange(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -1341,6 +1369,12 @@ public final class Node implements INode, Serializable { | ||||||
|                             nmgr.evictKey(new SyntheticKey(getKey(), prop)); |                             nmgr.evictKey(new SyntheticKey(getKey(), prop)); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|  |                 } else if (prel.groupby != null) { | ||||||
|  |                     String prop = node.getString("groupname"); | ||||||
|  |                     if (prop != null && state != TRANSIENT) { | ||||||
|  |                         nmgr.evictKey(new SyntheticKey(getKey(), prop)); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|                 } |                 } | ||||||
|                 // TODO: We should unset constraints to actually remove subnodes here, |                 // TODO: We should unset constraints to actually remove subnodes here, | ||||||
|                 // but omit it by convention and to keep backwards compatible. |                 // but omit it by convention and to keep backwards compatible. | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue