@Override
    public <T extends XsltElement> T wrapElement(XmlTag target, Class<T> clazz) {
        assert target.isValid();

        final Pair<ASTNode, XsltElement> wrapper = target.getUserData(WRAPPER);
        final ASTNode tagNode = target.getNode();
        final ASTNode nameNode = tagNode != null ? XmlChildRole.START_TAG_NAME_FINDER.findChild(tagNode) : null;
        if (wrapper != null) {
            if (wrapper.getFirst() == nameNode) {
                if (wrapper.second.isValid() && clazz.isAssignableFrom(wrapper.second.getClass())) {
                    return clazz.cast(wrapper.second);
                }
            }
        }
        assert XsltSupport.isXsltTag(target) : "Not an XSLT tag: {" + target.getNamespace() + "}" + target.getName();

        final XsltElement element;
        if (XsltSupport.isTemplate(target, false)) {
            element = new XsltTemplateImpl(target);
        } else if (XsltSupport.isVariable(target)) {
            element = new XsltVariableImpl(target);
        } else if (XsltSupport.isParam(target)) {
            element = new XsltParameterImpl(target);
        } else if (XsltSupport.isTemplateCall(target)) {
            element = new XsltCallTemplateImpl(target);
        } else if (XsltSupport.isApplyTemplates(target)) {
            element = new XsltApplyTemplatesImpl(target);
        } else if ("with-param".equals(target.getLocalName())) {
            element = new XsltWithParamImpl(target);
        } else if (XsltSupport.isXsltRootTag(target)) {
            element = new XsltStylesheetImpl(target);
        } else if (XsltSupport.isFunction(target)) {
            element = new XsltFunctionImpl(target);
        } else {
            element = new DummyElementImpl(target);
        }

        if (!(element instanceof DummyElementImpl)) {
            target.putUserData(WRAPPER, Pair.create(nameNode, element));
        }
        return clazz.cast(element);
    }
@Override
    public <T extends XsltElement> T wrapElement(XmlTag target, Class<T> clazz)