All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jfxcore.compiler.ast.ObjectNode Maven / Gradle / Ivy

// Copyright (c) 2022, 2024, JFXcore. All rights reserved.
// Use of this source code is governed by the BSD-3-Clause license that can be found in the LICENSE file.

package org.jfxcore.compiler.ast;

import org.jetbrains.annotations.Nullable;
import org.jfxcore.compiler.diagnostic.SourceInfo;
import org.jfxcore.compiler.ast.intrinsic.Intrinsic;
import org.jfxcore.compiler.ast.intrinsic.Intrinsics;
import org.jfxcore.compiler.ast.text.TextNode;
import org.jfxcore.compiler.diagnostic.errors.ObjectInitializationErrors;
import org.jfxcore.compiler.diagnostic.errors.PropertyAssignmentErrors;
import org.jfxcore.compiler.util.TypeHelper;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Represents an object entity.
 * In a FXML document, an object entity is represented by an upper-case element.
 */
public class ObjectNode extends AbstractNode implements ValueNode {

    private TypeNode type;
    private final List properties;
    private final List children;

    public ObjectNode(
            TypeNode type,
            Collection properties,
            Collection children,
            SourceInfo sourceInfo) {
        super(sourceInfo);
        this.type = checkNotNull(type);
        this.properties = new PropertyList();
        this.properties.addAll(checkNotNull(properties));
        this.children = new ArrayList<>(checkNotNull(children));
    }

    public boolean isIntrinsic(Intrinsic node) {
        return Intrinsics.find(this) == node;
    }

    public boolean isIntrinsic(Intrinsic node, Intrinsic... nodes) {
        Intrinsic intrinsic = Intrinsics.find(this);
        if (intrinsic == node) {
            return true;
        }

        for (Intrinsic n : nodes) {
            if (intrinsic == n) {
                return true;
            }
        }

        return false;
    }

    public TypeNode getType() {
        return type;
    }

    public List getProperties() {
        return properties;
    }

    public List getChildren() {
        return children;
    }

    public @Nullable PropertyNode findIntrinsicProperty(Intrinsic node) {
        for (PropertyNode property : properties) {
            if (property.isIntrinsic() && node.getName().equals(property.getName())) {
                return property;
            }
        }

        return null;
    }

    public @Nullable PropertyNode findProperty(String name) {
        for (PropertyNode property : properties) {
            if (!property.isIntrinsic() && name.equals(property.getName())) {
                return property;
            }
        }

        return null;
    }

    public PropertyNode getProperty(String name) {
        PropertyNode propertyNode = findProperty(name);

        if (propertyNode == null) {
            throw PropertyAssignmentErrors.propertyMustBeSpecified(
                getSourceInfo(), getType().getMarkupName(), name);
        }

        return propertyNode;
    }

    public TextNode getTextContent() {
        if (children.size() > 1) {
            throw ObjectInitializationErrors.objectCannotHaveMultipleChildren(
                SourceInfo.span(children.get(0).getSourceInfo(), children.get(children.size() - 1).getSourceInfo()),
                TypeHelper.getJvmType(this));
        }

        if (children.size() == 0 || !(children.get(0) instanceof TextNode)) {
            throw ObjectInitializationErrors.objectMustContainText(
                children.size() == 0 ? getSourceInfo() : children.get(0).getSourceInfo(),
                TypeHelper.getJvmType(this));
        }

        return (TextNode)children.get(0);
    }

    @Override
    public void acceptChildren(Visitor visitor) {
        super.acceptChildren(visitor);
        type = (TypeNode)type.accept(visitor);
        acceptChildren(properties, visitor, PropertyNode.class);
        acceptChildren(children, visitor, Node.class);

        // ObjectToPropertyTransform may have converted children to properties.
        // We need to fix this by moving all PropertyNodes to the properties list.
        var it = children.listIterator();
        while (it.hasNext()) {
            Node child = it.next();
            if (child instanceof PropertyNode propertyNode) {
                properties.add(propertyNode);
                it.remove();
            }
        }
    }

    @Override
    public String toString() {
        return type.toString();
    }

    @Override
    public ObjectNode deepClone() {
        return new ObjectNode(
            type.deepClone(),
            deepClone(properties),
            deepClone(children),
            getSourceInfo());
    }

    private class PropertyList extends AbstractList {
        private final List list = new ArrayList<>();
        private final Set names = new HashSet<>();

        @Override
        public PropertyNode get(int index) {
            return list.get(index);
        }

        @Override
        public int size() {
            return list.size();
        }

        @Override
        public PropertyNode set(int index, PropertyNode element) {
            names.remove(list.get(index).getMarkupName());

            if (names.contains(element.getMarkupName())) {
                throw PropertyAssignmentErrors.duplicateProperty(
                    element.getSourceInfo(), ObjectNode.this.getType().getMarkupName(), element.getMarkupName());
            }

            names.add(element.getMarkupName());
            return list.set(index, element);
        }

        @Override
        public void add(int index, PropertyNode element) {
            if (names.contains(element.getMarkupName())) {
                throw PropertyAssignmentErrors.duplicateProperty(
                    element.getSourceInfo(), ObjectNode.this.getType().getMarkupName(), element.getMarkupName());
            }

            names.add(element.getMarkupName());
            list.add(index, element);
        }

        @Override
        public PropertyNode remove(int index) {
            names.remove(list.get(index).getMarkupName());
            return list.remove(index);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy