I am working on a small project that requires loading an XML file. I found a nice code sample that extends DefaultHandler and uses a custom TreeRender to format the XML into a treeview here. The code compiles and runs (always a plus) and gives me the starting point I'm looking for but there is one little thing I don't understand in the code.
Here's the code snippet that I don't get:
public class XmlTreeView extends DefaultHandler {
private DefaultMutableTreeNode _base;
<snip>
#Override
public void startElement(String uri, String localName, String tagName, Attributes attr) throws SAXException {
System.out.println("startElement: uri=" + uri + " localname=" + localName + " tagName=" + tagName );
DefaultMutableTreeNode current = new DefaultMutableTreeNode(tagName);
_base.add(current);
_base = current;
for (int i = 0; i < attr.getLength(); i++) {
// <snip> attribute processing
}
}
The class declares a DefaultMutableTreeNode named _base. The startElement() method instantiates a new DefaultMutableTreeNode named current then does
_base.add(current);
_base = current;
All my programming knowledge tells me that the second statement assigns the new object (current) to the _base "variable", making the first statement useless. However, if I take out the first statement the code no longer works properly. In fact, if I take out either statement it no longer works properly. Both statements are required for the element to get added to the tree.
Can you explain to me what's happening here? I just don't get it.
Thanks in advance,
Steve
_base is a reference to an object. When you say _base.add(current), you are calling a method that makes some change to that object. When you then say _base = current;, _base becomes a reference to a different object. But the first object is still there. And whatever change you made to it can still impact the rest of the program, if there's a reference to it somewhere else.
These variables are badly named which is causing most of the confusion.
The field _base should really be called something like currentNode and the new Node created in startElement should be something like childNode.
Here is the same code but rewritten using these new variable names:
currentNode.add(childNode);
currentNode= childNode;
So you see when we enter a start element in the XML file, we create a new node and add it to our Object representation of the tree structure of the XML. The new child element that we just started is added to the current node as a child. Then we change our reference of the current node to point to this new child node. This makes the new child node our current node.
I assume in code that you haven't shown, there is an endElement where we do the reverse operation and move up the tree to the currentNode's parent.
You have a strange piece of code here in my mind but this is what is happening:
private DefaultMutableTreeNode _base;
Is acting as a global currentNode for the class. When you call startElement you are doing this:
DefaultMutableTreeNode current = new DefaultMutableTreeNode(tagName);
//Create a new TreeNode item based off the tagName
So you now have:
_base (no children)
Now you are going to add to the children of _base, the freshly created node
_base.add(current);
Now that this is done you have:
_base (no children)
current (child)
Finally
_base = current;
Now you have
(parent of _base) (the old base)
_base / current
The reference to _base now points to the freshly created child. When you call endElement, you will exit out of the _base and return to your old _base
_base is storing the xmlElement that you are currently working on. When you call startElement all calls to setAttribute or startElement will be based off this.
SUMMARY:
Here is what this looks like played out in code:
XmlWriter xWriter;
xWriter.startElement("NPC"); //_base becomes new node "hello"
xWriter.startAttribute("Greeting", "Hi"); //attribute is now set to _base (or greeting)
xWriter.startElement("Data"); //_base becomes new node "data"
xWriter.startAttribute("Height", "200"); //attribute is now set to _base (or data)
xWriter.endElement(); //on end element you move to parent of data, so greeting
xWriter.endElement(); //again you move to the parent
Creating:
<NPC Greeting='Hi'>
<Data Height='200'/>
</NPC>
Related
I decided to implement the Abstract List<Node> . here is a piece of it:
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class myNodeList implements NodeList{
Node root = null;
int length = 0;
public myNodeList() {}
public void addNode(Node node) {
if(root == null)
{
root = node;
}
else
root.appendChild(node);
length++;
System.out.println("this is the added node " +node);
}
}
but when I try to add a node, it gives me the following exception:
Exception in thread "main" org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was made to insert a node where it is not permitted.
at com.sun.org.apache.xerces.internal.dom.NodeImpl.insertBefore(NodeImpl.java:478)
at com.sun.org.apache.xerces.internal.dom.NodeImpl.appendChild(NodeImpl.java:235)
at pageparsertest.myNodeList.addNode(myNodeList.java:27)
is this because of the Node root = null; which makes to add a node to a null node?
then how can be fixed
You can't append to a com.sun.org.apache.xerces.internal.dom.NodeImpl, you'll need to use com.sun.org.apache.xerces.internal.dom.ParentNode.
appendChild will call insertBefore which only throws an Exception for NodeImpl
Source code
Move one or more node(s) to our list of children. Note that this
implicitly removes them from their previous parent.
By default we do not accept any children, ParentNode overrides this.
Take a look how Axis implemented their : http://grepcode.com/file/repo1.maven.org/maven2/com.ning/metrics.collector/1.0.2/org/apache/axis/message/NodeListImpl.java
It seems you're trying to build a Node tree using the first Node as the Root, not a node list. Which is not possible has your nodes are NodeImpl not ParentNode.
If you want a tree, you'll have to create (or import) somehow a parent node.
If you just need a list, then use a List.
You may need to create a fake custom parent to insert your nodes.
Take a look here : HIERARCHY_REQUEST_ERR while trying to add elements to xml file in a for loop
well this is embarressing but I changed my idea to implement that and instead usedstatic List<Node> listOfNodes = new ArrayList<Node>();
which worked well for me!
I want to copy a tree structure(source) to another one(target), but I got java.util.ConcurrentModificationException when I execute the method below:
private void mergeNode(TreeLayoutNode source, TreeLayoutNode target){
List<TreeLayoutNode> children = source.getChildren();
if(CollectionUtils.isEmpty(children)){
return;
}
Iterator<TreeLayoutNode> it = children.iterator();
while(it.hasNext()){
**TreeLayoutNode child = it.next();**
TreeLayoutNode copy = new TreeLayoutNode();
copy.setName(child.getName());
copy.setCapacity(child.getCapacity());
copy.setParent(child.getParent());
copy.setChildren(child.getChildren());
copy.setProportions(child.getProportions());
target.addChildNode(copy);
mergeNode(child, copy);
}
}
The code started with "**" is where exception occurs.Could any one give any hints?
Remove the call to 'copy.setChildren(child.getChildren())'. This results in references to children from the source tree being added to the destination tree, which is not what you want. The recursive method call will handle filling in the children.
Also you need to set the parent of the new node to the correct node in the new tree rather than calling 'copy.setParent(child.getParent())', which sets the parent reference to a node in the old tree. The call should probably be 'copy.setParent(target)'.
I'm trying to create a queue using two classes, a Node class and a Queue class for an assignment. Here's the node class:
class Node
{
protected Node next;
public Node()
{
next = null;
}
}
This class basically links the data together using a Node.next object. I've successfully been able to create a stack with push() and pop(), because the two operations happen on the same end, so the point are just manipulated between pointing to a new added node, and the previous node.
However, I'm having some difficulties understanding the logic to create a queue based on a similar structure. My queue class looks something like this:
class Queue
{
private Node footer;
private Node header;
public Queue()
{
footer = null;
header = null;
}
public void add(Node newNode)
{
//Adds onto the queue from the 'footer' end.
}
public Node remove()
{
//Removes from the queue from the 'header' end.
}
Here's what I understand: (1)The header and the footer point to the same first node. (2) Subsequent adding should change the footer to point to the added nodes, but the header stays on the first node added. (3) The header should point to the next oldest node upon removal.
Here's what I can't figure out (and where it's different than popping from a stack). How do I get the header to point to the 'next oldest node', given that I have more than 2 nodes in this queue? I know I can do this if I link header.next to the next node in the queue, but how can I access the next node so that it can point to it?
I thought about how in add(), the newNode.next should point to the next newNode (reverse direction of a Stack), but this can't work because the next newNode isn't in existence yet.. Another idea was to modify the Node class to have a Node.previous for a way to point backwards, but I would be breaking specification for this assignment.
My instructor hinted something about "header.next will point for second item as header and footer point to first node initially," and that the way to do this is pretty simple. However, I've been drawing how this works, and I'm confused how the initial pointing to the same node will allow header.next to "automatically" point to the next oldest node, especially if more and more nodes are added and the footer eventually is separated from the header by more than 2 nodes. Is there something about OOP I'm not seeing?
Any help would be great!
To expand on, and offer a subtle alternative to #Sanjeev's answer (one that I think your instructor was hinting to):
Rather than using footer to store "actual" nodes, use it as a placeholder: Declare it as a final variable, initialize it in your constructor and make sure that either a) it's next node is always your header (this would be called a circular list), or its next node is null.
Can you see how this solves your "this can't work because the next newNode isn't in existence yet" problem: Of course you can't point the last node added to the next one that will be added before adding it - instead, you point it to this "dummy" node - which is a placeholder for the next node that will be added, when and if it is.
add(Node newestNode){
identify the last node added as the one whose next property is the footer.
change the next property of that node from footer to this new newestNode
set the next property of this new newestNode to footer
}
It would be preferable to identify that last node added as the one that footer is pointing to (rather than the one pointing to footer), which would be easy if you were allowed to have previous as well as next properties on nodes, but it sounds like you're not allowed to do that. Of course, since we're using footer as a "dummy node", we could simply use footer.next the way we would footer.previous and have it point backwards instead of forwards, but I'll leave you to consider how clean that would be. There are other options here that I'll leave you to consider as well.
How do I get the header to point to the 'next oldest node'`
The "oldest" node was the first one added. The "newest" node is the last one added. How is the order of the rest of the nodes stored? The same way it was in your Stack - by traversing a chain of references stored as instance variables on your nodes. The main point I want to make is that Stacks and Queues, when implemented as linked data structures, are much more similar than you seem to be thinking, at least from a : Iterating through any linked data structure is done by following traversing these links - don't get too hung up on the fact that you're "moving" in a different direction - the same basic principles apply:
Node remove(){
identify the "oldest" node as header.next.
Store a reference to that node so you can return it.
identify the "second oldest node" as header.next.next
change header.next to header.next.next
return the reference to the old header.next you saved above.
(Note that using header/footer as placeholders, rather than storing "actual" nodes in them as #Sanjeev suggests, is not necessary, but it'll make your life easier - for instance, by helping you avoid a lot of null checking)
Here is the sudo code that will help you get started.
public void add(Node newNode)
{
if footer is null ?
then
header = newNode and footer = newNode;
else
footer.next = newNode and footer = newNode;
end if
}
public Node remove()
{
Node returnMe = header;
if header is not null?
then
header = header.next
if header is null
then
footer = null;
endif
end if
return returnMe;
}
How do I get the header to point to the 'next oldest node', given that
I have more than 2 nodes in this queue? I know I can do this if I link
header.next to the next node in the queue, but how can I access the
next node so that it can point to it?
To make header point to that node, you only need do header = header.next. The reason is that Java objectt assignment is by reference. Since header.next is type of Node, header is type of Node, it will copy the address of header.next to header, i.e., header is advanced one place.
I thought about how in add(), the newNode.next should point to the
next newNode (reverse direction of a Stack), but this can't work
because the next newNode isn't in existence yet..
I think it is no need to considering reverse direction. The reason is because for adding , it is to add element to the tail/footer of the queue. The only special case is that the queue didn't have any elements (footer == header == null), 1 element : (footer = header = element), other case: header won't change, but you need to append element to footer, and then make footer point to the new node.
When only 1 element, footer.next == header.next == null
The first thing that you need to do is make sure the first node you create is the oldest so it should be the first to be removed from the Queue based on First In First Out (FIFO) principle to archive this you might need to modify you're add method to something like this, by the way this example is based on single linked list implementation.
void add(char new_data)
{
/* 1. alloc the Node and put data*/
Node new_Node = new Node(new_data);
/* 2. Make next of new Node as head */
new_Node.next = head;
/* 3. Move the head to point to new Node */
head = new_Node;
}
then you will need a remove method which will remove the oldest node on the list first remember in Queue the order of remove is First In First Out (FIFO)
that being said this remove method should help you
void remove()
{
// Store head node
Node temp = head, prev = null;
// If head node itself holds the key to be deleted
if (temp != null )
{
head = temp.next; // Changed head
return;
}
// Search for the key to be deleted, keep track of the
// previous node as we need to change temp.next
while (temp != null)
{
prev = temp;
temp = temp.next;
}
// If key was not present in linked list
if (temp == null) return;
// Unlink the node from linked list
prev.next = temp.next;
}
This worked for me on my linked list
I'm using a JTree that is populated from a database.
The tree is created by setting the root node and its childs with custom objects this way:
private DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Categorias");
...
ResultSet primaryCategories = dbm.fetchAllCategories();
while (primaryCategories.next()){
Category category = new Category(primaryCategories.getLong("_id"),
primaryCategories.getString("category"));
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(category);
rootNode.add(childNode);
ResultSet currentSubcategory = dbm.fetchChildSubcategories(category.getCatId());
while (currentSubcategory.next()){
Category subcategory = new Category(currentSubcategory.getLong("_id"),
currentSubcategory.getString("category"));
childNode.add(new DefaultMutableTreeNode(subcategory, false));
}
}
...
After this, the tree is perfectly created. Populated with "Category" Objects, every object has its own ID number and its name to use in toString() method.
The problem comes when it's set editable. Once the node is renamed, the Category node is also converted into a String Object, so I cant update the new Category name value to the database.
I've tried to capture the renaming event with treeNodesChanged(TreeModelEvent e) but, the userObject is already changed to a String Object, and can't get a referece of what object was edited.
What way can I solve this? Should I have a copy of the tree that's shown and another of the downloaded from the database and uptade both everytime a change occurs?
*PD: *
I also tried to capture the changed node from the model overriding the method:
public void nodeChanged(TreeNode newNode) {
DefaultMutableTreeNode parent = ((DefaultMutableTreeNode)newNode.getParent());
int index = getIndexOfChild(parent, newNode);
DefaultMutableTreeNode oldNode = (DefaultMutableTreeNode) getChild(parent, index);
System.out.println(parent.getUserObject().getClass().toString());
System.out.println(oldNode.getUserObject().getClass().toString());
}
this prints:
class com.giorgi.commandserver.entity.Category
class java.lang.String
So the old node here has already been changed to a String and I've lost completely the reference to the older Category and its ID so I cannot update it in the database.
Any help is wellcome.
Okay, that took a bit of digging.
Basically, when the editing is "stopped", the JTree will request the editor's value via editor's getCellEditorValue. This is then passed to the model via the valuesForPathChanged method, which finally calls the node's setUserObject method.
Presumably, you are using either the default editor or one based on text field. This will return a String value.
What you need to do is trap the change to the setUserObject method of your tree node, access the value coming (ie, check if it's a String or not) and update as required.
Final solution was as MadProgrammer said to get it in:
public void valueForPathChanged(TreePath path, Object newValue) {
DefaultMutableTreeNode aNode = (DefaultMutableTreeNode)path.getLastPathComponent();
Category catNode = (Category) aNode.getUserObject();
catNode.setCategory((String) newValue);
catNode.updateFromDatabase();
nodeChanged(aNode);
}
I need to write my own Deque class and must used a doublylinked list implementation to store data. the problem is writing the method pushfromLeft(Thing thing) which will insert into the left side of the deque. Below is what I have thus far but does not seem to work.
public void pushLeft(Thing thing) {
Node beg = new Node();
Node end = new Node();
Node T = new Node();
if(isEmpty())
{
beg = first;
end = last;
beg = end;
T = beg.thing;
N++;
}
else
{
beg = beg.next;
end = end.next;
T = beg.previous;
N++;
}
Little you do in that method has any effect outside, except changing N and item. Presumably you should be modifying first. It would help if you provide the fields of your class, and what they mean, for context. For instance, it's not clear what item is.
You should also either come up with different conventions for naming member and local variables, or consistently use this., or both.
Might I make a suggestion that may clear a lot of this up for you. it's not what you asked for, but it may be what you need.
Use OO design, this means not operating on something but asking something to operate on itself. What this means is that Node should be more intelligent--currently you are acting on node.
Since Node is doubly linked, it can be pretty smart! It can have methods like:
newNode.insertBefore(currentNode)
newNode.insertAfter(currentNode)
currentNode.remove()
Once you have those, the rest of your code should clean up a bit. They should be trivial to implement given a doubly linked list.
void insertBefore(node existing) {
// first set my stuff up
previous = existing.previous;
next = existing;
// then point other stuff at me
previous.next = this;
existing.previous = this;
}
I think--that's just off the top of my head.
The other question is how do you handle your "Endpoints". Your first and last pointers have to be instances of Node for this to work, but if they are notice that the whole "If" factors out of your original code! Sweet!
Just ALWAYS have a first and last object that start out pointing to each other (and never take on values). When you do your first add, do first.insertAfter() or last.insertBefore() and you are done.
Another possibility, by the way, is to make the list circular--there is no reason that First and Last couldn't be the same "Special" unassigned node, you could still traverse it's Next (which will give you the first real item in the list) and Previous (giving you the last item in your list).
When iterating the entire list, if .value == null, you know you've made it to the other end which makes node.next() and previous() fascinatingly easy to implement (You don't really even need to implement .next, but see below.
/** returns null if there are no more items in the list */
Node next() {
return next;
}
Try it, it will simplify your code a LOT. Most people really don't get how useful actual OO code is.
Also, make all your variables private, it's a good habit to get into. In this case when you are having nodes operate on each other, they can still access each other's private members (not as dirty as it sounds) so you can still have the insertBefore as I wrote it AND you don't have to have getters and setters or public variables. Best of both worlds.
Also notice how your original class that "Operated" on node all but disappears--in fact, it can go away completely. If you needed some specific methods like find(item) or insertSorted(item) there is no reason you couldn't add them to node itself. This may be hard to see until after you implemented it though.
Funny how if you actually code well, most of the complaints people have about Java just don't come up.
Have you looked at the LinkedList source code as a reference?
You definitly don't need to create more than one Node inside adding method. If you want to read from left AND from right later, each Node must remember previous and next element. Then when adding, you just need to re-locate these links, like this:
public void pushLeft(Thing thing) {
Node newNode = new Node();
newNode.setValue(thing); //or just newNode.value = thing;
if(this.isEmpty())
{
this.first = this.last = newNode;
this.n=1;
}
else
{
this.first.previous = newNode;
newNode.next = this.first;
this.first = newNode;
this.n++;
}
}
It vould be wise to create a constuctor for Node class which shoul automaticaly set the value, then you can just wite:
Node newNode = new Node(thing);