com.evolveum.midpoint.prism.schema.PrismSchema.java Source code

Java tutorial

Introduction

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

Source

/*
 * Copyright (c) 2010-2014 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.schema;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.xml.namespace.QName;

import com.evolveum.midpoint.prism.*;

import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.EntityResolver;

import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;

/**
 * Schema as a collection of definitions. This is a midPoint-specific view of
 * schema definition. It is just a collection of definitions grouped under a
 * specific namespace.
 * 
 * The schema and all the public classes in this package define a schema
 * meta-model. It is supposed to be used for run-time schema interpretation. It
 * will not be a convenient tool to work with static data model objects such as
 * user or role. But it is needed for interpreting dynamic schemas for resource
 * objects, extensions and so on.
 * 
 * @author Radovan Semancik
 * 
 */
public class PrismSchema implements DebugDumpable {

    private static final long serialVersionUID = 5068618465625931984L;

    private static final Trace LOGGER = TraceManager.getTrace(PrismSchema.class);

    protected String namespace;
    protected Collection<Definition> definitions;
    protected PrismContext prismContext;

    protected PrismSchema(PrismContext prismContext) {
        this.prismContext = prismContext;
        definitions = new ArrayList<Definition>();
    }

    public PrismSchema(String namespace, PrismContext prismContext) {
        if (StringUtils.isEmpty(namespace)) {
            throw new IllegalArgumentException("Namespace can't be null or empty.");
        }
        this.namespace = namespace;
        this.prismContext = prismContext;
        definitions = new ArrayList<Definition>();
    }

    /**
     * Returns schema namespace.
     * 
     * All schema definitions are placed in the returned namespace.
     * 
     * @return schema namespace
     */
    public String getNamespace() {
        return namespace;
    }

    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }

    /**
     * Returns set of definitions.
     * 
     * The set contains all definitions of all types that were parsed. Order of
     * definitions is insignificant.
     * 
     * @return set of definitions
     */
    public Collection<Definition> getDefinitions() {
        if (definitions == null) {
            definitions = new HashSet<Definition>();
        }
        return definitions;
    }

    public <T extends Definition> Collection<T> getDefinitions(Class<T> type) {
        Collection<T> defs = new HashSet<T>();
        for (Definition def : getDefinitions()) {
            if (type.isAssignableFrom(def.getClass())) {
                defs.add((T) def);
            }
        }
        return defs;
    }

    public Collection<PrismObjectDefinition> getObjectDefinitions() {
        return getDefinitions(PrismObjectDefinition.class);
    }

    public Collection<ComplexTypeDefinition> getComplexTypeDefinitions() {
        return getDefinitions(ComplexTypeDefinition.class);
    }

    public void add(Definition def) {
        definitions.add(def);
    }

    public PrismContext getPrismContext() {
        return prismContext;
    }

    // TODO: cleanup this chaos
    public static PrismSchema parse(Element element, boolean isRuntime, String shortDescription,
            PrismContext prismContext) throws SchemaException {
        return parse(element, prismContext.getSchemaRegistry(), new PrismSchema(prismContext), isRuntime,
                shortDescription, prismContext);
    }

    public static PrismSchema parse(Element element, EntityResolver resolver, boolean isRuntime,
            String shortDescription, PrismContext prismContext) throws SchemaException {
        return parse(element, resolver, new PrismSchema(prismContext), isRuntime, shortDescription, prismContext);
    }

    protected static PrismSchema parse(Element element, PrismSchema schema, boolean isRuntime,
            String shortDescription, PrismContext prismContext) throws SchemaException {
        return parse(element, prismContext.getSchemaRegistry(), schema, isRuntime, shortDescription, prismContext);
    }

    protected static PrismSchema parse(Element element, EntityResolver resolver, PrismSchema schema,
            boolean isRuntime, String shortDescription, PrismContext prismContext) throws SchemaException {
        if (element == null) {
            throw new IllegalArgumentException("Schema element must not be null in " + shortDescription);
        }

        DomToSchemaProcessor processor = new DomToSchemaProcessor();
        processor.setEntityResolver(resolver);
        processor.setPrismContext(prismContext);
        processor.setShortDescription(shortDescription);
        processor.setRuntime(isRuntime);
        return processor.parseDom(schema, element);
    }

    public Document serializeToXsd() throws SchemaException {
        SchemaToDomProcessor processor = new SchemaToDomProcessor();
        processor.setPrismContext(prismContext);
        return processor.parseSchema(this);
    }

    // TODO: Methods for searching the schema, such as findDefinitionByName(),
    // etc.

    /**
     * Finds a PropertyContainerDefinition by the type name.
     * 
     * @param typeName
     *            property container type name
     * @return found property container definition
     * @throws IllegalStateException
     *             if more than one definition is found
     */
    public PrismContainerDefinition findContainerDefinitionByType(QName typeName) {
        return findContainerDefinitionByType(typeName, PrismContainerDefinition.class);
    }

    public <X extends Objectable> PrismObjectDefinition<X> findObjectDefinitionByType(QName typeName) {
        return findContainerDefinitionByType(typeName, PrismObjectDefinition.class);
    }

    public <X extends Objectable> PrismObjectDefinition<X> findObjectDefinitionByTypeAssumeNs(QName typeName) {
        return findContainerDefinitionByTypeAssumeNs(typeName, PrismObjectDefinition.class);
    }

    public <X extends Objectable> PrismObjectDefinition<X> findObjectDefinitionByElementName(QName elementName) {
        return findContainerDefinitionByElementName(elementName, PrismObjectDefinition.class);
    }

    public <T extends Objectable> PrismObjectDefinition<T> findObjectDefinitionByType(QName typeName,
            Class<T> type) {
        return findContainerDefinitionByType(typeName, PrismObjectDefinition.class);
    }

    // TODO create cache eagerly
    private Map<Class<? extends Objectable>, PrismObjectDefinition> classToDefCache = Collections
            .synchronizedMap(new HashMap());

    public <T extends Objectable> PrismObjectDefinition<T> findObjectDefinitionByCompileTimeClass(Class<T> type) {
        if (classToDefCache.containsKey(type)) { // there may be null values
            return classToDefCache.get(type);
        }

        PrismObjectDefinition definition = scanForPrismObjectDefinition(type);
        classToDefCache.put(type, definition);
        return definition;
    }

    private <T extends Objectable> PrismObjectDefinition<T> scanForPrismObjectDefinition(Class<T> type) {
        for (Definition def : getDefinitions()) {
            if (def instanceof PrismObjectDefinition<?>) {
                PrismObjectDefinition<?> objDef = (PrismObjectDefinition<?>) def;
                if (type.equals(objDef.getCompileTimeClass())) {
                    classToDefCache.put(type, objDef);
                    return (PrismObjectDefinition<T>) objDef;
                }
            }
        }
        return null;
    }

    private <T extends PrismContainerDefinition> T findContainerDefinitionByType(QName typeName, Class<T> type) {
        if (typeName == null) {
            throw new IllegalArgumentException("typeName must be supplied");
        }
        // TODO: check for multiple definition with the same type
        for (Definition definition : definitions) {
            if (type.isAssignableFrom(definition.getClass()) && typeName.equals(definition.getTypeName())) {
                return (T) definition;
            }
        }
        return null;
    }

    private <T extends PrismContainerDefinition> T findContainerDefinitionByTypeAssumeNs(QName typeName,
            Class<T> type) {
        if (typeName == null) {
            throw new IllegalArgumentException("typeName must be supplied");
        }
        // TODO: check for multiple definition with the same type
        for (Definition definition : definitions) {
            if (type.isAssignableFrom(definition.getClass())
                    && QNameUtil.match(typeName, definition.getTypeName())) {
                return (T) definition;
            }
        }
        return null;
    }

    public <C extends Containerable> PrismContainerDefinition<C> findContainerDefinitionByElementName(
            QName elementName) {
        return findContainerDefinitionByElementName(elementName, PrismContainerDefinition.class);
    }

    private <T extends PrismContainerDefinition> T findContainerDefinitionByElementName(QName elementName,
            Class<T> type) {
        if (elementName == null) {
            throw new IllegalArgumentException("elementName must be supplied");
        }
        // TODO: check for multiple definition with the same type
        for (Definition definition : definitions) {
            if (type.isAssignableFrom(definition.getClass())
                    && elementName.equals(((PrismContainerDefinition) definition).getName())) {
                return (T) definition;
            }
        }
        return null;
    }

    public <C extends Containerable> PrismContainerDefinition<C> findContainerDefinitionByCompileTimeClass(
            Class<C> type) {
        for (Definition def : getDefinitions()) {
            if (def instanceof PrismContainerDefinition<?>) {
                PrismContainerDefinition<C> contDef = (PrismContainerDefinition<C>) def;
                if (type.equals(contDef.getCompileTimeClass())) {
                    return contDef;
                }
            }
        }
        return null;
    }

    public <C extends Containerable> ComplexTypeDefinition findComplexTypeDefinitionByCompileTimeClass(
            Class<C> type) {
        for (Definition def : getDefinitions()) {
            if (def instanceof ComplexTypeDefinition) {
                ComplexTypeDefinition ctd = (ComplexTypeDefinition) def;
                if (type.equals(ctd.getCompileTimeClass())) {
                    return ctd;
                }
            }
        }
        return null;
    }

    public PrismPropertyDefinition findPropertyDefinitionByElementName(QName elementName) {
        return findPropertyDefinitionByElementName(elementName, PrismPropertyDefinition.class);
    }

    public PrismReferenceDefinition findReferenceDefinitionByElementName(QName elementName) {
        return findReferenceDefinitionByElementName(elementName, PrismReferenceDefinition.class);
    }

    private <T extends PrismPropertyDefinition> T findPropertyDefinitionByElementName(QName elementName,
            Class<T> type) {
        if (elementName == null) {
            throw new IllegalArgumentException("elementName must be supplied");
        }
        // TODO: check for multiple definition with the same type
        for (Definition definition : definitions) {
            if (type.isAssignableFrom(definition.getClass())
                    && elementName.equals(((PrismPropertyDefinition) definition).getName())) {
                return (T) definition;
            }
        }
        return null;
    }

    private <T extends PrismReferenceDefinition> T findReferenceDefinitionByElementName(QName elementName,
            Class<T> type) {
        if (elementName == null) {
            throw new IllegalArgumentException("elementName must be supplied");
        }
        // TODO: check for multiple definition with the same type
        for (Definition definition : definitions) {
            if (type.isAssignableFrom(definition.getClass())
                    && elementName.equals(((PrismReferenceDefinition) definition).getName())) {
                return (T) definition;
            }
        }
        return null;
    }

    /**
    * Finds complex type definition by type name.
    */
    public ComplexTypeDefinition findComplexTypeDefinition(QName typeName) {
        if (typeName == null) {
            throw new IllegalArgumentException("typeName must be supplied");
        }
        // TODO: check for multiple definition with the same type
        for (Definition definition : definitions) {
            if (definition instanceof ComplexTypeDefinition && typeName.equals(definition.getTypeName())) {
                return (ComplexTypeDefinition) definition;
            }
        }
        return null;
    }

    /**
     * Finds item definition by name.
     * 
     */
    @SuppressWarnings("unchecked")
    public <T extends ItemDefinition> T findItemDefinition(QName definitionName, Class<T> definitionType) {
        if (definitionName == null) {
            throw new IllegalArgumentException("definitionName must be supplied");
        }
        // TODO: check for multiple definition with the same type
        for (Definition definition : definitions) {
            if (definitionType.isAssignableFrom(definition.getClass())) {
                ItemDefinition idef = (ItemDefinition) definition;
                if (definitionName.equals(idef.getName())) {
                    return (T) idef;
                }
            }
        }
        return null;
    }

    /**
     * Finds item definition by local name
     */
    public <T extends ItemDefinition> T findItemDefinition(String localName, Class<T> definitionType) {
        if (localName == null) {
            throw new IllegalArgumentException("localName must be supplied");
        }
        // TODO: check for multiple definition with the same type
        for (Definition definition : definitions) {
            if (definitionType.isAssignableFrom(definition.getClass())) {
                ItemDefinition idef = (ItemDefinition) definition;
                if (localName.equals(idef.getName().getLocalPart())) {
                    return (T) idef;
                }
            }
        }
        return null;
    }

    /**
    * Finds item definition by type.
    * 
    */
    @SuppressWarnings("unchecked")
    public <T extends ItemDefinition> T findItemDefinitionByType(QName typeName, Class<T> definitionType) {
        if (typeName == null) {
            throw new IllegalArgumentException("typeName must be supplied");
        }
        // TODO: check for multiple definition with the same type
        for (Definition definition : definitions) {
            if (definitionType.isAssignableFrom(definition.getClass())) {
                ItemDefinition idef = (ItemDefinition) definition;
                if (typeName.equals(idef.getTypeName())) {
                    return (T) idef;
                }
            }
        }
        return null;
    }

    public boolean isEmpty() {
        return definitions.isEmpty();
    }

    /**
     * Creates a new property container definition and adds it to the schema.
     * 
     * This is a preferred way how to create definition in the schema.
     * 
     * @param localTypeName
     *            type name "relative" to schema namespace
     * @return new property container definition
     */
    public PrismContainerDefinition createPropertyContainerDefinition(String localTypeName) {
        QName typeName = new QName(getNamespace(), localTypeName);
        QName name = new QName(getNamespace(), toElementName(localTypeName));
        ComplexTypeDefinition cTypeDef = new ComplexTypeDefinition(typeName, prismContext);
        PrismContainerDefinition def = new PrismContainerDefinition(name, cTypeDef, prismContext);
        definitions.add(cTypeDef);
        definitions.add(def);
        return def;
    }

    public PrismContainerDefinition createPropertyContainerDefinition(String localElementName,
            String localTypeName) {
        QName typeName = new QName(getNamespace(), localTypeName);
        QName name = new QName(getNamespace(), localElementName);
        ComplexTypeDefinition cTypeDef = findComplexTypeDefinition(typeName);
        if (cTypeDef == null) {
            cTypeDef = new ComplexTypeDefinition(typeName, prismContext);
            definitions.add(cTypeDef);
        }
        PrismContainerDefinition def = new PrismContainerDefinition(name, cTypeDef, prismContext);
        definitions.add(def);
        return def;
    }

    public ComplexTypeDefinition createComplexTypeDefinition(QName typeName) {
        ComplexTypeDefinition cTypeDef = new ComplexTypeDefinition(typeName, prismContext);
        definitions.add(cTypeDef);
        return cTypeDef;
    }

    /**
     * Creates a top-level property definition and adds it to the schema.
     * 
     * This is a preferred way how to create definition in the schema.
     * 
     * @param localName
     *            element name "relative" to schema namespace
     * @param typeName
     *            XSD type name of the element
     * @return new property definition
     */
    public PrismPropertyDefinition createPropertyDefinition(String localName, QName typeName) {
        QName name = new QName(getNamespace(), localName);
        return createPropertyDefinition(name, typeName);
    }

    /**
     * Creates a top-level property definition and adds it to the schema.
     * 
     * This is a preferred way how to create definition in the schema.
     * 
     * @param localName
     *            element name "relative" to schema namespace
     * @param localTypeName
     *            XSD type name "relative" to schema namespace
     * @return new property definition
     */
    public PrismPropertyDefinition createPropertyDefinition(String localName, String localTypeName) {
        QName name = new QName(getNamespace(), localName);
        QName typeName = new QName(getNamespace(), localTypeName);
        return createPropertyDefinition(name, typeName);
    }

    /**
     * Creates a top-level property definition and adds it to the schema.
     * 
     * This is a preferred way how to create definition in the schema.
     * 
     * @param localName
     *            element name
     * @param typeName
     *            XSD type name of the element
     * @return new property definition
     */
    public PrismPropertyDefinition createPropertyDefinition(QName name, QName typeName) {
        PrismPropertyDefinition def = new PrismPropertyDefinition(name, typeName, prismContext);
        definitions.add(def);
        return def;
    }

    /**
     * Internal method to create a "nice" element name from the type name.
     */
    protected String toElementName(String localTypeName) {
        String elementName = StringUtils.uncapitalize(localTypeName);
        if (elementName.endsWith("Type")) {
            return elementName.substring(0, elementName.length() - 4);
        }
        return elementName;
    }

    protected QName toElementQName(QName qname) {
        return new QName(qname.getNamespaceURI(), toElementName(qname.getLocalPart()));
    }

    @Override
    public String debugDump() {
        return debugDump(0);
    }

    @Override
    public String debugDump(int indent) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < indent; i++) {
            sb.append(INDENT_STRING);
        }
        sb.append(toString()).append("\n");
        Iterator<Definition> i = definitions.iterator();
        while (i.hasNext()) {
            Definition def = i.next();
            sb.append(def.debugDump(indent + 1));
            if (i.hasNext()) {
                sb.append("\n");
            }
        }
        return sb.toString();
    }

    @Override
    public String toString() {
        return "Schema(ns=" + namespace + ")";
    }

}