1. Dmytro Kovalchuk
  2. cross-stitch

Commits

Dmytro Kovalchuk  committed e8c536c Draft

Started development of Registry module

  • Participants
  • Parent commits 729aba8
  • Branches default

Comments (0)

Files changed (30)

File pom.xml

View file
  • Ignore whitespace
         <module>swing-api</module>
         <module>swing-api-impl</module>
         <module>viewer</module>
-    </modules>
+    <module>registry</module>
+  </modules>
 </project>

File registry/nb-configuration.xml

View file
  • Ignore whitespace
+<?xml version="1.0" encoding="UTF-8"?>
+<project-shared-configuration>
+    <!--
+This file contains additional configuration written by modules in the NetBeans IDE.
+The configuration is intended to be shared among all the users of project and
+therefore it is assumed to be part of version control checkout.
+Without this configuration present, some functionality in the IDE may be limited or fail altogether.
+-->
+    <properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
+        <!--
+Properties that influence various parts of the IDE, especially code formatting and the like. 
+You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
+That way multiple projects can share the same settings (useful for formatting rules for example).
+Any value defined here will override the pom.xml file value but is only applicable to the current project.
+-->
+        <netbeans.compile.on.save>test</netbeans.compile.on.save>
+    </properties>
+</project-shared-configuration>

File registry/pom.xml

View file
  • Ignore whitespace
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>net.anatolich.cstitch</groupId>
+    <artifactId>cross-stitch</artifactId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <groupId>net.anatolich</groupId>
+  <artifactId>registry</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>registry</name>
+  <url>http://maven.apache.org</url>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymock</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+    <description>Registry module introduces concept of virtual file system to store all system configuration</description>
+</project>

File registry/src/main/java/net/anatolich/registry/AbstractItemNode.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registry;
+
+import java.util.Collections;
+import java.util.Iterator;
+import net.anatolich.registry.AbstractNode;
+import net.anatolich.registry.Attributes;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public abstract class AbstractItemNode<T extends Attributes> extends AbstractNode<T> {
+
+    public AbstractItemNode(String name, T attributes) {
+        super(name, attributes, 0);
+    }
+
+    public AbstractItemNode(String name, T attributes, int position) {
+        super(name, attributes, position);
+    }
+
+    @Override
+    public final void addChild(AbstractNode node) {
+    }
+
+    @Override
+    public final void removeChild(AbstractNode node) {
+    }
+
+}

File registry/src/main/java/net/anatolich/registry/AbstractNode.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registry;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public abstract class AbstractNode<T extends Attributes> {
+
+    private final T attributes;
+    private final String name;
+    private final int position;
+    private AbstractNode parent;
+    private final List<AbstractNode> children;
+    private Set<NodeChangeListener> listeners = new HashSet<>();
+
+    public AbstractNode(String name, T attributes, int position) {
+        if (name.indexOf("/") > -1) {
+            throw new IllegalArgumentException(String.format("Invalid node name: %1$s", name));
+        }
+        this.attributes = attributes;
+        this.name = name;
+        this.position = position;
+        this.children = new ArrayList<>();
+    }
+
+    public final T getAttributes() {
+        return attributes;
+    }
+
+    public final String getName() {
+        return name;
+    }
+
+    public final AbstractNode getParent() {
+        return this.parent;
+    }
+
+    protected final void setParent(AbstractNode parentNode) {
+        this.parent = parentNode;
+    }
+
+    public final int position() {
+        return position;
+    }
+
+    protected static Comparator<AbstractNode> positionComparator() {
+        return new PositionalComparator();
+    }
+
+    public boolean hasParent() {
+        return getParent() != null;
+    }
+
+    public void addChild(AbstractNode node) {
+        AbstractNode existingNode = getChild(node.getName());
+        if( existingNode != null ){
+            int nodePosition = children.indexOf(existingNode);
+            children.set(nodePosition, node); // replacing old node with the same name
+        } else {
+            children.add(node);
+        }
+        
+        Collections.sort(children, AbstractNode.positionComparator());
+        node.setParent(this);
+        fireNodeAdded(node);
+    }
+
+    public Iterator<AbstractNode> childIterator() {
+        return children.iterator();
+    }
+
+    public final int childrenCount() {
+        return children.size();
+    }
+
+    public final AbstractNode getChild(String name) {
+        for (AbstractNode node : children) {
+            if (node.getName().equals(name)) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    public final AbstractNode getChild(int index) {
+        return children.get(index);
+    }
+
+    public void removeChild(AbstractNode node) {
+        boolean removed = children.remove(node);
+        if (removed) {
+            fireNodeRemoved(node);
+        }
+    }
+
+    public void addNodeChangeListener(NodeChangeListener changeListener) {
+        listeners.add(changeListener);
+    }
+
+    public void removeNodeChangeListener(NodeChangeListener changeListener) {
+        listeners.remove(changeListener);
+    }
+
+    private static class PositionalComparator implements Comparator<AbstractNode> {
+
+        @Override
+        public int compare(AbstractNode node1, AbstractNode node2) {
+            int result = node1.position() - node2.position();
+            if (result != 0) {
+                return result;
+            }
+
+            return node1.getName().compareToIgnoreCase(node2.getName());
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "AbstractNode{" + "name=" + name + ", position=" + position + '}';
+    }
+
+    protected final void fireNodeAdded(AbstractNode newNode) {
+        for (NodeChangeListener nodeChangeListener : listeners) {
+            nodeChangeListener.nodeAdded(new NodeChangeEvent(newNode, this));
+        }
+    }
+
+    protected final void fireNodeRemoved(AbstractNode removedNode) {
+        for (NodeChangeListener nodeChangeListener : listeners) {
+            nodeChangeListener.nodeRemoved(new NodeChangeEvent(removedNode, this));
+        }
+    }
+}

File registry/src/main/java/net/anatolich/registry/Attributes.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registry;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public abstract class Attributes {
+
+    private final Map<String, Object> namedAttributes;
+    private final Map<Class, Object> typedAttributes;
+
+    public Attributes() {
+        namedAttributes = new HashMap<>();
+        typedAttributes = new HashMap<>();
+    }
+
+    /**
+     * Gets value of named attribute.
+     *
+     * @param key attribute name
+     * @return value of attribute or null if no attribute registered.
+     */
+    public final Object getAttribute(String key) {
+        return namedAttributes.get(key);
+    }
+
+    public final <T> T getAttribute(Class<T> key) {
+        return (T) typedAttributes.get(key);
+    }
+
+    protected final void putAttribute(String key, Object value) {
+        namedAttributes.put(key, value);
+    }
+
+    protected final <T> void putAttribute(Class<T> key, T value) {
+        typedAttributes.put(key, value);
+    }
+}

File registry/src/main/java/net/anatolich/registry/NodeChangeEvent.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registry;
+
+import java.util.Objects;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class NodeChangeEvent {
+
+    private final AbstractNode node;
+    private final AbstractNode parent;
+    private int oldPosition;
+    private int newPosition;
+
+    public NodeChangeEvent(AbstractNode node, AbstractNode parent) {
+        this.node = node;
+        this.parent = parent;
+    }
+
+    public NodeChangeEvent(AbstractNode node, AbstractNode parent, int oldPosition, int newPosition) {
+        this.node = node;
+        this.parent = parent;
+        this.oldPosition = oldPosition;
+        this.newPosition = newPosition;
+    }
+
+    public AbstractNode getNode() {
+        return node;
+    }
+
+    public AbstractNode getParent() {
+        return parent;
+    }
+
+    public int getOldPosition() {
+        return oldPosition;
+    }
+
+    public int getNewPosition() {
+        return newPosition;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 53 * hash + Objects.hashCode(this.node);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final NodeChangeEvent other = (NodeChangeEvent) obj;
+        if (!Objects.equals(this.node, other.node)) {
+            return false;
+        }
+        return true;
+    }
+}

File registry/src/main/java/net/anatolich/registry/NodeChangeListener.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registry;
+
+/**
+ * Listens for structural changes in registry or part of registry.
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public interface NodeChangeListener {
+    
+    void nodeAdded(NodeChangeEvent event);
+    
+    void nodeRemoved(NodeChangeEvent event);
+    
+    void nodePositionChanges(NodeChangeEvent event);
+    
+}

File registry/src/main/java/net/anatolich/registry/NodeEventSupport.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registry;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+class NodeEventSupport {
+    
+    public void fireNodeAddedEvent(AbstractNode node){
+    }
+    
+    public void fireNodeRemovedEvent(AbstractNode node){
+    }
+    
+    public void fireNodePositionChanged(AbstractNode node, int oldPosition, int newPosition){
+    }
+    
+    public void addRegistryChangeListener(NodeChangeListener listener){
+    }
+    
+    public void addRegistryChangeListener(NodeChangeListener listener, Path path){
+    }
+    
+    public void removeRegistryChangeListener(NodeChangeListener listener){
+    }
+
+}

File registry/src/main/java/net/anatolich/registry/NodeIsNotPartOfRegistryException.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registry;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public final class NodeIsNotPartOfRegistryException extends RuntimeException {
+
+    private final AbstractNode node;
+    private final String message;
+
+    public NodeIsNotPartOfRegistryException(AbstractNode node) {
+        this.node = node;
+        this.message = createMessage();
+    }
+
+    public NodeIsNotPartOfRegistryException(AbstractNode node, Throwable cause) {
+        super(cause);
+        this.node = node;
+        this.message = createMessage();
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+
+    }
+
+    private String createMessage() {
+        return String.format("Node %1$s is not registered in registry", node);
+    }
+}

File registry/src/main/java/net/anatolich/registry/NotExistingEntryException.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registry;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class NotExistingEntryException extends RuntimeException {
+
+    private final Path path;
+    private final String message;
+
+    public NotExistingEntryException(Path path) {
+        this.path = path;
+        this.message = createMessage();
+    }
+
+    public NotExistingEntryException(Path path, Throwable cause) {
+        super(cause);
+        this.path = path;
+        this.message = createMessage();
+
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+
+    }
+
+    public Path getPath() {
+        return path;
+    }
+
+    private String createMessage() {
+        return String.format("No registry entry exist for path %1$s", path.toString());
+    }
+}

File registry/src/main/java/net/anatolich/registry/Path.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registry;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents slash-separated path over registry nodes. Each string between
+ * slashes is a value of node's name property. Example: let's assume that we
+ * have registry part representing menu structure. We need to get a link to node
+ * representing "Recent Files" of File menu. Tree for that can be formed of the
+ * following nodes: "menubar" -> "file_menu" -> "recent_items". Path for it will
+ * be /menubar/file_menu/recent_items
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public final class Path {
+
+    private static List<String> parsePath(String path) {
+        List<String> elementList = new ArrayList<>(Arrays.asList(path.split("/")));
+        removeEmptyParts(elementList);
+        return elementList;
+    }
+
+    private final List<String> elementList;
+    private final String pathString;
+
+    Path(List<String> elementList) {
+        this.elementList = elementList;
+        pathString = createStringRepresentation();
+    }
+
+    public static Path fromString(String path) {
+        List<String> elementList = parsePath(path);
+
+        return new Path(elementList);
+    }
+
+    public static Path fromBasePath(Path base, String subPath) {
+        List<String> elementList = base.elementList;
+        elementList.addAll(parsePath(subPath));
+        return new Path(elementList);
+    }
+
+    private static void removeEmptyParts(List<String> elementList) {
+        Iterator<String> listIterator = elementList.iterator();
+        while (listIterator.hasNext()) {
+            String element = listIterator.next();
+            if (element.isEmpty()) {
+                listIterator.remove();
+            }
+        }
+    }
+
+    /**
+     * Gets path representing direct parent of current path or null if this path
+     * is a path of registry root node
+     *
+     * @return
+     */
+    public Path getParent() {
+        if(elementList.isEmpty()){
+            return null;
+        }
+        
+        if (elementList.size() == 1){
+            return Path.fromString("/");
+        }
+        
+        return new Path(elementList.subList(0, elementList.size() - 1));
+    }
+
+    public boolean isRootNodePath() {
+        return elementList.isEmpty();
+    }
+
+    protected Iterator<String> pathElementsIterator() {
+        return Collections.unmodifiableList(elementList).iterator();
+    }
+
+    private String createStringRepresentation() {
+        Iterator<String> iterator = elementList.iterator();
+        StringBuilder builder = new StringBuilder();
+        while (iterator.hasNext()) {
+            String element = iterator.next();
+            builder.append("/").append(element);
+        }
+        return builder.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 83 * hash + Objects.hashCode(this.pathString);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Path other = (Path) obj;
+        if (!Objects.equals(this.pathString, other.pathString)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "Path{" + pathString +"}";
+    }
+    
+    
+}

File registry/src/main/java/net/anatolich/registry/Registry.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registry;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public final class Registry implements NodeChangeListener {
+    private final AbstractNode rootNode;
+    private NodeEventSupport registryEventSupport;
+
+    public Registry() {
+        rootNode = new RootNode();
+        this.registryEventSupport = new NodeEventSupport();
+        this.rootNode.addNodeChangeListener(this);
+    }
+
+    Registry(NodeEventSupport registryEventSupport) {
+        this.rootNode = new RootNode();
+        this.registryEventSupport = registryEventSupport;
+        this.rootNode.addNodeChangeListener(this);
+    }
+    
+    /**
+     * 
+     * @param path
+     * @return 
+     * @throws NotExistingEntryException when registry entry for path not exist.
+     */
+    public AbstractNode getNodeByPath(Path path){
+        Iterator<String> pathElementsIterator = path.pathElementsIterator();
+        AbstractNode currentNode = rootNode;
+        while (pathElementsIterator.hasNext()) {
+            String nodeName = pathElementsIterator.next();
+            currentNode = currentNode.getChild(nodeName);
+            if (currentNode ==  null){
+                throw new NotExistingEntryException(path);
+            }
+        }
+        return currentNode;
+    }
+
+    public AbstractNode rootNode() {
+        return rootNode;
+    }
+
+    /**
+     * 
+     * @param node
+     * @return 
+     * @throws NodeIsNotPartOfRegistryException when node is not registered in registry.
+     */
+    public Path getNodePath(AbstractNode node) {
+        List<String> pathElements = new ArrayList<>();
+        AbstractNode currentNode = node;
+        while(currentNode.hasParent()){ 
+
+            String nodeName = currentNode.getName();
+            
+            if(nodeName != null && !nodeName.isEmpty()){
+                pathElements.add(nodeName);
+            }
+            
+            currentNode = currentNode.getParent();
+        }
+        
+        if(!currentNode.equals(rootNode)){ // Check if node is detached 
+            throw new NodeIsNotPartOfRegistryException(node);
+        }
+        
+        Collections.reverse(pathElements);
+        return new Path(pathElements);
+    }
+
+    @Override
+    public void nodeAdded(NodeChangeEvent event) {
+        AbstractNode addedNode = event.getNode();
+        listenNode(addedNode);
+        this.registryEventSupport.fireNodeAddedEvent(addedNode);
+    }
+
+    @Override
+    public void nodeRemoved(NodeChangeEvent event) {
+        AbstractNode removedNode = event.getNode();
+        removedNode.removeNodeChangeListener(this);
+        this.registryEventSupport.fireNodeRemovedEvent(removedNode);
+    }
+
+    @Override
+    public void nodePositionChanges(NodeChangeEvent event) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    private void listenNode(AbstractNode addedNode) {
+        Iterator<AbstractNode> childIterator = addedNode.childIterator();
+        while (childIterator.hasNext()) {
+            AbstractNode childNode = childIterator.next();
+            listenNode(childNode);
+            
+        }
+        addedNode.addNodeChangeListener(this);
+    }
+    
+    private static class RootNode extends AbstractNode<Attributes> {
+
+        public RootNode() {
+            super("", new Attributes() {}, 0);
+        }
+        
+    }
+    
+}

File registry/src/main/java/net/anatolich/registryimpl/menu/CheckboxMenuItemNode.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registryimpl.menu;
+
+import net.anatolich.registry.AbstractItemNode;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class CheckboxMenuItemNode extends AbstractItemNode<MenuItemAttributes> implements MenuVisitorAcceptor {
+
+    public CheckboxMenuItemNode(String name, MenuItemAttributes attributes) {
+        super(name, attributes);
+    }
+
+    @Override
+    public void accept(MenuBuilderVisitor visitor) {
+    }
+}

File registry/src/main/java/net/anatolich/registryimpl/menu/MenuAttributes.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registryimpl.menu;
+
+import javax.swing.Action;
+import javax.swing.JMenu;
+import net.anatolich.registry.Attributes;
+
+
+public class MenuAttributes  extends Attributes{
+    private final Action action;
+    private JMenu menuItem;
+
+    public MenuAttributes(Action action, int position) {
+        this.action = action;
+    }
+
+    public Action getAction() {
+        return action;
+    }
+
+    public JMenu getMenuItem() {
+        return menuItem;
+    }
+
+    public void setMenuItem(JMenu menu) {
+        this.menuItem = menu;
+    }
+    
+}

File registry/src/main/java/net/anatolich/registryimpl/menu/MenuBuilderVisitor.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registryimpl.menu;
+
+import javax.swing.JMenuBar;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public final class MenuBuilderVisitor implements MenuVisitor {
+    
+    @Override
+    public void visitMenuStart(MenuNode node){}
+    
+    @Override
+    public void visitMenuEnd() {}
+    
+    @Override
+    public void visitMenuItem(MenuItemNode node){}
+    
+    @Override
+    public void visitRadioButtonItem(RadioButtonMenuItemNode node) {}
+    
+    @Override
+    public void visitSeparatorItem (MenuSeparatorNode node) {}
+    
+    @Override
+    public JMenuBar getMenuBar(){
+        return null;
+    }
+}

File registry/src/main/java/net/anatolich/registryimpl/menu/MenuItemAttributes.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registryimpl.menu;
+
+import javax.swing.Action;
+import javax.swing.JMenuItem;
+import net.anatolich.registry.Attributes;
+
+public class MenuItemAttributes extends Attributes{
+
+    private final Action action;
+    private JMenuItem menuItem;
+
+    public MenuItemAttributes(Action action, int position) {
+        this.action = action;
+    }
+
+    public Action getAction() {
+        return action;
+    }
+
+    public JMenuItem getMenuItem() {
+        return menuItem;
+    }
+
+    public void setMenuItem(JMenuItem menuItem) {
+        this.menuItem = menuItem;
+    }
+}

File registry/src/main/java/net/anatolich/registryimpl/menu/MenuItemNode.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registryimpl.menu;
+
+import net.anatolich.registry.AbstractItemNode;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class MenuItemNode extends AbstractItemNode<MenuItemAttributes> implements MenuVisitorAcceptor {
+
+    public MenuItemNode(String name, MenuItemAttributes attributes) {
+        super(name, attributes);
+    }
+
+    @Override
+    public void accept(MenuBuilderVisitor visitor) {
+        visitor.visitMenuItem(this);
+    }
+    
+}

File registry/src/main/java/net/anatolich/registryimpl/menu/MenuNode.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registryimpl.menu;
+
+import java.util.Iterator;
+import net.anatolich.registry.AbstractNode;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class MenuNode extends AbstractNode<MenuAttributes> implements MenuVisitorAcceptor {
+
+    public MenuNode(String name, MenuAttributes attributes, int position) {
+        super(name, attributes, position);
+    }
+    
+    @Override
+    public void accept(MenuBuilderVisitor visitor){
+        visitor.visitMenuStart(this);
+        
+        Iterator<AbstractNode> childIterator = childIterator();
+        while (childIterator.hasNext()) {
+            AbstractNode node = childIterator.next();
+            ((MenuVisitorAcceptor) node).accept(visitor);
+        }
+        
+        visitor.visitMenuEnd();
+        
+    }
+}

File registry/src/main/java/net/anatolich/registryimpl/menu/MenuSeparatorNode.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registryimpl.menu;
+
+import net.anatolich.registry.AbstractItemNode;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class MenuSeparatorNode extends AbstractItemNode<SeparatorAttributes> implements MenuVisitorAcceptor {
+
+    public MenuSeparatorNode(String name, SeparatorAttributes attributes) {
+        super(name, attributes);
+    }
+
+    @Override
+    public void accept(MenuBuilderVisitor visitor) {
+        visitor.visitSeparatorItem(this);
+    }
+}

File registry/src/main/java/net/anatolich/registryimpl/menu/MenuVisitor.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registryimpl.menu;
+
+import javax.swing.JMenuBar;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public interface MenuVisitor {
+
+    JMenuBar getMenuBar();
+
+    void visitMenuEnd();
+
+    void visitMenuItem(MenuItemNode node);
+
+    void visitMenuStart(MenuNode node);
+
+    void visitRadioButtonItem(RadioButtonMenuItemNode node);
+
+    void visitSeparatorItem(MenuSeparatorNode node);
+    
+}

File registry/src/main/java/net/anatolich/registryimpl/menu/MenuVisitorAcceptor.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registryimpl.menu;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public interface MenuVisitorAcceptor {
+
+    void accept(MenuBuilderVisitor visitor);
+    
+}

File registry/src/main/java/net/anatolich/registryimpl/menu/RadioButtonMenuItemAttributes.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registryimpl.menu;
+
+import javax.swing.Action;
+import javax.swing.ButtonGroup;
+import javax.swing.JRadioButtonMenuItem;
+import net.anatolich.registry.Attributes;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class RadioButtonMenuItemAttributes extends Attributes {
+
+    private final Action action;
+    private final String groupName;
+    private ButtonGroup group;
+    private JRadioButtonMenuItem menuItem;
+
+    public RadioButtonMenuItemAttributes(Action action, String groupName, int position) {
+        this.action = action;
+        this.groupName = groupName;
+    }
+
+    public Action getAction() {
+        return action;
+    }
+
+    public String getGroupName() {
+        return groupName;
+    }
+
+    public ButtonGroup getGroup() {
+        return group;
+    }
+
+    public JRadioButtonMenuItem getMenuItem() {
+        return menuItem;
+    }
+
+    public void setGroup(ButtonGroup group) {
+        this.group = group;
+    }
+
+    public void setMenuItem(JRadioButtonMenuItem menuItem) {
+        this.menuItem = menuItem;
+    }
+}

File registry/src/main/java/net/anatolich/registryimpl/menu/RadioButtonMenuItemNode.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registryimpl.menu;
+
+import net.anatolich.registry.AbstractItemNode;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class RadioButtonMenuItemNode extends AbstractItemNode<RadioButtonMenuItemAttributes> implements MenuVisitorAcceptor {
+
+    public RadioButtonMenuItemNode(String name, RadioButtonMenuItemAttributes attributes) {
+        super(name, attributes);
+    }
+
+    @Override
+    public void accept(MenuBuilderVisitor visitor) {
+        visitor.visitRadioButtonItem(this);
+    }
+}

File registry/src/main/java/net/anatolich/registryimpl/menu/SeparatorAttributes.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registryimpl.menu;
+
+import net.anatolich.registry.Attributes;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class SeparatorAttributes extends Attributes{
+
+    private final int position;
+
+    public SeparatorAttributes(int position) {
+        this.position = position;
+    }
+
+    public int getPosition() {
+        return position;
+    }
+}

File registry/src/test/java/net/anatolich/registry/AbstractItemNodeTest.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registry;
+
+import net.anatolich.registry.AbstractItemNode;
+import net.anatolich.registry.AbstractNode;
+import net.anatolich.registry.Attributes;
+import org.easymock.EasyMockSupport;
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class AbstractItemNodeTest extends EasyMockSupport{
+   
+    private AbstractItemNode node;
+   
+    @Before
+    public void setUp() {
+        node = new AbstractItemNode("Name", createNiceMock(Attributes.class)) {};
+    }
+
+    @Test
+    public void testAddNode() {
+        node.addChild(createMock(AbstractNode.class));
+        
+        assertEquals(0, node.childrenCount());
+    }
+    
+    @Test
+    public void testRemoveNode() {
+        node.removeChild(createMock(AbstractNode.class));
+        
+        assertEquals(0, node.childrenCount());
+    }
+    
+    @Test(expected=IndexOutOfBoundsException.class)
+    public void testGetChild(){
+        node.getChild(0);
+        
+    }
+    
+    @Test
+    public void ensureEmptyIteratorReturned(){
+        assertFalse(node.childIterator().hasNext());
+    }
+}

File registry/src/test/java/net/anatolich/registry/AbstractNodeTest.java

View file
  • Ignore whitespace
+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.registry;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import static org.easymock.EasyMock.*;
+import org.easymock.EasyMockSupport;
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class AbstractNodeTest extends EasyMockSupport {
+    protected AbstractNode groupNode;
+    protected AbstractNode node1 = new AbstractNode("node1", null, 0) {
+    };
+    protected AbstractNode node2 = new AbstractNode("node2", null, 0) {
+    };
+
+    /**
+     * Test of getAttributes method, of class AbstractNode.
+     */
+    @Test
+    public void testGetAttributes() {
+        System.out.println("getAttributes");
+        final Attributes attributes = createNiceMock(Attributes.class);
+        AbstractNode instance = new AbstractNodeImpl(attributes);
+        Attributes expResult = attributes;
+        Attributes result = instance.getAttributes();
+        assertEquals(expResult, result);
+    }
+
+    /**
+     * Test of getName method, of class AbstractNode.
+     */
+    @Test
+    public void testGetName() {
+        System.out.println("getName");
+        final String name = "NameToTest";
+        AbstractNode instance = new AbstractNodeImpl(name, null);
+        String expResult = name;
+        String result = instance.getName();
+        assertEquals(expResult, result);
+    }
+
+    /**
+     * Test of getParent method, of class AbstractNode.
+     */
+    @Test
+    public void testGetParent() {
+        System.out.println("getParent");
+        final AbstractNode parentNode = createNiceMock(AbstractNode.class);
+        AbstractNode instance = new AbstractNodeImpl("instance", null);
+        AbstractNode expResult = parentNode;
+        instance.setParent(parentNode);
+        AbstractNode result = instance.getParent();
+        assertEquals(expResult, result);
+    }
+    
+    @Test
+    public void testHasParent(){
+        final AbstractNode parentNode = createNiceMock(AbstractNode.class);
+        AbstractNode instance = new AbstractNodeImpl("instance", null);
+        
+        boolean result = instance.hasParent();        
+        assertFalse("Parent node it not set so hasParent must be false", result);
+        
+        instance.setParent(parentNode);
+        result = instance.hasParent();
+        assertTrue("Parent node it set so hasParent must be true", result);
+    }
+
+    @Test
+    public void testPositionComparator() {
+        System.out.println("positionComparator");
+        final AbstractNode node100a = new AbstractNodeImpl("A", null, 100);
+        final AbstractNode node100b = new AbstractNodeImpl("B", null, 100);
+        final AbstractNode node200 = new AbstractNodeImpl("A", null, 200);
+
+        Comparator<AbstractNode> comparator = AbstractNode.positionComparator();
+
+        assertTrue("node200, node100a", comparator.compare(node200, node100a) > 0);
+        assertTrue("node100a, node200", comparator.compare(node100a, node200) < 0);
+        assertTrue("node100a, node100b", comparator.compare(node100a, node100b) < 0);
+        assertTrue("node100b, node100a", comparator.compare(node100b, node100a) > 0);
+        assertTrue("node100a, node100a", comparator.compare(node100a, node100a) == 0);
+
+    }
+    
+    @Test(expected=IllegalArgumentException.class)
+    public void testInvalidNodeName(){
+        String invalidName = "invalid/node/name"; //must not contain slashes
+        
+        AbstractNode node = new AbstractNodeImpl(invalidName);
+    }
+
+    @Before
+    public void setUp() {
+        groupNode = new AbstractNode("Name", new Attributes() {}, 0) {};
+    }
+
+    @Test
+    public void testAddingChild() {
+        NodeChangeListener changeListener = createMock(NodeChangeListener.class);
+        
+        changeListener.nodeAdded(new NodeChangeEvent(node1, groupNode));
+        changeListener.nodeAdded(new NodeChangeEvent(node2, groupNode));
+        
+        groupNode.addNodeChangeListener(changeListener);
+        replay(changeListener);
+        
+        groupNode.addChild(node1);
+        assertEquals(1, groupNode.childrenCount());
+        
+        groupNode.addChild(node2);
+        assertEquals(2, groupNode.childrenCount());
+        
+        verify(changeListener);
+    }
+    
+    @Test
+    public void testRemoveChild(){
+        NodeChangeListener changeListener = createMock(NodeChangeListener.class);
+        
+        changeListener.nodeAdded(new NodeChangeEvent(node1, groupNode));
+        changeListener.nodeAdded(new NodeChangeEvent(node2, groupNode));
+        
+        changeListener.nodeRemoved(new NodeChangeEvent(node2, groupNode));
+        changeListener.nodeRemoved(new NodeChangeEvent(node1, groupNode));
+        
+        replay(changeListener);
+        
+        //Initialization
+        groupNode.addNodeChangeListener(changeListener);
+        groupNode.addChild(node1);
+        groupNode.addChild(node2);
+        
+        groupNode.removeChild(node2);
+        assertEquals("Should be one child node after removing one child", 1, groupNode.childrenCount());
+        
+        groupNode.removeChild(node2);
+        assertEquals("Children count should not change after removing non-existing node", 1, groupNode.childrenCount());
+        
+        groupNode.removeChild(node1);
+        assertEquals("Children count should not change after removing non-existing node", 0, groupNode.childrenCount());
+                
+        verify(changeListener);        
+    }
+
+    @Test
+    public void testChildSorting() {
+        AbstractNode node100 = new AbstractNode("Node100", null, 100) {
+        };
+        AbstractNode node200 = new AbstractNode("Node200", null, 200) {
+        };
+        AbstractNode node300 = new AbstractNode("Node300", null, 300) {
+        };
+        groupNode.addChild(node300);
+        groupNode.addChild(node100);
+        groupNode.addChild(node200);
+        Iterator<AbstractNode> childIterator = groupNode.childIterator();
+        assertEquals(node100, childIterator.next());
+        assertEquals(node200, childIterator.next());
+        assertEquals(node300, childIterator.next());
+    }
+    
+    @Test
+    public void testNodeOverrideByName(){
+        String nodeName = "item";
+        AbstractNode earlierNode = new AbstractNode(nodeName, null, 0) {};
+        AbstractNode latterNode = new AbstractNode(nodeName, null, 10) {};
+        
+        groupNode.addChild(earlierNode);
+        groupNode.addChild(latterNode);
+        
+        assertEquals("Nodes with the same name must override each other",1, groupNode.childrenCount());
+        assertEquals("Latter node must be available by named query", latterNode, groupNode.getChild(nodeName));
+    }
+
+    @Test
+    public void testGetChildByName() {
+        groupNode.addChild(node1);
+        groupNode.addChild(node2);
+        final String node1Name = "node1";
+        assertEquals(String.format("name -> %1$s", node1Name), node1, groupNode.getChild(node1Name));
+        final String node2Name = "node2";
+        assertEquals(String.format("name -> %1$s", node2Name), node2, groupNode.getChild(node2Name));
+        final String node3Name = "node3";
+        assertNull(String.format("name -> %1$s", node3Name), groupNode.getChild(node3Name));
+    }
+
+    @Test
+    public void testGetChildByNumber() {
+        node1 = new AbstractNode("node1", null, 10) {
+        };
+        node2 = new AbstractNode("node2", null, 20) {
+        };
+        groupNode.addChild(node1);
+        groupNode.addChild(node2);
+        assertEquals(node2, groupNode.getChild(1));
+        assertEquals(node1, groupNode.getChild(0));
+    }
+
+    @Test
+    public void testParentSettingOnAdding() {
+        AbstractNode childNode = createMock(AbstractNode.class);
+        replay(childNode);
+        groupNode.addChild(childNode);
+        assertEquals(groupNode, childNode.getParent());
+    }
+
+    /**
+     * Test of setParent method, of class AbstractNode.
+     */
+    private static class AbstractNodeImpl extends AbstractNode {
+
+        public AbstractNodeImpl(String name) {
+            super(name, null, 0);
+        }
+