I'm using libxml2 to parse HTML. I want to remove certain formatting tags like <center>, while keeping their content (for example, a link).
This means I'll have to remove certain child nodes from my xmlNodeSet, but keep that node's children.
Right now, I got this code:
xmlNodePtr parentNode = nodes->nodeTab[i];
if (parentNode != NULL) {
xmlNodePtr child = parentNode->children;
xmlNodePtr parentNextSibling = parentNode->next;
xmlNodePtr grandParent = NULL;
while (child) {
xmlUnlinkNode(child);
if (parentNextSibling != NULL) {
xmlAddPrevSibling(parentNextSibling, child);
}
else {
if (grandParent == NULL)
grandParent = parentNode->parent;
xmlAddChild(grandParent, child);
}
child = child->next;
}
xmlUnlinkNode(parentNode);
xmlFree(parentNode);
}
The code does add the child to the document, but it also deletes the node I was adding it as a sibling to. What am I doing wrong?
You're not saving off the child->next pointer before you cut it out of the tree. As soon as you unlink a node, it isn't part of the tree, so child->next becomes NULL. Then, after you reinsert it into the tree (before parentNode->next), the child->next pointer now points to what was previously parentNode->next, so the next time through the loop, you delete parentNode->next. Things can only go downhill from there. :-)
Related
In google chrome, especially now with custom elements, it became very cumbersome to select and element by hand nowadays, even though the browser knows the whole path to it already. Or is there a way that leads to a query selected for an element that I'm inspecting?
Situation:
What chrome can tell me:
What chrome is unable to create for me AFAIK:
While building an chrome extension I have found a need to uniquely locate an element when returning to a page. To do this I needed to create a query string for a selected element (custom context menu click)
While searching for a solution I found this unanswered question.
As I could not find an off the shelf solution or API to do the task I wrote the following function. It is untested in the wild, is very rough and ready (using poor node traversing techniques). I posted it in this state lest I forget and this question remains unanswered.
Create Query String For Element
A function to build a query string that will uniquely locate an element from a reference of the element.
const querytStr = createQueryStringForElement(myElement); // return string or undefined
if (querytStr) {
const element = document.querySelector(queryStr);
console.log(element === myElement); // expected result true
}
If the function fails to create a query that uniquely locates an element it returns undefined. else it returns the query string.
Example results
"#editor > div.ace_scroller > div.ace_content > div.ace_layer.ace_text-layer > div.ace_line:nth-child(45) > span.ace_punctuation.ace_operator"
"#buttons" // A UI container
"#buttons > div.buttons" // A sub UI container
"#buttons > div.buttons:nth-child(2)" // A button element by position
"#buttons > div.buttons:nth-child(3)" // A button element by position
How it works
The code assumes that the page is well formed (ids must be unique).
The query string will try to start with an id eg "#elementId" but if an element has no id the query will use the tag and class names. eg "div.my-class".
The tag and class name may not uniquely identify the element. To check if the query is unique, the query string is used to query the DOM from the elements parent.
If needed the query string will use the elements position to refine the query "div.my-class:nth-child(2)". Unfortunately this makes the resultant query string insensitive to changes in element order.
The query string is built up along each parent until it finds an element with an id or there are no more parents.
The final step uses the query to see if the query finds the correct element returning the query if successful.
The code
function createQueryStringForElement(element) {
const getElementSel = element => {
const tName = element.tagName.toLowerCase();
var i = 0, str = element.id ? "#" + element.id : sel = tName;
if (str.includes("#")) { return str}
str += element.classList.length ? "." + [...element.classList.values()].join(".") : "";
if (element.parentElement) {
const res = element.parentElement.querySelector(str);
if (res !== element) {
while (i < element.parentElement.children.length) {
if (element.parentElement.children[i] === element) {
i > 0 && (str += ":nth-child(" + (i + 1) + ")" );
break;
}
i++;
}
}
}
return str;
}
const queryPath = [];
const original = element;
do {
const subQuery = getElementSel(element);
queryPath.push(subQuery);
if (subQuery[0] === "#") { break }
element = element.parentElement;
} while (element);
const query = queryPath.reverse().join(" > ");
try {
const els = document.querySelector(query);
if (els === original) { return query }
} catch(e) { }
}
Let's assume that I have a JTree similar to the picture I provided, in which the number of leaves and nodes will vary every time I run the code. Now how could I remove the nodes that are empty (AKA have no children) since I can't check so see if its going to be empty as I add them to the tree?
I have tried using an enumeration to traverse the tree and check for every node to check how many children it has but that didn't help because even if I could tell that the node I am at is a node that needs to be removed I have to tell his parent to remove him I cant tell him to remove himself from his parent node.
What do I need to use to achieve what I am looking for?
Now how could I remove the nodes that are empty...so i want a node to either have only other nodes or only leaves but not both at the same time
Traverse the tree and check for nodes which have the following criteria
Is a leaf
Has siblings that are not leafs.
even if I could tell that the node I am at is a node that needs to be removed I have to tell his parent to remove him I cant tell him to remove himself from his parent node.
That is what the DefaultTreeModel.removeNodeFromParent() method does. So you can traverse the tree recursively and just remove nodes based upon your given criteria.
DefaultMutableTreeNode root = new DefaultMutableTreeNode ("Root");
//other code
DefaultTreeModel treeModel = new DefaultTreeModel(root);
JTree tree = new JTree(treeModel);
//populate tree
recurseTree(root, treeModel);
//method to recursively remove leaf nodes that have non-leaf siblings
private void recurseTree(MutableTreeNode node, DefaultTreeModel treeModel){
if ( node.isLeaf() ){
TreeNode parent = node.getParent();
for ( int i = 0; i < parent.getChildCount(); i++ ){
if ( !parent.getChildAt(i).isLeaf() ){
treeModel.removeNodeFromParent(node);
break;
}
}
}else{
for ( int i = 0; i < node.getChildCount(); i++ ){
recurseTree((MutableTreeNode)node.getChildAt(i), treeModel);
}
}
}
All this being said, it looks like a long route around the initial addition of those nodes. Without knowing the underlying data structure to populate the tree one can only guess as to how to go about preventing those nodes from being added in the first place.
I have a parent Div and child Div like below.
--ParentDiv
--ChildDiv!
Parent and Child divs
I know the ParentDiv Id (which is static). I want to know the ChildDiv Id which keeps changing. How to do this?
I tried below matching the Class. But it says.. "The playback failed to find the control with the given search properties."
HtmlControl childDiv = parentDiv.GetChildren()[0].Find<HtmlControl>(new { Class = "cmd-datatable" });
string id = parentLvl2.GetProperty("Id").ToString();
Thanks in Advance!
The following should return the id of the first child.
HtmlDiv parent = new HtmlDiv(browser);
parent.SearchProperties["id"] = "thisDivsID";
string childID = parent.GetChildren()[0].GetProperty("ID").ToString();
You could also wrap the whole thing in a foreach loop:
List<string> ids = new List<string>();
foreach(UITestControl child in parent.GetChildren())
{
ids.Add(child.GetProperty("ID").ToString();
}
I have a list populated with duplicate entries. I am using a hash to store each unique entry from this list. Each key will then point to an object of type DefaultMutableTreeNode which represents the JTree node. My goal is to have this node point to all entries from the list which have the same node-same as the parent.
I have added these parent-nodes without problems but when the children nodes are added (via insertNodeInto), only parent nodes appear. A code-snippet is below; am very appreciative for advice / time.
// an unsorted list of items
List<MyObject>list = hash.get(key);
// clause to check for size
if (list.size() > 0) {
// iterate through each item in the list and fetch obj
for (int i=0; i < list.size(); i++) {
MyObject be = list.get(i);
// if object is not in hash, add to hash and point obj to new node
if (!hashParents.containsKey(be)) {
DefaultMutableTreeNode parent =
new DefaultMutableTreeNode(be.getSequence());
// add the key and jtree node to hash
hashParents.put(be, parent);
// insert node to tree, relead model
((DefaultMutableTreeNode)(root)).insert(
new DefaultMutableTreeNode("parent node"),
root.getChildCount());
((DefaultTreeModel)(tree.getModel())).reload();
}
// now that a parent-node exists, create a child
DefaultMutableTreeNode child = new DefaultMutableTreeNode("child");
// insert the new child to the parent (from the hash)
((DefaultTreeModel)(tree.getModel())).insertNodeInto(child, hashParents.get(be),
hashParents.get(be).getChildCount());
// render the tree visible
((DefaultTreeModel)(tree.getModel())).reload();
}
}
You make mistake here
// insert node to tree, relead model
((DefaultMutableTreeNode)(root)).insert(
new DefaultMutableTreeNode("parent node"),
root.getChildCount());
You already create node parent above, but don't use it. You insert in tree another node, but child nodes you still insert in parent. That's why they don't appear in tree.
This code fragment look like
// insert node to tree, relead model
((DefaultMutableTreeNode)(root)).insert(
parent,
root.getChildCount());
I'm working with a large set of hierarchical taxonomic terms, where each term ("203") has a matching "term203" movie clip on the stage, and am having trouble getting a recursive function to return all of a given term's descendants.
There is a main Dictionary() object with the following nested organization for each term:
{ [object Movie Clip] : { "tid":203, "parent":99, "name":"Culture", selected:false, "otherData":"etc" } }
...where the [object Movie Clip]'s instance name would be "term203". All of these object:subObjectArray items ("terms") are stored in a master taxonomy:Dictionary() object.
I've been trying to make a recursive function (which is in itself already a little above my head) that takes the click.target of a movie clip and returns a new Dictionary() object with all of the children and grandchildren and great grandchildren (etc) of that term, in the same, nested organization described above.
The code below traces the right number of recursive loops, but the returned Dictionary() object only contains the first run's terms (only the immediate children of the requested term).
var taxonomy:Dictionary = new Dictionary();
// ...Term info is loaded into taxonomy from a JSON-style text file)
// ...MOUSE_OVER event listeners are added to each
function revealChildren(hvr:MouseEvent):void {
trace("Spotlighting " + taxonomy[hvr.target].name + "'s children...");
for(var key:Object in getAllChildren(taxonomy[hvr.target].tid)) {
trace("Animating " + taxonomy[key].tid); // Traces only immediate children
var revealTween = new Tween(key, "alpha", Regular.easeInOut, key.alpha, 1, 1, true);
}
}
function getAllChildren(origin):Dictionary {
var children:Dictionary = new Dictionary();
for(var element:Object in taxonomy) {
if(taxonomy[element].parent == origin) {
var subSet = getAllChildren(taxonomy[element].tid);
children[element] = subSet; // *CAN'T ACCESS 'subSet' PROPERLY*
trace("Parent = " + origin);
trace("Matched! Adding " + taxonomy[element].tid + " as key and setting its value to " + subSet); // Traces correct amount of times, one for each descendent
}
else {
}
}
return children;
}
I certainly do not claim to be the most efficient AS3 programmer, so I am open to alternative configurations. However, after trying static and nested Arrays, I would prefer to continue using the Dictionary() object as my main pool.
As noted, only the immediate children end up animating in the revealChildren() function. It's mystifying to me then, that in the getAllChildren() function, all of the descendants trace sequentially (well in no particular order) in the output window.
Also I can't get any sort of name or property out of the subSet Object. That could be the problem.
I've only tested it as far as 'two generations,' but it seems that only the first round of calling the function successfully adds those terms to the new Dictionary() object and returns it intact to the animating function.
Too bad dict.filter(getDescendants) won't work. Please help!
To simplify things, I've added an output parameter called children. This is the Dictionary into which our function will store its results. It has a default value, so you don't need to specify one. In that case, it will create a new instance for itself.
function getAllChildren(origin:*, children:Dictionary = null):Dictionary {
if (children = null) children = new Dictionary();
for(var element:* in taxonomy) {
if(taxonomy[element].parent == origin) {
children[element] = taxonomy[element];
getAllChildren(taxonomy[element].tid, children);
}
}
return children;
}
When a child is discovered, it is copied over exactly: children[element] = taxonomy[element];
Next, the function calls itself recursively, supplying it the same output dictionary as it has been using.
Edit:
In response to your comment... Your code originally said this after finding a child named element:
children[element] = getAllChildren(taxonomy[element].tid);
You're making children[element] equal to a Dictionary object here. What you create is a tree structure, mapping MovieClip objects to Dictionary objects containing a similar mapping of its children. Using a for in loop on this structure will only give you the top-level children. It will not recursively traverse the entire tree.