Hi,
I want to inform you that I currently put the aside the intention of
adding xmlDOMWrapSetAttr(Node) and xmlDOMWrapRemoveAttr(Node)
functionality. Looking at the code of the Delphi-wrapper, I'm having
difficulties in finding oppropriate code to be put into Libxml2,
which does what I need without adding a lot of additional stuff.
However, I still would be happy if there would be more Libxml2
code, which would be intended to be used by DOM(-like)
wrappers, in order to avoid inventing the wheel for every wrapper
out there.
I attached a snippet of the Delphi-wrapper's internal
set-attribute-ns code for anyone who's interested in the
issue; this is how I tried to solve it in Delphi. This currently
avoids any IDness semantics.
Regards,
Kasimier
procedure ozdom_setAttributeValue(const aDoc: IDOMDocument;
const aElement: IDOMElement; const aAttr: IDOMAttr; aLxAttr: xmlAttrPtr;
const aValue: DOMString);
procedure _RaiseAlreadyExists;
begin
raise EDOMException.create(NOT_SUPPORTED_ERR,
'Cannot change the nodeValue of a default attribute if it already exists
as a specific attribute.',
nil, 'ozdom_setAttributeValue');
end;
procedure _SetValueDefault;
// The messy DTD-default-attribute stuff.
var
lxNs: xmlNsPtr;
lxAttrDecl: xmlAttributePtr;
lxElem: xmlNodePtr;
begin
// Clone the attribute declaration into a normal attribute and add it to
its owner element.
// Problematic scenario:
// The default attribute is hold by two wrappers.
// The first one changes the nodeValue -> a normal attribute is created.
// The second one changes the nodeValue -> again, a normal attribute is
created.
// This will result is two attributes with the same name being created.
// We'll solve this by looking up if an equal "normal" attribute already
exists;
// a bit messy though.
lxElem := (aElement as IDOMLxNodeAccess).getLxNodePtr();
lxAttrDecl := xmlAttributePtr(aLxAttr);
// Do not allow changes to fixed attributes.
if (lxAttrDecl.def = XML_ATTRIBUTE_FIXED) then
raise EDOMException.Create(NO_MODIFICATION_ALLOWED_ERR,
'Cannot change the value of a FIXED attribute.',
nil, 'ozdom_setAttributeValue');
// Process only, if the default attribute is not already existent as a
namespace declaration
// attribute (nsDef entry). IOW, do not allow changes to namespace
declaration attributes.
if // @attrDecl.prefix is "xmlns" if it declares a prefixed namespace.
((lxAttrDecl.prefix <> nil) and
(xmlStrEqual(lxAttrDecl.prefix, xmlCharPtr('xmlns')) = 1)) or
// @attrDecl.name is "xmlns" if it declares a default namespace.
(xmlStrEqual(lxAttrDecl.name, xmlCharPtr('xmlns')) = 1)
then begin
raise EDOMException.Create(NO_MODIFICATION_ALLOWED_ERR,
'Changing of default (from a DTD) namespace declaration attributes is
not supported (yet).',
nil, 'ozdom_setAttributeValue');
// TODO -oKB: Support changing of default ns-decl attributes.
end;
// Process only, if the default attribute was not already converted to a
"normal" attribute.
// This is needed for the scenario where the default attribute is hold by
multiple wrappers.
if (ozdom_getNormalAttribute(lxElem, lxAttrDecl.prefix, lxAttrDecl.name) <>
nil) then
// EXCEPTION.
_RaiseAlreadyExists;
// Copy the namespace.
// Because an attribute declaration carries only the prefix with it, we
have to lookup its
// namespace.
if (lxAttrDecl.prefix <> nil) then begin
// TODO 1 -oKB: We need a *strict* ns-lookup-by-prefix function in
Libxml2.
if (ozdom_searchNsByPrefixStrict(lxElem.doc, lxElem, lxAttrDecl.prefix,
lxNs) <> 1) then
raise EDOMException.Create(NAMESPACE_ERR,
Format('No namespace declaration in scope for the prefix "%s".',
[UTF8Decode(lxAttrDecl.prefix)]),
nil, 'ozdom_setAttributeValue');
end else
lxNs := nil;
// Clone the default attr; i.e. create a "specified" attribute.
// Don't clone the value, since we are going to change it afterwards anyway.
aLxAttr := ozdom_cloneAttrDecl(lxAttrDecl, aDoc, false, lxNs);
if (aLxAttr = nil) then
raise EDOMException.Create(NAMESPACE_ERR,
'Failed to clone a default attribute.',
nil, 'ozdom_setAttributeValue');
// The attr-wrapper will be re-initialized with a clone of the attribute
declaration.
if (aAttr <> nil) then
(aAttr as IInternalDOMNode).initInternalStructure(xmlNodePtr(aLxAttr));
// Append it to the element's attributes.
ozdom_appendAttributeRaw(lxElem, aLxAttr);
// Set the value.
if (aValue <> '') then
xmlAddChild(xmlNodePtr(aLxAttr), xmlNewDocText(lxElem.doc,
xmlCharPtr(UTF8Encode(aValue))));
end;
procedure _RemoveContent;
var
cur, tmp: xmlNodePtr;
begin
// Don't free the nodes, since they might be referenced by wrappers.
// xmlFreeNodeList(aLxAttr.children);
// TODO -oKB: This is not very efficient, but how to do it otherwise?
cur := aLxAttr.children;
while (cur <> nil) do begin
tmp := cur;
cur := cur.next;
tmp.next := nil;
tmp.prev := nil;
tmp.parent := nil;
(aDoc as IInternalDOMDocument).addOrphanNode(xmlNodePtr(tmp));
end;
aLxAttr.children := nil;
aLxAttr.last := nil;
end;
begin
if (aLxAttr.type_ = XML_ATTRIBUTE_DECL) then begin
_SetValueDefault();
end else begin
if (aLxAttr.children <> nil) then begin
_RemoveContent();
end;
// Set the value.
if (aValue <> '') then
xmlAddChild(xmlNodePtr(aLxAttr), xmlNewDocText(aLxAttr.doc,
xmlCharPtr(UTF8Encode(aValue))));
end;
end;
procedure TDOMElement.internalSetAttributeNS(const aNamespaceURI, aPrefix,
aLocalName, aValue: DOMString);
var
lxPrefix, lxNamespaceURI, lxLocalName: xmlCharPtr;
lxAttr: xmlAttrPtr;
begin
lxLocalName := xmlCharPtr(UTF8Encode(aLocalName));
if (aPrefix = '') then
lxPrefix := nil
else
lxPrefix := xmlCharPtr(UTF8Encode(aPrefix));
if (aNamespaceURI = '') then
lxNamespaceURI := nil
else
lxNamespaceURI := xmlCharPtr(UTF8Encode(aNamespaceURI));
// xmlHasNSProp() will also return default/fixed attribute declarations of a
DTD.
lxAttr := xmlHasNsProp(fXmlNode, lxLocalName, lxNamespaceURI);
// Do not modify fixed attributes.
if (lxAttr <> nil) then
check_fixedAttr(lxAttr);
// Create a new attribute-node if needed.
if (lxAttr = nil) then begin
lxAttr := xmlNewDocProp(fXmlNode.doc, lxLocalName, nil);
if (lxAttr = nil) then
raise EDOMException.Create(0, 'Internal Libxml2 error: xmlNewDocProp()
failed.',
self, 'internalSetAttributeNS');
// Aquire the namespace structure.
if (lxNamespaceURI <> nil) then begin
lxAttr.ns := (fOwnerDocument as
IInternalDOMDocument).acquireNs(lxNamespaceURI, lxPrefix);
if (lxAttr.ns = nil) then
raise EDOMException.Create(0, 'Failed to acquire a namespace
structure.',
self, 'internalSetAttributeNS');
end;
// Append it to the element's attributes.
ozdom_appendAttributeRaw(fXmlNode, lxAttr);
end;
// Set the attribute's value.
ozdom_setAttributeValue(fOwnerDocument, self, nil, lxAttr, aValue);
(fOwnerDocument as IInternalDOMDocument).switchOnChanged;
end;
procedure TDOMElement.internalSetNSDeclarationAttribute(const aPrefix,
aNamespaceURI: DOMString);
var
lxNs: xmlNsPtr;
begin
lxNs := ozdom_getDeclaredNamespaceByPrefix(fXmlNode, aPrefix);
if (lxNs = nil) then begin
// Create a new namespace declaration attribute.
// This puts the ns into the nsDef list.
lxNs := ozdom_CreateNsDeclAttr(fXmlNode, aNamespaceURI, aPrefix);
(fOwnerDocument as IInternalDOMDocument).switchOnChanged;
end else begin
// TODO -oKB: We do not yet support changing of the value of a namespace
declaration attribute.
// Remove it and add a new one as a workaround.
if (strComp(lxNs.href, xmlCharPtr(UTF8Encode(aNamespaceURI))) <> 0) then
raise EDOMException.Create(NO_MODIFICATION_ALLOWED_ERR, 'Cannot change
the value of a ' +
'namespace declaration attribute.', self, 'setAttribute');
end;
end;_______________________________________________
xml mailing list, project page http://xmlsoft.org/
[email protected]
http://mail.gnome.org/mailman/listinfo/xml