xmlSchemaCheckElemSubstGroup(xmlSchemaParserCtxtPtr ctxt,
			     xmlSchemaElementPtr elemDecl)
{
    if ((WXS_SUBST_HEAD(elemDecl) == NULL) ||
	/* SPEC (1) "Its {abstract} is false." */
	(elemDecl->flags & XML_SCHEMAS_ELEM_ABSTRACT))
	return;
    {
	xmlSchemaElementPtr head;
	xmlSchemaTypePtr headType, type;
	int set, methSet;
	/*
	* SPEC (2) "It is validly substitutable for HEAD subject to HEAD's
	* {disallowed substitutions} as the blocking constraint, as defined in
	* Substitution Group OK (Transitive) ($3.3.6)."
	*/
	for (head = WXS_SUBST_HEAD(elemDecl); head != NULL;
	    head = WXS_SUBST_HEAD(head)) {
	    set = 0;
	    methSet = 0;
	    /*
	    * The blocking constraints.
	    */
	    if (head->flags & XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION)
		continue;
	    headType = head->subtypes;
	    type = elemDecl->subtypes;
	    if (headType == type)
		goto add_member;
	    if (head->flags & XML_SCHEMAS_ELEM_BLOCK_RESTRICTION)
		set |= XML_SCHEMAS_TYPE_BLOCK_RESTRICTION;
	    if (head->flags & XML_SCHEMAS_ELEM_BLOCK_EXTENSION)
		set |= XML_SCHEMAS_TYPE_BLOCK_EXTENSION;
	    /*
	    * SPEC: Substitution Group OK (Transitive) (2.3)
	    * "The set of all {derivation method}s involved in the
	    * derivation of D's {type definition} from C's {type definition}
	    * does not intersect with the union of the blocking constraint,
	    * C's {prohibited substitutions} (if C is complex, otherwise the
	    * empty set) and the {prohibited substitutions} (respectively the
	    * empty set) of any intermediate {type definition}s in the
	    * derivation of D's {type definition} from C's {type definition}."
	    */
	    /*
	    * OPTIMIZE TODO: Optimize this a bit, since, if traversing the
	    * subst.head axis, the methSet does not need to be computed for
	    * the full depth over and over.
	    */
	    /*
	    * The set of all {derivation method}s involved in the derivation
	    */
	    while ((type != NULL) && (type != headType)) {
		if ((WXS_IS_EXTENSION(type)) &&
		    ((methSet & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) == 0))
		    methSet |= XML_SCHEMAS_TYPE_BLOCK_EXTENSION;

		if (WXS_IS_RESTRICTION(type) &&
		    ((methSet & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) == 0))
		    methSet |= XML_SCHEMAS_TYPE_BLOCK_RESTRICTION;

		type = type->baseType;
	    }
	    /*
	    * The {prohibited substitutions} of all intermediate types +
	    * the head's type.
	    */
	    type = elemDecl->subtypes->baseType;
	    while (type != NULL) {
		if (WXS_IS_COMPLEX(type)) {
		    if ((type->flags &
			    XML_SCHEMAS_TYPE_BLOCK_EXTENSION) &&
			((set & XML_SCHEMAS_TYPE_BLOCK_EXTENSION) == 0))
		    set |= XML_SCHEMAS_TYPE_BLOCK_EXTENSION;
		    if ((type->flags &
			    XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) &&
			((set & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) == 0))
		    set |= XML_SCHEMAS_TYPE_BLOCK_RESTRICTION;
		} else
		    break;
		if (type == headType)
		    break;
		type = type->baseType;
	    }
	    if ((set != 0) &&
		(((set & XML_SCHEMAS_TYPE_BLOCK_EXTENSION) &&
		(methSet & XML_SCHEMAS_TYPE_BLOCK_EXTENSION)) ||
		((set & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) &&
		(methSet & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION)))) {
		continue;
	    }
add_member:
	    xmlSchemaAddElementSubstitutionMember(ctxt, head, elemDecl);
	    if ((head->flags & XML_SCHEMAS_ELEM_SUBST_GROUP_HEAD) == 0)
		head->flags |= XML_SCHEMAS_ELEM_SUBST_GROUP_HEAD;
	}
    }
}