Package nodebox.node

Source Code of nodebox.node.NodeLibrary

package nodebox.node;

import com.google.common.base.Objects;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import nodebox.function.FunctionLibrary;
import nodebox.function.FunctionRepository;
import nodebox.graphics.Point;
import nodebox.graphics.Rect;
import nodebox.util.FileUtils;
import nodebox.util.LoadException;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.google.common.base.Preconditions.*;

public class NodeLibrary {

    private static final Pattern NUMBER_AT_THE_END = Pattern.compile("^(.*?)(\\d*)$");

    public static final String CURRENT_FORMAT_VERSION = "20";

    public static final Splitter PORT_NAME_SPLITTER = Splitter.on(".");

    public static final NodeLibrary coreLibrary = NodeLibrary.load(new File("libraries/core/core.ndbx"), NodeRepository.empty());

    public static NodeLibrary create(String libraryName, Node root) {
        return create(libraryName, root, NodeRepository.of(), FunctionRepository.of(), UUID.randomUUID());
    }

    public static NodeLibrary create(String libraryName, Node root, FunctionRepository functionRepository) {
        return create(libraryName, root, NodeRepository.of(), functionRepository);
    }

    public static NodeLibrary create(String libraryName, Node root, NodeRepository nodeRepository, FunctionRepository functionRepository) {
        return create(libraryName, root, nodeRepository, functionRepository, UUID.randomUUID());
    }

    private static NodeLibrary create(String libraryName, Node root, NodeRepository nodeRepository, FunctionRepository functionRepository, UUID uuid) {
        return new NodeLibrary(libraryName, null, root, nodeRepository, functionRepository, ImmutableMap.<String, String>of(), ImmutableList.<Device>of(), uuid);
    }

    public static NodeLibrary load(String libraryName, String xml, NodeRepository nodeRepository) throws LoadException {
        checkNotNull(libraryName, "Library name cannot be null.");
        checkNotNull(xml, "XML string cannot be null.");
        try {
            return load(libraryName, null, new StringReader(xml), nodeRepository);
        } catch (XMLStreamException e) {
            throw new LoadException(null, "Could not read NDBX string", e);
        }
    }

    public static NodeLibrary load(String libraryName, String xml, File baseFile, NodeRepository nodeRepository) throws LoadException {
        checkNotNull(libraryName, "Library name cannot be null.");
        checkNotNull(xml, "XML string cannot be null.");
        try {
            return load(libraryName, baseFile, new StringReader(xml), nodeRepository);
        } catch (XMLStreamException e) {
            throw new LoadException(null, "Could not read NDBX string", e);
        }
    }

    public static NodeLibrary load(File f, NodeRepository nodeRepository) throws LoadException {
        checkNotNull(f, "File cannot be null.");
        String libraryName = FileUtils.stripExtension(f);
        try {
            return load(libraryName, f, createFileReader(f), nodeRepository);
        } catch (FileNotFoundException e) {
            throw new LoadException(f, "File not found.");
        } catch (XMLStreamException e) {
            throw new LoadException(f, "Could not read NDBX file", e);
        }
    }

    public static Map<String, String> parseHeader(File f) {
        try {
            XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
            XMLStreamReader reader = xmlInputFactory.createXMLStreamReader(createFileReader(f));
            while (reader.hasNext()) {
                int eventType = reader.next();
                if (eventType == XMLStreamConstants.START_ELEMENT) {
                    String tagName = reader.getLocalName();
                    if (tagName.equals("ndbx")) {
                        return parseHeader(reader);
                    } else {
                        throw new XMLStreamException("Only tag ndbx allowed, not " + tagName, reader.getLocation());
                    }
                }
            }
        } catch (XMLStreamException e) {
            throw new RuntimeException(e);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
        return Collections.emptyMap();
    }

    private static Map<String, String> parseHeader(XMLStreamReader reader) throws XMLStreamException {
        Map<String, String> propertyMap = new HashMap<String, String>();
        while (true) {
            int eventType = reader.next();
            if (eventType == XMLStreamConstants.START_ELEMENT) {
                String tagName = reader.getLocalName();
                if (tagName.equals("property")) {
                    parseProperty(reader, propertyMap);
                }
            } else if (eventType == XMLStreamConstants.END_ELEMENT) {
                String tagName = reader.getLocalName();
                if (tagName.equals("ndbx"))
                    break;
            }
        }
        return propertyMap;
    }

    /**
     * Create a file reader using the UTF-8 encoding.
     * <p/>
     * Unfortunately, Java's FileReader constructor does not accept an encoding, which is an oversight in the API.
     * Instead, it opts to create a reader with the default platform encoding. This means it differs between platforms,
     * and even inside and outside of the IDE.
     * <p/>
     * This method removes the ambiguity and always reads files in UTF-8.
     *
     * @param file the file to read.
     * @return A Reader.
     */
    private static Reader createFileReader(File file) throws FileNotFoundException {
        try {
            return new InputStreamReader(new FileInputStream(file), "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    private final String name;
    private final File file;
    private final Node root;
    private final NodeRepository nodeRepository;
    private final FunctionRepository functionRepository;
    private final ImmutableMap<String, String> properties;
    private final ImmutableList<Device> devices;
    private final UUID uuid;

    private NodeLibrary(String name, File file, Node root, NodeRepository nodeRepository, FunctionRepository functionRepository, Map<String, String> properties, List<Device> devices, UUID uuid) {
        checkNotNull(name, "Name cannot be null.");
        checkNotNull(root, "Root node cannot be null.");
        checkNotNull(functionRepository, "Function repository cannot be null.");
        this.name = name;
        this.root = root;
        this.nodeRepository = nodeRepository;
        this.functionRepository = functionRepository;
        this.file = file;
        this.properties = ImmutableMap.copyOf(properties);
        this.devices = ImmutableList.copyOf(devices);
        this.uuid = uuid;
    }

    public String getName() {
        return name;
    }

    public File getFile() {
        return file;
    }

    public UUID getUuid() {
        return uuid;
    }

    public Node getRoot() {
        return root;
    }

    public Node getNodeForPath(String path) {
        checkArgument(path.startsWith("/"), "Only absolute paths are supported.");
        if (path.length() == 1) return root;

        Node node = root;
        path = path.substring(1);
        for (String name : Splitter.on("/").split(path)) {
            node = node.getChild(name);
            if (node == null) return null;
        }
        return node;
    }

    public NodeRepository getNodeRepository() {
        return nodeRepository;
    }

    public FunctionRepository getFunctionRepository() {
        return functionRepository;
    }

    //// Properties ////

    public boolean hasProperty(String name) {
        return properties.containsKey(name);
    }

    public String getProperty(String name) {
        return properties.get(name);
    }

    public Set<String> getPropertyNames() {
        return properties.keySet();
    }

    public String getProperty(String name, String defaultValue) {
        if (hasProperty(name)) {
            return properties.get(name);
        } else {
            return defaultValue;
        }
    }

    public int getIntProperty(String name, int defaultValue) {
        if (hasProperty(name)) {
            try {
                return Integer.parseInt(properties.get(name));
            } catch (NumberFormatException e) {
                return defaultValue;
            }
        } else {
            return defaultValue;
        }
    }

    public Map<String, String> getProperties() {
        return properties;
    }

    public Rect getBounds() {
        return Rect.centeredRect(getIntProperty("canvasX", 0),
                getIntProperty("canvasY", 0),
                getIntProperty("canvasWidth", 0),
                getIntProperty("canvasHeight", 0));
    }

    public boolean isValidPropertyName(String name) {
        checkNotNull(name);
        // no whitespace, only lowercase, numbers + period.
        return true;
    }

    public NodeLibrary withProperty(String name, String value) {
        ImmutableMap.Builder<String, String> b = ImmutableMap.builder();
        checkArgument(isValidPropertyName(name), "Property name '%s' is not valid.", name);
        b.putAll(properties);
        b.put(name, value);
        return withProperties(b.build());
    }

    public NodeLibrary withPropertyRemoved(String name) {
        if (!hasProperty(name)) return this;
        ImmutableMap.Builder<String, String> b = ImmutableMap.builder();
        for (Map.Entry<String, String> entry : this.properties.entrySet()) {
            if (!entry.getKey().equals(name)) {
                b.put(entry);
            }
        }
        return withProperties(b.build());
    }

    public NodeLibrary withProperties(Map<String, String> properties) {
        return new NodeLibrary(this.name, this.file, this.root, this.nodeRepository, this.functionRepository, ImmutableMap.copyOf(properties), this.devices, this.uuid);
    }


    public ImmutableList<Device> getDevices() {
        return devices;
    }

    public boolean hasDevice(String name) {
        for (Device device : devices) {
            if (device.getName().equals(name))
                return true;
        }
        return false;
    }

    public Device getDevice(String name) {
        for (Device device : devices) {
            if (device.getName().equals(name))
                return device;
        }
        return null;
    }

    public NodeLibrary withDeviceAdded(Device device) {
        checkNotNull(device, "Device cannot be null.");
        checkArgument(!hasDevice(device.getName()), "There is already a device named %s", device.getName());
        ImmutableList.Builder<Device> b = ImmutableList.builder();
        b.addAll(getDevices());
        b.add(device);
        return new NodeLibrary(this.name, this.file, this.root, this.nodeRepository, this.functionRepository, this.properties, b.build(), this.uuid);
    }

    public NodeLibrary withDeviceRemoved(Device device) {
        return withDeviceRemoved(device.getName());
    }

    public String uniqueName(String prefix) {
        Matcher m = NUMBER_AT_THE_END.matcher(prefix);
        m.find();
        String namePrefix = m.group(1);
        String number = m.group(2);
        int counter;
        if (number.length() > 0) {
            counter = Integer.parseInt(number);
        } else {
            counter = 1;
        }
        while (true) {
            String suggestedName = namePrefix + counter;
            if (!hasDevice(suggestedName)) {
                return suggestedName;
            }
            ++counter;
        }
    }

    public NodeLibrary withDeviceRemoved(String name) {
        ImmutableList.Builder<Device> b = ImmutableList.builder();
        for (Device device : getDevices()) {
            if (!device.getName().equals(name))
                b.add(device);
        }
        return new NodeLibrary(this.name, this.file, this.root, this.nodeRepository, this.functionRepository, this.properties, b.build(), this.uuid);
    }

    public NodeLibrary withDevicePropertyChanged(String deviceName, String propertyName, String propertyValue) {
        checkArgument(hasDevice(deviceName), "No device %s present.");
        Device newDevice = getDevice(deviceName).withProperty(propertyName, propertyValue);
        ImmutableList.Builder<Device> b = ImmutableList.builder();
        for (Device device : getDevices()) {
            if (device.getName().equals(deviceName))
                b.add(newDevice);
            else
                b.add(device);
        }
        return new NodeLibrary(this.name, this.file, this.root, this.nodeRepository, this.functionRepository, this.properties, b.build(), this.uuid);
    }

    //// Loading ////

    private static NodeLibrary load(String libraryName, File file, Reader r, NodeRepository nodeRepository) throws XMLStreamException {
        XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
        XMLStreamReader reader = xmlInputFactory.createXMLStreamReader(r);
        NodeLibrary nodeLibrary = null;
        while (reader.hasNext()) {
            int eventType = reader.next();
            if (eventType == XMLStreamConstants.START_ELEMENT) {
                String tagName = reader.getLocalName();
                if (tagName.equals("ndbx")) {
                    String formatVersion = reader.getAttributeValue(null, "formatVersion");
                    if (formatVersion != null && !CURRENT_FORMAT_VERSION.equals(formatVersion)) {
                        throw new OutdatedLibraryException(file, "File uses version " + formatVersion + ", current version is " + CURRENT_FORMAT_VERSION + ".");
                    }
                    String uuidString = reader.getAttributeValue(null, "uuid");
                    UUID uuid = (uuidString == null) ? UUID.randomUUID() : UUID.fromString(uuidString);
                    nodeLibrary = parseNDBX(libraryName, file, reader, nodeRepository, uuid);
                } else {
                    throw new XMLStreamException("Only tag ndbx allowed, not " + tagName, reader.getLocation());
                }
            }
        }
        return nodeLibrary;
    }

    private static NodeLibrary parseNDBX(String libraryName, File file, XMLStreamReader reader, NodeRepository nodeRepository, UUID uuid) throws XMLStreamException {
        List<FunctionLibrary> functionLibraries = new LinkedList<FunctionLibrary>();
        Map<String, String> propertyMap = new HashMap<String, String>();
        Node rootNode = Node.ROOT;
        List<Device> devices = new LinkedList<Device>();

        while (true) {
            int eventType = reader.next();
            if (eventType == XMLStreamConstants.START_ELEMENT) {
                String tagName = reader.getLocalName();
                if (tagName.equals("property")) {
                    parseProperty(reader, propertyMap);
                } else if (tagName.equals("link")) {
                    FunctionLibrary functionLibrary = parseLink(file, reader);
                    functionLibraries.add(functionLibrary);
                } else if (tagName.equals("device")) {
                    Device device = parseDevice(reader);
                    devices.add(device);
                } else if (tagName.equals("node")) {
                    rootNode = parseNode(reader, rootNode, nodeRepository);
                } else {
                    throw new XMLStreamException("Unknown tag " + tagName, reader.getLocation());
                }
            } else if (eventType == XMLStreamConstants.END_ELEMENT) {
                String tagName = reader.getLocalName();
                if (tagName.equals("ndbx"))
                    break;
            }
        }
        FunctionLibrary[] fl = functionLibraries.toArray(new FunctionLibrary[functionLibraries.size()]);
        return new NodeLibrary(libraryName, file, rootNode, nodeRepository, FunctionRepository.of(fl), propertyMap, devices, uuid);
    }

    private static FunctionLibrary parseLink(File file, XMLStreamReader reader) throws XMLStreamException {
        String linkRelation = reader.getAttributeValue(null, "rel");
        checkState(linkRelation.equals("functions"));
        String ref = reader.getAttributeValue(null, "href");
        // loading should happen lazily?
        return FunctionLibrary.load(file, ref);
    }

    /**
     * Parse the <property> tag and add the result to the propertyMap.
     */
    private static void parseProperty(XMLStreamReader reader, Map<String, String> propertyMap) throws XMLStreamException {
        String name = reader.getAttributeValue(null, "name");
        String value = reader.getAttributeValue(null, "value");
        if (name == null || value == null) return;
        propertyMap.put(name, value);
    }

    /**
     * Parse the external devices.
     */
    private static Device parseDevice(XMLStreamReader reader) throws XMLStreamException {
        String name = reader.getAttributeValue(null, "name");
        String type = reader.getAttributeValue(null, "type");

        Device device = Device.deviceForType(name, type);

        while (true) {
            int eventType = reader.next();
            if (eventType == XMLStreamConstants.START_ELEMENT) {
                String tagName = reader.getLocalName();
                if (tagName.equals("property")) {
                    String propertyName = reader.getAttributeValue(null, "name");
                    String propertyValue = reader.getAttributeValue(null, "value");
                    if (propertyName == null || propertyValue == null) continue;
                    device = device.withProperty(propertyName, propertyValue);
                }
            } else if (eventType == XMLStreamConstants.END_ELEMENT) {
                String tagName = reader.getLocalName();
                if (tagName.equals("device"))
                    break;
            }
        }
        return device;
    }

    /**
     * Parse a specific node attribute value and add it to the attributeMap.
     */
    private static void parseNodeAttribute(XMLStreamReader reader, Map<String, String> attributeMap, String attribute) throws XMLStreamException {
        attributeMap.put(attribute, reader.getAttributeValue(null, attribute));
    }

    /**
     * Parse the <node> tag's attribute values.
     */
    private static Map<String, String> parseNodeAttributes(XMLStreamReader reader) throws XMLStreamException {
        Map<String, String> attributeMap = new HashMap<String, String>();
        String[] attributes = {"prototype", "name", "comment", "category", "description", "image", "function",
                "outputType", "outputRange", "position", "renderedChild", "handle", "alwaysRendered"};
        for (String attribute : attributes)
            parseNodeAttribute(reader, attributeMap, attribute);
        return attributeMap;
    }

    /**
     * @param attributeMap   The map containing node attributes.
     * @param extendFromNode The node from which to extend when there is no specified prototype.
     * @param parent         The parent node.
     * @param nodeRepository The node library dependencies.
     * @return A new node.
     */

    private static Node createNode(Map<String, String> attributeMap, Node extendFromNode, Node parent, NodeRepository nodeRepository) {
        String prototypeId = attributeMap.get("prototype");
        String name = attributeMap.get("name");
        String comment = attributeMap.get("comment");
        String category = attributeMap.get("category");
        String description = attributeMap.get("description");
        String image = attributeMap.get("image");
        String function = attributeMap.get("function");
        String outputType = attributeMap.get("outputType");
        String outputRange = attributeMap.get("outputRange");
        String position = attributeMap.get("position");
        String handle = attributeMap.get("handle");
        String alwaysRendered = attributeMap.get("alwaysRendered");

        Node prototype = prototypeId == null ? extendFromNode : lookupNode(prototypeId, parent, nodeRepository);
        if (prototype == null) return null;
        Node node = prototype.extend();

        if (name != null)
            node = node.withName(name);
        if (comment != null)
            node = node.withComment(comment);
        if (category != null)
            node = node.withCategory(category);
        if (description != null)
            node = node.withDescription(description);
        if (image != null)
            node = node.withImage(image);
        if (function != null)
            node = node.withFunction(function);
        if (outputType != null)
            node = node.withOutputType(outputType);
        if (outputRange != null)
            node = node.withOutputRange(Port.Range.valueOf(outputRange.toUpperCase()));
        if (position != null)
            node = node.withPosition(Point.valueOf(position));
        if (handle != null)
            node = node.withHandle(handle);
        if (alwaysRendered != null)
            node = node.withAlwaysRenderedSet(Boolean.parseBoolean(alwaysRendered));
        return node;
    }

    /**
     * Parse the <node> tag.
     *
     * @param reader         The XML stream.
     * @param parent         The parent node.
     * @param nodeRepository The node library dependencies.
     * @return The new node.
     * @throws XMLStreamException if a parse error occurs.
     */
    private static Node parseNode(XMLStreamReader reader, Node parent, NodeRepository nodeRepository) throws XMLStreamException {
        Map<String, String> attributeMap = parseNodeAttributes(reader);
        Node node = createNode(attributeMap, Node.ROOT, parent, nodeRepository);
        String prototypeId = attributeMap.get("prototype");
        if (node == null) {
            throw new XMLStreamException("Prototype " + prototypeId + " could not be found.", reader.getLocation());
        }

        while (true) {
            int eventType = reader.next();
            if (eventType == XMLStreamConstants.START_ELEMENT) {
                String tagName = reader.getLocalName();

                if (tagName.equals("node") || tagName.equals("importCoreNode")) {
                    if (prototypeId == null && !node.isNetwork())
                        node = createNode(attributeMap, Node.NETWORK, parent, nodeRepository);
                }

                if (tagName.equals("node")) {
                    node = node.withChildAdded(parseNode(reader, node, nodeRepository));
                } else if (tagName.equals("importCoreNode")) {
                    String s = reader.getAttributeValue(null, "ref");
                    Node coreNode = Node.coreNodes.get(s);
                    if (coreNode == null) {
                        throw new XMLStreamException("Core node '" + s + "' does not exist.", reader.getLocation());
                    }
                    node = node.withChildAdded(coreNode);
                } else if (tagName.equals("port")) {
                    String portName = reader.getAttributeValue(null, "name");
                    // Remove the port if it is already on the prototype.
                    if (node.hasInput(portName)) {
                        node = node.withInputChanged(portName, parsePort(reader, node.getInput(portName)));
                    } else {
                        node = node.withInputAdded(parsePort(reader, null));
                    }
                } else if (tagName.equals("conn")) {
                    node = node.withConnectionAdded(parseConnection(reader));
                } else {
                    throw new XMLStreamException("Unknown tag " + tagName, reader.getLocation());
                }
            } else if (eventType == XMLStreamConstants.END_ELEMENT) {
                String tagName = reader.getLocalName();
                if (tagName.equals("node"))
                    break;
            }
        }

        // This has to come at the end, since the child first needs to exist.
        String renderedChildName = attributeMap.get("renderedChild");
        if (renderedChildName != null)
            node = node.withRenderedChildName(renderedChildName);

        return node;
    }

    /**
     * Lookup the node in the node repository.
     * <p/>
     * If the node id consists of just a node name, without spaces, it is looked up in the parent node.
     *
     * @param nodeId         The node id.
     * @param parent         The parent node.
     * @param nodeRepository The node repository.
     * @return The existing node.
     */
    private static Node lookupNode(String nodeId, Node parent, NodeRepository nodeRepository) {
        if (nodeId.contains(".")) {
            return nodeRepository.getNode(nodeId);
        } else {
            return parent.getChild(nodeId);
        }
    }

    private static Port parsePort(XMLStreamReader reader, Port prototype) throws XMLStreamException {
        // Name and type are always required.
        String name = reader.getAttributeValue(null, "name");
        String type = reader.getAttributeValue(null, "type");
        String label = reader.getAttributeValue(null, "label");
        String childReference = reader.getAttributeValue(null, "childReference");
        String widget = reader.getAttributeValue(null, "widget");
        String range = reader.getAttributeValue(null, "range");
        String value = reader.getAttributeValue(null, "value");
        String description = reader.getAttributeValue(null, "description");
        String min = reader.getAttributeValue(null, "min");
        String max = reader.getAttributeValue(null, "max");

        Port port;
        if (prototype == null) {
            port = Port.portForType(name, type);
        } else {
            port = prototype;
        }

        // Widget, value, min, max are optional and could come from the prototype.
        if (label != null)
            port = port.withParsedAttribute(Port.Attribute.LABEL, label);
        if (childReference != null)
            port = port.withParsedAttribute(Port.Attribute.CHILD_REFERENCE, childReference);
        if (widget != null)
            port = port.withParsedAttribute(Port.Attribute.WIDGET, widget);
        if (range != null)
            port = port.withParsedAttribute(Port.Attribute.RANGE, range);
        if (value != null)
            port = port.withParsedAttribute(Port.Attribute.VALUE, value);
        if (description != null)
            port = port.withParsedAttribute(Port.Attribute.DESCRIPTION, description);
        if (min != null)
            port = port.withParsedAttribute(Port.Attribute.MINIMUM_VALUE, min);
        if (max != null)
            port = port.withParsedAttribute(Port.Attribute.MAXIMUM_VALUE, max);

        ImmutableList.Builder<MenuItem> b = ImmutableList.builder();

        while (true) {
            int eventType = reader.next();
            if (eventType == XMLStreamConstants.START_ELEMENT) {
                String tagName = reader.getLocalName();
                if (tagName.equals("menu")) {
                    b.add(parseMenuItem(reader));
                } else {
                    throw new XMLStreamException("Unknown tag " + tagName, reader.getLocation());
                }
            } else if (eventType == XMLStreamConstants.END_ELEMENT) {
                String tagName = reader.getLocalName();
                if (tagName.equals("port"))
                    break;
            }
        }
        ImmutableList<MenuItem> items = b.build();
        if (!items.isEmpty())
            port = port.withMenuItems(items);
        return port;
    }

    private static MenuItem parseMenuItem(XMLStreamReader reader) throws XMLStreamException {
        String key = reader.getAttributeValue(null, "key");
        String label = reader.getAttributeValue(null, "label");
        if (key == null)
            throw new XMLStreamException("Menu item key cannot be null.", reader.getLocation());
        return new MenuItem(key, label != null ? label : key);
    }

    private static Connection parseConnection(XMLStreamReader reader) throws XMLStreamException {
        String outputNode = reader.getAttributeValue(null, "output");
        String input = reader.getAttributeValue(null, "input");
        Iterator<String> inputIterator = PORT_NAME_SPLITTER.split(input).iterator();
        String inputNode = inputIterator.next();
        String inputPort = inputIterator.next();
        return new Connection(outputNode, inputNode, inputPort);
    }

    ///// Mutation methods ////

    public NodeLibrary withRoot(Node newRoot) {
        return new NodeLibrary(this.name, this.file, newRoot, this.nodeRepository, this.functionRepository, this.properties, this.devices, this.uuid);
    }

    public NodeLibrary withFunctionRepository(FunctionRepository newRepository) {
        return new NodeLibrary(this.name, this.file, this.root, this.nodeRepository, newRepository, this.properties, this.devices, this.uuid);
    }

    public NodeLibrary withFile(File newFile) {
        return new NodeLibrary(this.name, newFile, this.root, this.nodeRepository, this.functionRepository, this.properties, this.devices, this.uuid);
    }

    //// Saving ////

    public String toXml() {
        return NDBXWriter.asString(this);
    }

    /**
     * Write the NodeLibrary to a file.
     *
     * @param file The file to save.
     * @throws java.io.IOException When file saving fails.
     */
    public void store(File file) throws IOException {
        NDBXWriter.write(this, file);
    }

    //// Object overrides ////

    @Override
    public int hashCode() {
        return Objects.hashCode(name, root, functionRepository);
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof NodeLibrary)) return false;
        final NodeLibrary other = (NodeLibrary) o;
        return Objects.equal(name, other.name)
                && Objects.equal(root, other.root)
                && Objects.equal(functionRepository, other.functionRepository);
    }

    @Override
    public String toString() {
        return String.format("<NodeLibrary %s>", name);
    }

}
TOP

Related Classes of nodebox.node.NodeLibrary

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.