/*
* JAME 6.2.1
* http://jame.sourceforge.net
*
* Copyright 2001, 2016 Andrea Medeghini
*
* This file is part of JAME.
*
* JAME is an application for creating fractals and other graphics artifacts.
*
* JAME is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JAME is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JAME. If not, see <http://www.gnu.org/licenses/>.
*
*/
package net.sf.jame.core.tree;
import net.sf.jame.core.config.ConfigContext;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author Andrea Medeghini
*/
public abstract class Node {
private static final Logger logger = Logger.getLogger(Node.class.getName());
private final Map<String, Object> map = new HashMap<String, Object>();
private List<NodeCommand> commandList = new LinkedList<NodeCommand>();
private List<Node> childList;
private NodeEditor editor;
private Node parentNode;
private boolean changed;
private final String nodeId;
private String nodeLabel;
private String nodeClass;
private String extensionId;
private NodeValue<?> value;
private NodeValue<?> previousValue;
private ConfigContext context;
private NodeSession session;
/**
* @see java.lang.Object#finalize()
*/
@Override
protected void finalize() throws Throwable {
dispose();
super.finalize();
}
/**
* @return
*/
public boolean isHighFrequency() {
return false;
}
/**
*
*/
public void dispose() {
if (commandList != null) {
commandList.clear();
commandList = null;
}
if (childList != null) {
for (Node child : childList) {
child.dispose();
}
childList.clear();
childList = null;
}
value = null;
previousValue = null;
parentNode = null;
session = null;
context = null;
editor = null;
}
/**
* Constructs a new node.
*
* @param nodeId the nodeId.
*/
public Node(final String nodeId) {
if (nodeId == null) {
throw new IllegalArgumentException("nodeId is null");
}
this.nodeId = nodeId;
}
/**
* @param e the node event.
*/
void fireNodeChanged(final NodeEvent e) {
if (parentNode != null) {
parentNode.fireNodeChanged(e);
}
}
/**
* @param e the node event.
*/
void fireNodeAdded(final NodeEvent e) {
if (parentNode != null) {
parentNode.fireNodeAdded(e);
}
}
/**
* @param e the node event.
*/
void fireNodeRemoved(final NodeEvent e) {
if (parentNode != null) {
parentNode.fireNodeRemoved(e);
}
}
/**
* @param e the node event.
*/
void fireNodeAccepted(final NodeEvent e) {
if (parentNode != null) {
parentNode.fireNodeAccepted(e);
}
}
/**
* @param e the node event.
*/
void fireNodeCancelled(final NodeEvent e) {
if (parentNode != null) {
parentNode.fireNodeCancelled(e);
}
}
/**
* @param command the node command.
*/
void fireNodeCommandCreated(final NodeCommand command) {
commandList.add(command);
if (parentNode != null) {
parentNode.fireNodeCommandCreated(command);
}
}
// /**
// * @param command the node command.
// */
// void fireNodeCommandDisposed(final NodeCommand command) {
// commandList.remove(command);
// if (parentNode != null) {
// parentNode.fireNodeCommandDisposed(command);
// }
// }
/**
*
*/
protected void fireNodeChanged() {
this.fireNodeChanged(new NodeEvent(this, getNodePath()));
}
/**
*
*/
protected void fireNodeAdded(final NodePath path) {
// if (parentNode != null) {
// parentNode.fireNodeChanged();
// }
this.fireNodeAdded(new NodeEvent(this, path));
}
/**
*
*/
protected void fireNodeRemoved(final NodePath path) {
// if (parentNode != null) {
// parentNode.fireNodeChanged();
// }
this.fireNodeRemoved(new NodeEvent(this, path));
}
/**
*
*/
protected void fireNodeAccepted(final NodePath path) {
this.fireNodeAccepted(new NodeEvent(this, path));
}
/**
*
*/
protected void fireNodeCancelled(final NodePath path) {
this.fireNodeCancelled(new NodeEvent(this, path));
}
/**
* Returns the node path.
*
* @return the path.
*/
public NodePath getNodePath() {
NodePath path;
if (parentNode != null) {
path = parentNode.getNodePath();
path.addPathElement(parentNode.getChildList().indexOf(this));
}
else {
path = new NodePath();
}
return path;
}
private void setParentNode(final Node parentNode) {
this.parentNode = parentNode;
}
/**
* @param session
*/
public void setSession(final NodeSession session) {
this.session = session;
// if (Node.logger.isDebugEnabled()) {
// if (session != null) {
// Node.logger.debug("Set session to \"" + session.getSessionName() + "\" for node \"" + getNodeId() + "\"");
// }
// else {
// Node.logger.debug("Set session to null for node \"" + getNodeId() + "\"");
// }
// }
for (final Node node : getChildList()) {
node.setSession(session);
}
}
/**
* @return
*/
public NodeSession getSession() {
if (session == null) {
throw new IllegalStateException("Session is not defined");
}
return session;
}
public boolean existsSession() {
return session != null;
}
/**
* @param context
*/
public void setContext(final ConfigContext context) {
this.context = context;
for (final Node node : getChildList()) {
node.setContext(context);
}
}
/**
* @return
*/
public ConfigContext getContext() {
if (context == null) {
throw new IllegalStateException("Context is not defined");
}
return context;
}
/**
* Returns the parent.
*
* @return the parent.
*/
public Node getParentNode() {
return parentNode;
}
/**
* @param node
* @return
*/
public boolean isChildNode(final Node node) {
return childList.contains(node);
}
/**
* Returns a child.
*
* @param index the child index.
* @return the child.
*/
public Node getChildNode(final int index) {
if ((index < 0) || (index >= getChildList().size())) {
return null;
}
return getChildList().get(index);
}
/**
* Returns the number of childs.
*
* @return the number of childs.
*/
public int getChildNodeCount() {
return getChildList().size();
}
/**
* Returns the node value.
*
* @return the node value.
*/
public NodeValue<?> getNodeValue() {
return value;
}
/**
* Returns the previous node value.
*
* @return the previous node value.
*/
public NodeValue<?> getPreviousNodeValue() {
return previousValue;
}
/**
* Sets the node value.
*
* @param value the node value to set.
*/
protected final void setNodeValue(final NodeValue<?> value) {
if (Node.isValueChanged(value, this.value)) {
previousValue = this.value;
this.value = value;
updateNode();
this.fireNodeChanged();
}
}
/**
* @param value
* @param prevValue
* @return
*/
protected static boolean isValueChanged(final Object value, final Object prevValue) {
return ((value == null) && (prevValue != null)) || ((value != null) && !value.equals(prevValue));
}
/**
*
*/
protected void updateNode() {
updateChildNodes();
}
/**
*
*/
protected void updateChildNodes() {
}
/**
* Returns the node editor.
*
* @return the node editor.
*/
public NodeEditor getNodeEditor() {
return editor;
}
/**
* Sets the node editor.
*
* @param editor the node editor to set.
*/
protected void setNodeEditor(final NodeEditor editor) {
this.editor = editor;
}
/**
* Returns the nodeId.
*
* @return the nodeId.
*/
public String getNodeId() {
return nodeId;
}
/**
* Returns the nodeClass.
*
* @return the nodeClass.
*/
public String getNodeClass() {
return nodeClass;
}
/**
* Sets the nodeClass.
*
* @param the nodeClass.
*/
protected void setNodeClass(final String nodeClass) {
this.nodeClass = nodeClass;
}
/**
* Returns the nodeLabel.
*
* @return the nodeLabel
*/
public String getNodeLabel() {
return nodeLabel;
}
/**
* Sets the nodeClass.
*
* @param the nodeClass.
*/
protected void setNodeLabel(final String nodeLabel) {
this.nodeLabel = nodeLabel;
}
/**
* Returns true if node has pending commands.
*
* @return true if node has pending commands.
*/
public boolean hasPendingCommands() {
return changed;
}
/**
* Accepts node value.
*
* @param session
*/
public final void accept() {
for (int i = 0; i < commandList.size(); i++) {
final NodeCommand command = commandList.get(i);
if (!command.isConsumed()) {
command.accept(getSession(), getContext().getTimestamp());
command.consume();
}
}
doAccept();
}
/**
* Cancels node value.
*/
public final void cancel() {
for (int i = commandList.size() - 1; i >= 0; i--) {
final NodeCommand command = commandList.get(i);
if (!command.isConsumed()) {
command.cancel();
command.consume();
}
}
doCancel();
}
/**
*
*/
protected final void doAccept() {
commandList.clear();
for (int i = 0; i < getChildNodeCount(); i++) {
getChildNode(i).doAccept();
}
if (changed) {
changed = false;
this.fireNodeChanged();
}
fireNodeAccepted(getNodePath());
}
/**
*
*/
protected final void doCancel() {
commandList.clear();
for (int i = 0; i < getChildNodeCount(); i++) {
getChildNode(i).doCancel();
}
if (changed) {
changed = false;
this.fireNodeChanged();
}
fireNodeCancelled(getNodePath());
}
/**
* Returns true if the node is mutable.
*
* @return true if the node is mutable.
*/
public boolean isMutable() {
return false;
}
/**
* Returns true if the node is editable.
*
* @return true if the node is editable.
*/
public boolean isEditable() {
return false;
}
/**
* Returns true if the node is an attribute.
*
* @return true if the node is an attribute.
*/
public boolean isAttribute() {
return false;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
if (hasPendingCommands()) {
builder.append("*");
}
builder.append(nodeId);
builder.append(" (");
builder.append(nodeClass != null ? nodeClass : "<no class>");
builder.append(")");
if (extensionId != null) {
builder.append(" [");
builder.append(extensionId);
builder.append("]");
}
if (isAttribute() && (getNodeEditor() != null)) {
builder.append(" value = [");
builder.append(getNodeEditor().getNodeValueAsString());
builder.append("]");
}
return builder.toString();
}
/**
* Appends a child to parent.
*
* @param node the child to append.
*/
protected void appendChildNodeToParent(final Node node) {
if (parentNode != null) {
parentNode.appendChildNode(node);
}
}
/**
* Appends a child.
*
* @param node the child to append.
*/
public void appendChildNode(final Node node) {
node.setParentNode(this);
node.setContext(context);
node.setSession(session);
if (getChildList().contains(node)) {
if (Node.logger.isLoggable(Level.FINE)) {
Node.logger.fine("Node " + node.getLabel() + " is already in the list");
}
return;
}
getChildList().add(node);
final NodePath path = node.getNodePath();
node.fireNodeAdded(path);
node.nodeAdded();
}
/**
* Removes a child.
*
* @param index the child to remove.
*/
public void removeChildNode(final int nodeIndex) {
final Node node = getChildList().get(nodeIndex);
final NodePath path = node.getNodePath();
node.fireNodeRemoved(path);
getChildList().remove(nodeIndex);
node.nodeRemoved();
node.setContext(null);
node.setSession(null);
node.setParentNode(null);
}
/**
* Removes all the children.
*/
public void removeAllChildNodes() {
for (int i = getChildNodeCount() - 1; i >= 0; i--) {
removeChildNode(i);
}
}
/**
* @param index
* @param node
*/
public void insertNodeBefore(final int index, final Node node) {
node.setParentNode(this);
node.setContext(context);
node.setSession(session);
if (getChildList().contains(node)) {
if (Node.logger.isLoggable(Level.FINE)) {
Node.logger.fine("Node " + node.getLabel() + " is already in the list");
}
return;
}
if ((index < 0) || (index > getChildList().size())) {
throw new IllegalArgumentException("index out of bounds");
}
getChildList().add(index, node);
final NodePath path = node.getNodePath();
node.fireNodeAdded(path);
node.nodeAdded();
}
/**
* @param index
* @param node
*/
public void insertNodeAfter(final int index, final Node node) {
node.setParentNode(this);
node.setContext(context);
node.setSession(session);
if (getChildList().contains(node)) {
if (Node.logger.isLoggable(Level.FINE)) {
Node.logger.fine("Node " + node.getLabel() + " is already in the list");
}
return;
}
if ((index < 0) || (index > getChildList().size() - 1)) {
throw new IllegalArgumentException("index out of bounds");
}
if (index < getChildList().size() - 1) {
getChildList().add(index + 1, node);
}
else {
getChildList().add(node);
}
final NodePath path = node.getNodePath();
node.fireNodeAdded(path);
node.nodeAdded();
}
/**
* @param index
* @param node
*/
public void insertChildNodeAt(final int index, final Node node) {
if (index < getChildList().size()) {
insertNodeBefore(index, node);
}
else if (index > 0) {
insertNodeAfter(index - 1, node);
}
else {
appendChildNode(node);
}
}
/**
* @param index
*/
public void moveUpChildNode(final int index) {
final Node node = getChildList().get(index);
if (index > 0) {
removeChildNode(index);
insertNodeBefore(index - 1, node);
}
}
/**
* @param index
*/
public void moveDownChildNode(final int index) {
final Node node = getChildList().get(index);
if (index < getChildList().size() - 1) {
removeChildNode(index);
insertNodeAfter(index, node);
}
}
/**
* @param index
*/
public void setChildNode(final int index, final Node node) {
if ((index < 0) || (index > getChildList().size() - 1)) {
throw new IllegalArgumentException("index out of bounds");
}
removeChildNode(index);
insertNodeAfter(index, node);
}
/**
* Returns the node value as string.
*
* @return the string.
*/
public String getValueAsString() {
return toString();
}
/**
* @return the label.
*/
public final String getLabel() {
final StringBuilder builder = new StringBuilder();
if (hasPendingCommands()) {
builder.append("*");
}
addLabel(builder);
return builder.toString();
}
/**
* @param builder
*/
protected void addLabel(final StringBuilder builder) {
if (nodeLabel != null) {
builder.append(nodeLabel);
}
}
/**
* @return the description.
*/
public final String getDescription() {
final StringBuilder builder = new StringBuilder();
addDescription(builder);
if (parentNode != null) {
builder.append(" [");
builder.append(parentNode.getChildList().indexOf(this));
builder.append("]");
}
return builder.toString();
}
/**
* @param builder
*/
protected void addDescription(final StringBuilder builder) {
addLabel(builder);
}
/**
* @param command
*/
void appendCommand(final NodeCommand command) {
changed = true;
fireNodeCommandCreated(command);
}
/**
* @param node
* @return the index.
*/
public int indexOf(final Node node) {
return getChildList().indexOf(node);
}
private List<Node> getChildList() {
if (childList == null) {
childList = new ArrayList<Node>();
}
return childList;
}
/**
*
*/
protected void nodeAdded() {
}
/**
*
*/
protected void nodeRemoved() {
}
/**
* @return
*/
public String dump() {
final StringBuilder builder = new StringBuilder();
dumpNode(builder, this, 0);
return builder.toString();
}
private void dumpNode(final StringBuilder builder, final Node node, final int level) {
for (int i = 0; i < level; i++) {
builder.append(" ");
}
builder.append(node);
if (node.getChildNodeCount() > 0) {
if (node.getParentNode() != null) {
builder.append(" path = [");
builder.append(node.getNodePath().toString());
builder.append("]");
}
builder.append(" {\n");
for (int i = 0; i < node.getChildNodeCount(); i++) {
dumpNode(builder, node.getChildNode(i), level + 1);
}
for (int i = 0; i < level; i++) {
builder.append(" ");
}
builder.append("}\n");
}
else {
if (node.getParentNode() != null) {
builder.append(" path = [");
builder.append(node.getNodePath().toString());
builder.append("]");
}
builder.append("\n");
}
}
/**
* @param path
* @return
*/
public Node getNodeByPath(final String path) {
NodePath nodePath = NodePath.valueOf(path);
final Integer[] pe = nodePath.getPathElements();
Node node = this;
for (final Integer element : pe) {
node = node.getChildNode(element);
}
return node;
}
/**
* @param key
* @param value
*/
public void putObject(final String key, final Object value) {
map.put(key, value);
}
/**
* @param key
* @return
*/
public Object getObject(final String key) {
return map.get(key);
}
/**
* @param key
*/
public void removeObject(final String key) {
map.remove(key);
}
/**
* @return
*/
public RootNode getRootNode() {
if (parentNode != null) {
return parentNode.getRootNode();
}
return null;
}
public String getExtensionId() {
return extensionId;
}
public void setExtensionId(String extensionId) {
this.extensionId = extensionId;
}
}