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

org.xmlet.xsdparser.xsdelements.XsdElement Maven / Gradle / Ivy

package org.xmlet.xsdparser.xsdelements;

import org.w3c.dom.Node;
import org.xmlet.xsdparser.core.XsdParserCore;
import org.xmlet.xsdparser.xsdelements.elementswrapper.ConcreteElement;
import org.xmlet.xsdparser.xsdelements.elementswrapper.NamedConcreteElement;
import org.xmlet.xsdparser.xsdelements.elementswrapper.ReferenceBase;
import org.xmlet.xsdparser.xsdelements.elementswrapper.UnsolvedReference;
import org.xmlet.xsdparser.xsdelements.enums.BlockEnum;
import org.xmlet.xsdparser.xsdelements.enums.FinalEnum;
import org.xmlet.xsdparser.xsdelements.enums.FormEnum;
import org.xmlet.xsdparser.xsdelements.exceptions.ParsingException;
import org.xmlet.xsdparser.xsdelements.visitors.XsdAbstractElementVisitor;
import org.xmlet.xsdparser.xsdelements.visitors.XsdAnnotatedElementsVisitor;
import org.xmlet.xsdparser.xsdelements.visitors.XsdElementVisitor;

import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;

/**
 * A class representing the xsd:element element. Extends {@link XsdNamedElements} because it's one of the
 * {@link XsdAbstractElement} concrete classes that can have a {@link XsdNamedElements#name} attribute.
 *
 * @see xsd:element description and usage at w3c
 */
public class XsdElement extends XsdNamedElements {

    public static final String XSD_TAG = "xsd:element";
    public static final String XS_TAG = "xs:element";

    /**
     * {@link XsdElementVisitor} which restricts its children to {@link XsdComplexType} and {@link XsdSimpleType}
     * instances.
     * Can also have {@link XsdAnnotation} as children as per inheritance of {@link XsdAnnotatedElementsVisitor}.
     */
    private XsdElementVisitor visitor = new XsdElementVisitor(this);

    /**
     * The {@link XsdComplexType} instance wrapped in a {@link ReferenceBase} object.
     */
    private ReferenceBase complexType;

    /**
     * The {@link XsdSimpleType} instance wrapped in a {@link ReferenceBase} object.
     */
    private ReferenceBase simpleType;

    /**
     * The type of the current element.
     * Either specified a built in data type or is a reference to a existent {@link XsdComplexType} or a
     * {@link XsdSimpleType} instances.
     */
    private ReferenceBase type;

    /**
     * Specifies the name of an element that can be substituted with this element. Only should be present if this
     * {@link XsdElement} is a top level element, i.e. his parent is a XsdSchema element.
     */
    private ReferenceBase substitutionGroup;

    /**
     * Specifies a default value for the element. It's only available if the type contents are text only type defined by
     * a simpleType.
     */
    private String defaultObj;

    /**
     * Specifies a fixed value for the element. It's only available if the type contents are text only type defined by
     * a simpleType.
     */
    private String fixed;

    /**
     * Specifies if the current {@link XsdElement} attribute is "qualified" or "unqualified".
     */
    private FormEnum form;

    /**
     * Specifies if the this {@link XsdElement} support a null value.
     */
    private boolean nillable;

    /**
     * Specifies whether the element can be used in an instance document.
     */
    private boolean abstractObj;

    /**
     * Prevents an element with a specified type of derivation from being used in place of this {@link XsdElement} element.
     * Possible values are:
        * extension - prevents elements derived by extension;
        * restriction - prevents elements derived by restriction;
        * substitution - prevents elements derived by substitution;
        * #all - all of the above.
     */
    private BlockEnum block;

    /**
     * Prevents other elements to derive depending on its value. This attribute cannot be present unless this
     * {@link XsdElement} is a top level element, i.e. his parent is a XsdSchema element.
         * extension - prevents elements derived by extension;
         * restriction - prevents elements derived by restriction;
         * #all - all of the above.
     */
    private FinalEnum finalObj;

    /**
     * Specifies the minimum number of times this element can occur in the parent element. The value can be any
     * number bigger or equal to 0. Default value is 1. This attribute cannot be used if the parent element is the
     * XsdSchema element.
     */
    private Integer minOccurs;

    /**
     * Specifies the maximum number of times this element can occur in the parent element. The value can be any
     * number bigger or equal to 0, or if you want to set no limit on the maximum number, use the value "unbounded".
     * Default value is 1. This attribute cannot be used if the parent element is the XsdSchema element.
     */
    private String maxOccurs;

    public XsdElement(@NotNull XsdParserCore parser, @NotNull Map attributesMap) {
        super(parser, attributesMap);

        String typeString = attributesMap.get(TYPE_TAG);

        if (typeString != null){
            if (XsdParserCore.getXsdTypesToJava().containsKey(typeString)){
                HashMap attributes = new HashMap<>();
                attributes.put(NAME_TAG, typeString);
                this.type = ReferenceBase.createFromXsd(new XsdComplexType(this, this.parser, attributes));
            } else {
                this.type = new UnsolvedReference(typeString, new XsdElement(this, this.parser, new HashMap<>()));
                parser.addUnsolvedReference((UnsolvedReference) this.type);
            }
        }

        String formDefault = AttributeValidations.getFormDefaultValue(parent);
        String blockDefault = AttributeValidations.getBlockDefaultValue(parent);
        String finalDefault = AttributeValidations.getFinalDefaultValue(parent);
        String substitutionGroup = attributesMap.getOrDefault(SUBSTITUTION_GROUP_TAG, null);

        if (substitutionGroup != null){
            this.substitutionGroup = new UnsolvedReference(substitutionGroup, new XsdElement(this, this.parser, new HashMap<>()));
            parser.addUnsolvedReference((UnsolvedReference) this.substitutionGroup);
        }

        this.defaultObj = attributesMap.getOrDefault(DEFAULT_TAG, defaultObj);
        this.fixed = attributesMap.getOrDefault(FIXED_TAG, fixed);
        this.form = AttributeValidations.belongsToEnum(FormEnum.QUALIFIED, attributesMap.getOrDefault(FORM_TAG, formDefault));
        this.nillable = AttributeValidations.validateBoolean(attributesMap.getOrDefault(NILLABLE_TAG, "false"));
        this.abstractObj = AttributeValidations.validateBoolean(attributesMap.getOrDefault(ABSTRACT_TAG, "false"));
        this.block = AttributeValidations.belongsToEnum(BlockEnum.ALL, attributesMap.getOrDefault(BLOCK_TAG, blockDefault));
        this.finalObj = AttributeValidations.belongsToEnum(FinalEnum.ALL, attributesMap.getOrDefault(FINAL_TAG, finalDefault));
        this.minOccurs = AttributeValidations.validateNonNegativeInteger(XSD_TAG, MIN_OCCURS_TAG, attributesMap.getOrDefault(MIN_OCCURS_TAG, "1"));
        this.maxOccurs = AttributeValidations.maxOccursValidation(XSD_TAG, attributesMap.getOrDefault(MAX_OCCURS_TAG, "1"));
    }

    public XsdElement(XsdAbstractElement parent, @NotNull XsdParserCore parser, @NotNull Map elementFieldsMapParam) {
        this(parser, elementFieldsMapParam);
        setParent(parent);
    }

    /**
     * Runs verifications on each concrete element to ensure that the XSD schema rules are verified.
     */
    @Override
    public void validateSchemaRules() {
        super.validateSchemaRules();

        rule2();
        rule3();
        rule4();
        rule5();
        rule6();
        rule7();
    }

    private static String xsdElementIsXsdSchema = XSD_TAG + " is a " + XsdSchema.XSD_TAG + " element.";

    /**
     * Asserts if the current object has a form attribute while being a direct child of the top level XsdSchema element,
     * which isn't allowed, throwing an exception in that case.
     */
    private void rule7() {
        if (parent instanceof XsdSchema && attributesMap.containsKey(FORM_TAG)){
            throw new ParsingException(XSD_TAG + " element: The " + FORM_TAG + " attribute can only be present when the parent of the " + xsdElementIsXsdSchema);
        }
    }

    private void rule6() {
        //fixed 	Optional. Specifies a fixed value for the element (can only be used if the element's content is a simple type or text only)
    }

    private void rule5() {
        // default 	Optional. Specifies a default value for the element (can only be used if the element's content is a simple type or text only)
    }

    /**
     * Asserts if the current object isn't a direct child of the top level XsdSchema and has a value for the substitutionGroup,
     * which isn't allowed, throwing an exception in that case.
     */
    private void rule4() {
        if (!(parent instanceof XsdSchema) && substitutionGroup != null){
            throw new ParsingException(XSD_TAG + " element: The " + SUBSTITUTION_GROUP_TAG + " attribute can only be present when the parent of the " + xsdElementIsXsdSchema);
        }
    }

    /**
     * Asserts if the current object has a ref attribute while being a direct child of the top level XsdSchema element, which isn't allowed,
     * throwing an exception in that case.
     */
    private void rule3() {
        if (parent instanceof XsdSchema && attributesMap.containsKey(REF_TAG)){
            throw new ParsingException(XSD_TAG + " element: The " + REF_TAG + " attribute cannot be present when the parent of the " + xsdElementIsXsdSchema);
        }
    }

    /**
     * Asserts if the current object is a direct child of the top level XsdSchema element and doesn't have a name, which isn't allowed,
     * throwing an exception in that case.
     */
    private void rule2() {
        if (parent instanceof XsdSchema && name == null){
            throw new ParsingException(XSD_TAG + " element: The " + NAME_TAG + " attribute is required when the parent of the " + xsdElementIsXsdSchema);
        }
    }

    @Override
    public void accept(XsdAbstractElementVisitor visitorParam) {
        super.accept(visitorParam);
        visitorParam.visit(this);
    }

    @Override
    public XsdElementVisitor getVisitor() {
        return visitor;
    }

    /**
     * Performs a copy of the current object for replacing purposes. The cloned objects are used to replace
     * {@link UnsolvedReference} objects in the reference solving process.
     * @param placeHolderAttributes The additional attributes to add to the clone.
     * @return A copy of the object from which is called upon.
     */
    @Override
    public XsdElement clone(@NotNull Map placeHolderAttributes) {
        placeHolderAttributes.putAll(attributesMap);
        placeHolderAttributes.remove(TYPE_TAG);
        placeHolderAttributes.remove(REF_TAG);

        XsdElement elementCopy = new XsdElement(this.parent, this.parser, placeHolderAttributes);

        elementCopy.simpleType = this.simpleType;
        elementCopy.complexType = this.complexType;
        elementCopy.type = this.type;

        return elementCopy;
    }

    /**
     * This method aims to replace the previously created {@link UnsolvedReference} in case that the type of the
     * current {@link XsdElement} instance is not a built-in type.
     * @param element A concrete element with a name that will replace the {@link UnsolvedReference} object created in the
     *                {@link XsdElement} constructor. The {@link UnsolvedReference} is only replaced if there
     *                is a match between the {@link UnsolvedReference#ref} and the {@link NamedConcreteElement#name}.
     */
    @Override
    public void replaceUnsolvedElements(NamedConcreteElement element) {
        super.replaceUnsolvedElements(element);

        XsdNamedElements elem = element.getElement();

        boolean isComplexOrSimpleType = elem instanceof XsdComplexType || elem instanceof XsdSimpleType;

        if (this.type instanceof UnsolvedReference && isComplexOrSimpleType && compareReference(element, (UnsolvedReference) this.type)){
            this.type = element;
            elem.setParent(this);
        }

        if (this.substitutionGroup instanceof UnsolvedReference && elem instanceof XsdElement && compareReference(element, (UnsolvedReference) this.substitutionGroup)){
            XsdElement xsdElement = (XsdElement) elem;

            this.type = xsdElement.type;
            this.simpleType = xsdElement.simpleType;
            this.complexType = xsdElement.complexType;
            this.substitutionGroup = element;
            elem.setParent(this);
        }
    }

    public XsdComplexType getXsdComplexType() {
        return complexType == null ? getXsdType() : (XsdComplexType) complexType.getElement();
    }

    public XsdSimpleType getXsdSimpleType(){
        return simpleType instanceof ConcreteElement ? (XsdSimpleType) simpleType.getElement() : null;
    }

    private XsdComplexType getXsdType(){
        if (type instanceof ConcreteElement){
            return (XsdComplexType) type.getElement();
        }

        return null;
    }

    public static ReferenceBase parse(@NotNull XsdParserCore parser, Node node){
        return xsdParseSkeleton(node, new XsdElement(parser, convertNodeMap(node.getAttributes())));
    }

    public String getFinal() {
        return finalObj.getValue();
    }

    @SuppressWarnings("unused")
    public boolean isNillable() {
        return nillable;
    }

    @SuppressWarnings("unused")
    public Integer getMinOccurs() {
        return minOccurs;
    }

    @SuppressWarnings("unused")
    public String getMaxOccurs() {
        return maxOccurs;
    }

    @SuppressWarnings("unused")
    public boolean isAbstractObj() {
        return abstractObj;
    }

    public void setComplexType(ReferenceBase complexType) {
        this.complexType = complexType;
    }

    public void setSimpleType(ReferenceBase simpleType) {
        this.simpleType = simpleType;
    }

    @SuppressWarnings("unused")
    public String getBlock() {
        return block.getValue();
    }

    @SuppressWarnings("unused")
    public String getForm() {
        return form.getValue();
    }

    public String getType(){
        if (this.type != null && this.type instanceof NamedConcreteElement){
            return ((NamedConcreteElement) this.type).getName();
        }

        return attributesMap.getOrDefault(TYPE_TAG, null);
    }

    public ReferenceBase getSubstitutionGroup() {
        return substitutionGroup;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy