com.evolveum.midpoint.prism.ComplexTypeDefinitionImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.prism.ComplexTypeDefinitionImpl.java

Source

/*
 * Copyright (c) 2010-2016 Evolveum
 *
 * 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 com.evolveum.midpoint.prism;

import com.evolveum.midpoint.prism.path.IdItemPathSegment;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.path.ItemPathSegment;
import com.evolveum.midpoint.prism.path.NameItemPathSegment;
import com.evolveum.midpoint.prism.path.ObjectReferencePathSegment;
import com.evolveum.midpoint.prism.path.ParentPathSegment;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.PrettyPrinter;
import org.jetbrains.annotations.NotNull;
import com.evolveum.midpoint.util.QNameUtil;
import org.apache.commons.lang.StringUtils;

import java.util.*;

import javax.xml.namespace.QName;

/**
 * TODO
 * 
 * @author Radovan Semancik
 * 
 */
public class ComplexTypeDefinitionImpl extends TypeDefinitionImpl implements ComplexTypeDefinition {

    private static final long serialVersionUID = 2655797837209175037L;
    @NotNull
    private final List<ItemDefinition> itemDefinitions = new ArrayList<>();
    private boolean containerMarker;
    private boolean objectMarker;
    private boolean xsdAnyMarker;
    private boolean listMarker;
    private QName extensionForType;

    private String defaultNamespace;
    @NotNull
    private List<String> ignoredNamespaces = new ArrayList<>();

    public ComplexTypeDefinitionImpl(@NotNull QName typeName, @NotNull PrismContext prismContext) {
        super(typeName, prismContext);
    }

    //region Trivia
    protected String getSchemaNamespace() {
        return getTypeName().getNamespaceURI();
    }

    /**
     * Returns set of item definitions.
     * 
     * The set contains all item definitions of all types that were parsed.
     * Order of definitions is insignificant.
     * 
     * @return set of definitions
     */
    @NotNull
    @Override
    public List<? extends ItemDefinition> getDefinitions() {
        return Collections.unmodifiableList(itemDefinitions);
    }

    public void add(ItemDefinition<?> definition) {
        itemDefinitions.add(definition);
    }

    @Override
    public QName getExtensionForType() {
        return extensionForType;
    }

    public void setExtensionForType(QName extensionForType) {
        this.extensionForType = extensionForType;
    }

    @Override
    public boolean isContainerMarker() {
        return containerMarker;
    }

    public void setContainerMarker(boolean containerMarker) {
        this.containerMarker = containerMarker;
    }

    @Override
    public boolean isObjectMarker() {
        return objectMarker;
    }

    @Override
    public boolean isXsdAnyMarker() {
        return xsdAnyMarker;
    }

    public void setXsdAnyMarker(boolean xsdAnyMarker) {
        this.xsdAnyMarker = xsdAnyMarker;
    }

    public boolean isListMarker() {
        return listMarker;
    }

    public void setListMarker(boolean listMarker) {
        this.listMarker = listMarker;
    }

    @Override
    public String getDefaultNamespace() {
        return defaultNamespace;
    }

    public void setDefaultNamespace(String defaultNamespace) {
        this.defaultNamespace = defaultNamespace;
    }

    @Override
    @NotNull
    public List<String> getIgnoredNamespaces() {
        return ignoredNamespaces;
    }

    public void setIgnoredNamespaces(@NotNull List<String> ignoredNamespaces) {
        this.ignoredNamespaces = ignoredNamespaces;
    }

    public void setObjectMarker(boolean objectMarker) {
        this.objectMarker = objectMarker;
    }

    //endregion

    //region Creating definitions
    public PrismPropertyDefinitionImpl createPropertyDefinition(QName name, QName typeName) {
        PrismPropertyDefinitionImpl propDef = new PrismPropertyDefinitionImpl(name, typeName, prismContext);
        itemDefinitions.add(propDef);
        return propDef;
    }

    // Creates reference to other schema
    // TODO: maybe check if the name is in different namespace
    // TODO: maybe create entirely new concept of property reference?
    public PrismPropertyDefinition createPropertyDefinition(QName name) {
        PrismPropertyDefinition propDef = new PrismPropertyDefinitionImpl(name, null, prismContext);
        itemDefinitions.add(propDef);
        return propDef;
    }

    public PrismPropertyDefinitionImpl createPropertyDefinition(String localName, QName typeName) {
        QName name = new QName(getSchemaNamespace(), localName);
        return createPropertyDefinition(name, typeName);
    }

    public PrismPropertyDefinition createPropertyDefinition(String localName, String localTypeName) {
        QName name = new QName(getSchemaNamespace(), localName);
        QName typeName = new QName(getSchemaNamespace(), localTypeName);
        return createPropertyDefinition(name, typeName);
    }
    //endregion

    //region Finding definitions

    // TODO deduplicate w.r.t. findNamedItemDefinition
    @Override
    public <T extends ItemDefinition> T findItemDefinition(@NotNull QName name, @NotNull Class<T> clazz,
            boolean caseInsensitive) {
        for (ItemDefinition def : getDefinitions()) {
            if (def.isValidFor(name, clazz, caseInsensitive)) {
                return (T) def;
            }
        }
        return null;
    }

    @Override
    public <ID extends ItemDefinition> ID findItemDefinition(@NotNull ItemPath path, @NotNull Class<ID> clazz) {
        for (;;) {
            if (path.isEmpty()) {
                throw new IllegalArgumentException("Cannot resolve empty path on complex type definition " + this);
            }
            ItemPathSegment first = path.first();
            if (first instanceof NameItemPathSegment) {
                QName firstName = ((NameItemPathSegment) first).getName();
                return findNamedItemDefinition(firstName, path.rest(), clazz);
            } else if (first instanceof IdItemPathSegment) {
                path = path.rest();
            } else if (first instanceof ParentPathSegment) {
                ItemPath rest = path.rest();
                ComplexTypeDefinition parent = getSchemaRegistry().determineParentDefinition(this, rest);
                if (rest.isEmpty()) {
                    // requires that the parent is defined as an item (container, object)
                    return (ID) getSchemaRegistry().findItemDefinitionByType(parent.getTypeName());
                } else {
                    return parent.findItemDefinition(rest, clazz);
                }
            } else if (first instanceof ObjectReferencePathSegment) {
                throw new IllegalStateException(
                        "Couldn't use '@' path segment in this context. CTD=" + getTypeName() + ", path=" + path);
            } else {
                throw new IllegalStateException("Unexpected path segment: " + first + " in " + path);
            }
        }
    }

    // path starts with NamedItemPathSegment
    public <ID extends ItemDefinition> ID findNamedItemDefinition(@NotNull QName firstName, @NotNull ItemPath rest,
            @NotNull Class<ID> clazz) {
        ID found = null;
        for (ItemDefinition def : getDefinitions()) {
            if (def.isValidFor(firstName, clazz, false)) {
                if (found != null) {
                    throw new IllegalStateException(
                            "More definitions found for " + firstName + "/" + rest + " in " + this);
                }
                found = (ID) def.findItemDefinition(rest, clazz);
                if (QNameUtil.hasNamespace(firstName)) {
                    return found; // if qualified then there's no risk of matching more entries
                }
            }
        }
        return found;
    }
    //endregion

    /**
     * Merge provided definition into this definition.
     */
    @Override
    public void merge(ComplexTypeDefinition otherComplexTypeDef) {
        for (ItemDefinition otherItemDef : otherComplexTypeDef.getDefinitions()) {
            add(otherItemDef.clone());
        }
    }

    @Override
    public void revive(PrismContext prismContext) {
        if (this.prismContext != null) {
            return;
        }
        this.prismContext = prismContext;
        for (ItemDefinition def : itemDefinitions) {
            def.revive(prismContext);
        }
    }

    @Override
    public boolean isEmpty() {
        return itemDefinitions.isEmpty();
    }

    @NotNull
    @Override
    public ComplexTypeDefinitionImpl clone() {
        ComplexTypeDefinitionImpl clone = new ComplexTypeDefinitionImpl(this.typeName, prismContext);
        copyDefinitionData(clone);
        return clone;
    }

    public ComplexTypeDefinition deepClone() {
        return deepClone(new HashMap<>());
    }

    @NotNull
    @Override
    public ComplexTypeDefinition deepClone(Map<QName, ComplexTypeDefinition> ctdMap) {
        if (ctdMap != null) {
            ComplexTypeDefinition clone = ctdMap.get(this.getTypeName());
            if (clone != null) {
                return clone; // already cloned
            }
        }
        ComplexTypeDefinitionImpl clone = clone(); // shallow
        if (ctdMap != null) {
            ctdMap.put(this.getTypeName(), clone);
        }
        clone.itemDefinitions.clear();
        for (ItemDefinition itemDef : this.itemDefinitions) {
            clone.itemDefinitions.add(itemDef.deepClone(ctdMap));
        }
        return clone;
    }

    protected void copyDefinitionData(ComplexTypeDefinitionImpl clone) {
        super.copyDefinitionData(clone);
        clone.containerMarker = this.containerMarker;
        clone.objectMarker = this.objectMarker;
        clone.xsdAnyMarker = this.xsdAnyMarker;
        clone.extensionForType = this.extensionForType;
        clone.defaultNamespace = this.defaultNamespace;
        clone.ignoredNamespaces = this.ignoredNamespaces;
        clone.itemDefinitions.addAll(this.itemDefinitions);
    }

    public void replaceDefinition(QName propertyName, ItemDefinition newDefinition) {
        for (int i = 0; i < itemDefinitions.size(); i++) {
            ItemDefinition itemDef = itemDefinitions.get(i);
            if (itemDef.getName().equals(propertyName)) {
                if (!itemDef.getClass().isAssignableFrom(newDefinition.getClass())) {
                    throw new IllegalArgumentException("The provided definition of class "
                            + newDefinition.getClass().getName() + " does not match existing definition of class "
                            + itemDef.getClass().getName());
                }
                if (!itemDef.getName().equals(newDefinition.getName())) {
                    newDefinition = newDefinition.clone();
                    ((ItemDefinitionImpl) newDefinition).setName(propertyName);
                }
                // Make sure this is set, not add. set will keep correct ordering
                itemDefinitions.set(i, newDefinition);
                return;
            }
        }
        throw new IllegalArgumentException(
                "The definition with name " + propertyName + " was not found in complex type " + getTypeName());
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = super.hashCode();
        result = prime * result + (containerMarker ? 1231 : 1237);
        result = prime * result + ((extensionForType == null) ? 0 : extensionForType.hashCode());
        result = prime * result + ((itemDefinitions == null) ? 0 : itemDefinitions.hashCode());
        result = prime * result + (objectMarker ? 1231 : 1237);
        result = prime * result + (xsdAnyMarker ? 1231 : 1237);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!super.equals(obj)) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        ComplexTypeDefinitionImpl other = (ComplexTypeDefinitionImpl) obj;
        if (containerMarker != other.containerMarker) {
            return false;
        }
        if (extensionForType == null) {
            if (other.extensionForType != null) {
                return false;
            }
        } else if (!extensionForType.equals(other.extensionForType)) {
            return false;
        }
        if (!itemDefinitions.equals(other.itemDefinitions)) {
            return false;
        }
        if (objectMarker != other.objectMarker) {
            return false;
        }
        if (xsdAnyMarker != other.xsdAnyMarker) {
            return false;
        }
        // TODO ignored and default namespaces
        return true;
    }

    @Override
    public String debugDump(int indent) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < indent; i++) {
            sb.append(DebugDumpable.INDENT_STRING);
        }
        sb.append(toString());
        if (extensionForType != null) {
            sb.append(",ext:");
            sb.append(PrettyPrinter.prettyPrint(extensionForType));
        }
        if (ignored) {
            sb.append(",ignored");
        }
        if (containerMarker) {
            sb.append(",Mc");
        }
        if (objectMarker) {
            sb.append(",Mo");
        }
        if (xsdAnyMarker) {
            sb.append(",Ma");
        }
        extendDumpHeader(sb);
        for (ItemDefinition def : getDefinitions()) {
            sb.append("\n");
            sb.append(def.debugDump(indent + 1));
            extendDumpDefinition(sb, def);
        }
        return sb.toString();
    }

    protected void extendDumpHeader(StringBuilder sb) {
        // Do nothing
    }

    protected void extendDumpDefinition(StringBuilder sb, ItemDefinition<?> def) {
        // Do nothing      
    }

    /**
      * Return a human readable name of this class suitable for logs.
      */
    @Override
    protected String getDebugDumpClassName() {
        return "CTD";
    }

    @Override
    public String getDocClassName() {
        return "complex type";
    }

    //   @Override
    //   public void accept(Visitor visitor) {
    //      super.accept(visitor);
    //      itemDefinitions.forEach(def -> def.accept(visitor));
    //   }
}