docs: @SuppressWarnings( "fallthrough" )
      private void versionNodeAt( AbstractJcrNode node,
                                  Name nodeName, 
                                  MutableCachedNode parentInVersionHistory,
                                  boolean forceCopy,
                                  SessionCache nodeCache,
                                  SessionCache versionHistoryCache ) throws RepositoryException {
          int onParentVersion = 0;
          if (forceCopy) {
              onParentVersion = OnParentVersionAction.COPY;
          } else {
              onParentVersion = node.getDefinition().getOnParentVersion();
          }
  
          NodeKey key = parentInVersionHistory.getKey().withRandomId();
  
          switch (onParentVersion) {
              case OnParentVersionAction.ABORT:
                  throw new VersionException(JcrI18n.cannotCheckinNodeWithAbortChildNode.text(nodeName, 
                                                                                              node.getParent().getName()));
              case OnParentVersionAction.VERSION:
                  if (node.isNodeType(JcrMixLexicon.VERSIONABLE)) {
                      // The frozen node should reference the version history of the node ...
                      JcrVersionHistoryNode history = node.getVersionHistory();
                      org.modeshape.jcr.value.Property primaryType = propertyFactory().create(JcrLexicon.PRIMARY_TYPE,
                                                                                              JcrNtLexicon.VERSIONED_CHILD);
                      Reference childVersionHistoryValue = session.referenceFactory().create(history.key(), true);                    
                      org.modeshape.jcr.value.Property childVersionHistory = propertyFactory().create(
                              JcrLexicon.CHILD_VERSION_HISTORY,
                              childVersionHistoryValue);
                      parentInVersionHistory.createChild(versionHistoryCache, key, nodeName, primaryType, childVersionHistory);
                      return;
                  }
  
                  // Otherwise, treat it as a copy, as per section 3.13.9 bullet item 5 in JSR-283, so DO NOT break ...
              case OnParentVersionAction.COPY:
                  // Per section 3.13.9 item 5 in JSR-283, an OPV of COPY or VERSION (if not versionable)
                  // results in COPY behavior "regardless of the OPV values of the sub-items".
                  // We can achieve this by making the onParentVersionAction always COPY for the
                  // recursive call ...
                  forceCopy = true;
  
                  PropertyFactory factory = propertyFactory();
                  List<Property> props = new LinkedList<Property>();
  
                  if (node.isShared()) {
                      // This is a shared node, so we should store a proxy to the shareable node ...
                      props.add(factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FROZEN_NODE));
                      props.add(factory.create(JcrLexicon.FROZEN_PRIMARY_TYPE, ModeShapeLexicon.SHARE));
                      props.add(factory.create(JcrLexicon.FROZEN_UUID, node.getIdentifier()));
                      props.add(factory.create(JcrLexicon.UUID, key));
                      parentInVersionHistory.createChild(versionHistoryCache, key, nodeName, props);
  
                      // The proxies to shareable nodes never have children (nor versionable properties), so we're done ...
                      return;
                  }
  
                  // But the copy needs to be a 'nt:frozenNode', so that it doesn't compete with the actual node
                  // (outside of version history) ...
                  Name primaryTypeName = node.getPrimaryTypeName();
                  Set<Name> mixinTypeNames = node.getMixinTypeNames();
                  props.add(factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FROZEN_NODE));
                  props.add(factory.create(JcrLexicon.FROZEN_PRIMARY_TYPE, primaryTypeName));
                  props.add(factory.create(JcrLexicon.FROZEN_MIXIN_TYPES, mixinTypeNames));
                  props.add(factory.create(JcrLexicon.FROZEN_UUID, node.getIdentifier()));
                  props.add(factory.create(JcrLexicon.UUID, key));
                  addVersionedPropertiesFor(node, forceCopy, props);
                  MutableCachedNode newCopy = parentInVersionHistory.createChild(versionHistoryCache, key, nodeName, props);
  
                  // Now process the children of the versionable node ...
                  NodeKey parentKey = node.key();
                  for (ChildReference childRef : node.node().getChildReferences(nodeCache)) {
                      AbstractJcrNode child = session.node(childRef.getKey(), null, parentKey);
                      versionNodeAt(child, childRef.getName(), newCopy, forceCopy, nodeCache, versionHistoryCache);
                  }
                  return;
              case OnParentVersionAction.INITIALIZE:
              case OnParentVersionAction.COMPUTE:
              case OnParentVersionAction.IGNORE:
                  // Do nothing for these. No built-in types require initialize or compute for child nodes.
                  return;
              default:
                  throw new IllegalStateException("Unexpected value: " + onParentVersion);
          }
      }