org.betaconceptframework.astroboa.engine.definition.visitor.CmsDefinitionVisitor.java Source code

Java tutorial

Introduction

Here is the source code for org.betaconceptframework.astroboa.engine.definition.visitor.CmsDefinitionVisitor.java

Source

/*
 * Copyright (C) 2005-2012 BetaCONCEPT Limited
 *
 * This file is part of Astroboa.
 *
 * Astroboa is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Astroboa is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Astroboa.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.betaconceptframework.astroboa.engine.definition.visitor;

import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import javax.xml.XMLConstants;
import javax.xml.namespace.QName;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.betaconceptframework.astroboa.api.model.BetaConceptNamespaceConstants;
import org.betaconceptframework.astroboa.api.model.ValueType;
import org.betaconceptframework.astroboa.api.model.definition.ContentObjectTypeDefinition;
import org.betaconceptframework.astroboa.api.model.definition.LocalizableCmsDefinition;
import org.betaconceptframework.astroboa.api.model.definition.ObjectReferencePropertyDefinition;
import org.betaconceptframework.astroboa.api.model.exception.CmsException;
import org.betaconceptframework.astroboa.api.model.io.ResourceRepresentationType;
import org.betaconceptframework.astroboa.cache.region.DefinitionCacheRegion;
import org.betaconceptframework.astroboa.model.impl.ItemQName;
import org.betaconceptframework.astroboa.model.impl.definition.ObjectReferencePropertyDefinitionImpl;
import org.betaconceptframework.astroboa.model.impl.item.CmsDefinitionItem;
import org.betaconceptframework.astroboa.model.impl.item.ItemUtils;
import org.betaconceptframework.astroboa.util.CmsConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.sun.xml.xsom.XSAnnotation;
import com.sun.xml.xsom.XSAttGroupDecl;
import com.sun.xml.xsom.XSAttributeDecl;
import com.sun.xml.xsom.XSAttributeUse;
import com.sun.xml.xsom.XSComplexType;
import com.sun.xml.xsom.XSContentType;
import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSFacet;
import com.sun.xml.xsom.XSIdentityConstraint;
import com.sun.xml.xsom.XSModelGroup;
import com.sun.xml.xsom.XSModelGroupDecl;
import com.sun.xml.xsom.XSNotation;
import com.sun.xml.xsom.XSParticle;
import com.sun.xml.xsom.XSSchema;
import com.sun.xml.xsom.XSSimpleType;
import com.sun.xml.xsom.XSWildcard;
import com.sun.xml.xsom.XSXPath;
import com.sun.xml.xsom.visitor.XSVisitor;

/**
 * @author Gregory Chomatas (gchomatas@betaconcept.com)
 * @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
 * 
 */
public class CmsDefinitionVisitor implements XSVisitor {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private DefinitionCacheRegion definitionCacheRegion;

    private Map<String, XSAttGroupDecl> attributeGroupDeclarations = new TreeMap<String, XSAttGroupDecl>();
    private Map<String, XSComplexType> complexTypeDeclarations = new TreeMap<String, XSComplexType>();
    private Map<String, XSElementDecl> elementDeclarations = new TreeMap<String, XSElementDecl>();

    private Map<ItemQName, XSAttributeUse> builtInAttributes = new HashMap<ItemQName, XSAttributeUse>();

    private Map<String, Set<String>> topicPropertyPathsPerTaxonomies = new HashMap<String, Set<String>>();
    private Set<String> multivalueProperties = new HashSet<String>();
    private Map<String, byte[]> xmlSchemaDefinitionsPerFilename = new HashMap<String, byte[]>();
    private Map<String, List<String>> contentTypeHierarchy = new HashMap<String, List<String>>();
    private Map<QName, String> xmlSchemaDefinitionURLsPerQName = new HashMap<QName, String>();

    private List<String> definitionsUnderProcess = new ArrayList<String>();

    private Map<String, LocalizableCmsDefinition> internalDefinitionsCache = new HashMap<String, LocalizableCmsDefinition>();

    private List<ObjectReferencePropertyDefinition> objectReferencePropertyDefinitions = new ArrayList<ObjectReferencePropertyDefinition>();

    public void createContentDefintions() throws Exception {

        if (attributeGroupDeclarations.isEmpty())
            throw new Exception("Cms internal attribute group "
                    + CmsDefinitionItem.contentObjectPropertyAttGroup.getLocalPart() + "  is not found");

        //Order of this is significant

        //Visit attribute group declarations
        for (XSAttGroupDecl attributeGroupDecl : attributeGroupDeclarations.values())
            attributeGroupDecl.visit(this);

        //Visit Elements
        for (XSElementDecl elementDecl : elementDeclarations.values()) {
            elementDecl.visit(this);
        }

        //Visit ComplexTypes which were not processed at all during element visit
        for (Entry<String, XSComplexType> complexDecl : complexTypeDeclarations.entrySet()) {

            //Visit Complex Type only if it has not been visited before
            //and it is not an Astroboa model complex type
            String targetNamespace = complexDecl.getValue().getTargetNamespace();

            if (StringUtils.isNotBlank(targetNamespace)
                    && !BetaConceptNamespaceConstants.ASTROBOA_MODEL_DEFINITION_URI.equals(targetNamespace)
                    && !BetaConceptNamespaceConstants.ASTROBOA_API_URI.equals(targetNamespace)
                    && !definitionCacheRegion.hasComplexTypeDefinition(complexDecl.getKey())) {
                complexDecl.getValue().visit(this);
            }
        }

        //Further process ContentObjectPropertyDefinitions which contain content type restriction
        processContentObjectPropertyDefinitions();

        //Notify cache for several additional information
        definitionCacheRegion
                .putTopicPropertyPathsPerTaxonomy(transformSetInMapToList(topicPropertyPathsPerTaxonomies));
        definitionCacheRegion.putXMLSchemaDefinitionsPerFilename(xmlSchemaDefinitionsPerFilename);
        definitionCacheRegion.putMultivalueProperties(new ArrayList<String>(multivalueProperties));
        definitionCacheRegion.putContentTypeHierarchy(contentTypeHierarchy);
        definitionCacheRegion.putLocationURLForDefinition(xmlSchemaDefinitionURLsPerQName);

        definitionCacheRegion.printDefinitionCacheToLog();

    }

    /**
     * 
     */
    private void processContentObjectPropertyDefinitions() {

        if (CollectionUtils.isNotEmpty(objectReferencePropertyDefinitions)) {

            for (ObjectReferencePropertyDefinition objectReferencePropertyDefinition : objectReferencePropertyDefinitions) {

                List<String> acceptedContentTypes = objectReferencePropertyDefinition.getAcceptedContentTypes();

                if (CollectionUtils.isNotEmpty(acceptedContentTypes)) {

                    //Use Set to ensure that values are unique
                    Set<String> expandedContentTypes = new HashSet<String>();

                    for (String acceptedContentType : acceptedContentTypes) {
                        if (contentTypeHierarchy.containsKey(acceptedContentType)) {
                            //AcceptedContentType is a super type and thus we need to 
                            //keep all content types which extend this super type
                            expandedContentTypes.addAll(contentTypeHierarchy.get(acceptedContentType));
                        } else {
                            expandedContentTypes.add(acceptedContentType);
                        }
                    }

                    ((ObjectReferencePropertyDefinitionImpl) objectReferencePropertyDefinition)
                            .addExpandedAcceptedContentTypes(new HashSet<String>(expandedContentTypes));

                    if (logger.isDebugEnabled()) {
                        logger.debug("Accepted content types {} of property {} have been expanded to {}",
                                new Object[] { objectReferencePropertyDefinition.getAcceptedContentTypes(),
                                        objectReferencePropertyDefinition.getFullPath(),
                                        objectReferencePropertyDefinition.getExpandedAcceptedContentTypes() });
                    }
                }
            }
        }

    }

    private Map<String, List<String>> transformSetInMapToList(Map<String, Set<String>> mapWithSet) {

        Map<String, List<String>> mapWithList = new HashMap<String, List<String>>();
        if (MapUtils.isNotEmpty(mapWithSet)) {
            for (Entry<String, Set<String>> entry : mapWithSet.entrySet()) {
                mapWithList.put(entry.getKey(), new ArrayList<String>(entry.getValue()));
            }
        }

        return mapWithList;
    }

    public void annotation(XSAnnotation arg0) {
    }

    public void attGroupDecl(XSAttGroupDecl attGroup) {

        //Process only built-in attribute group
        if (attGroup.getName().equals(CmsDefinitionItem.contentObjectPropertyAttGroup.getLocalPart())) {
            String targetNameSpace = attGroup.getTargetNamespace();
            Iterator<? extends XSAttributeUse> attrDeclarationsIter = attGroup.iterateDeclaredAttributeUses();
            while (attrDeclarationsIter.hasNext()) {
                XSAttributeUse attributeUse = attrDeclarationsIter.next();
                String name = attributeUse.getDecl().getName();

                //Use default prefix
                ItemQName attribute = ItemUtils.createNewItem(
                        BetaConceptNamespaceConstants.ASTROBOA_MODEL_DEFINITION_PREFIX, targetNameSpace, name);
                builtInAttributes.put(attribute, attributeUse);

            }
        }
    }

    public void attributeDecl(XSAttributeDecl arg0) {
    }

    public void attributeUse(XSAttributeUse arg0) {

    }

    public void complexType(XSComplexType complexType) {

        //Visit only complex properties which extend complexCmsPropertyType
        //Normally this method is called when visiting global complex types
        if (isElementValidComplexType(complexType)) {
            CmsPropertyVisitor contentObjectPropertyVisitor = new CmsPropertyVisitor(builtInAttributes, null, false,
                    false, 0, this);
            complexType.visit(contentObjectPropertyVisitor);

            cacheDefinition(contentObjectPropertyVisitor.getDefinition());
        } else {
            logger.debug("Type {} does not extend builtin complex type 'complexCmsPropertyType'", complexType);
        }

    }

    private boolean isElementValidComplexType(XSComplexType complexType) {
        //According to XSOM API method getBaseType always returns not null
        String typeName = complexType.getBaseType().getName();
        String typeNamespace = complexType.getBaseType().getTargetNamespace();

        ItemQName complexTypeAsItemQName = ItemUtils.createNewItem("", typeNamespace, typeName);

        return complexTypeAsItemQName.equals(CmsDefinitionItem.complexCmsPropertyType); //Complex Type must extend ComplexCmsProperty type 

    }

    public void cacheInternalDefinition(LocalizableCmsDefinition definition) {
        if (definition != null) {
            String typeQName = getDefinitionQName(definition);

            //Put definition to InternalCache
            try {
                internalDefinitionsCache.put(typeQName, definition);
            } catch (Exception e) {
                throw new CmsException(e);
            }

            if (ValueType.ContentType == definition.getValueType()
                    || ValueType.Complex == definition.getValueType()) {
                if (definitionsUnderProcess.contains(typeQName)) {
                    definitionsUnderProcess.remove(typeQName);
                }
            }
        }
    }

    public boolean internalCacheHasDefinition(String typeName, String typeNamespace) {
        return typeName != null && typeNamespace != null
                && internalDefinitionsCache.containsKey("{" + typeNamespace + "}" + typeName);
    }

    public LocalizableCmsDefinition getDefinitionFromInternalCache(String typeName, String typeNamespace) {
        if (typeName != null && typeNamespace != null) {
            return internalDefinitionsCache.get("{" + typeNamespace + "}" + typeName);
        } else {
            return null;
        }
    }

    public void cacheDefinition(LocalizableCmsDefinition definition) {
        if (definition != null) {

            //Put definition to CacheManager according to its valueType
            try {
                definitionCacheRegion.putDefinition(definition.getValueType(), definition.getName(), definition);
            } catch (Exception e) {
                throw new CmsException(e);
            }

            if (ValueType.ContentType == definition.getValueType()
                    || ValueType.Complex == definition.getValueType()) {

                String typeQName = getDefinitionQName(definition);

                if (definitionsUnderProcess.contains(typeQName)) {
                    definitionsUnderProcess.remove(typeQName);
                }

                xmlSchemaDefinitionURLsPerQName.put(definition.getQualifiedName(),
                        definition.url(ResourceRepresentationType.XSD));

                //Get base content types
                if (ValueType.ContentType == definition.getValueType()) {
                    List<String> superContentTypes = ((ContentObjectTypeDefinition) definition)
                            .getSuperContentTypes();

                    if (CollectionUtils.isNotEmpty(superContentTypes)) {
                        for (String superContentType : superContentTypes) {
                            if (!contentTypeHierarchy.containsKey(superContentType)) {
                                contentTypeHierarchy.put(superContentType, new ArrayList<String>());
                            }

                            if (!contentTypeHierarchy.get(superContentType).contains(definition.getName())) {
                                contentTypeHierarchy.get(superContentType).add(definition.getName());
                            }
                        }
                    }
                }

                //Create property paths
                DefinitionPropertyPathBuilder definitionPropertyPathBuilder = new DefinitionPropertyPathBuilder(
                        ValueType.Complex == definition.getValueType());
                definition.accept(definitionPropertyPathBuilder);

                objectReferencePropertyDefinitions
                        .addAll(definitionPropertyPathBuilder.getObjectReferencePropertyDefinitions());

                //definitionPropertyPathBuilder.loadPropertyPathsForDefinition(((ContentObjectTypeDefinition)definition).getPropertyDefinitions(), definition);

                //Get Multivalue properties
                if (CollectionUtils.isNotEmpty(definitionPropertyPathBuilder.getMutlivalueProperties())) {
                    multivalueProperties.addAll(definitionPropertyPathBuilder.getMutlivalueProperties());
                }

                //Get topic property paths per taxonomy and add them to list
                Map<String, Set<String>> childTopicPropertyPaths = definitionPropertyPathBuilder
                        .getTopicPropertyPathsPerTaxonomy();
                if (MapUtils.isNotEmpty(childTopicPropertyPaths)) {
                    for (Entry<String, Set<String>> topicPropertyPathPerTaxonomy : childTopicPropertyPaths
                            .entrySet()) {

                        List<String> topicPropertyPathList = new ArrayList<String>(
                                topicPropertyPathPerTaxonomy.getValue());
                        String taxonomyName = topicPropertyPathPerTaxonomy.getKey();

                        if (CollectionUtils.isNotEmpty(topicPropertyPathList)) {

                            if (!topicPropertyPathsPerTaxonomies.containsKey(taxonomyName)) {
                                topicPropertyPathsPerTaxonomies.put(taxonomyName, new HashSet<String>());
                            }

                            for (String topicPropertyPath : topicPropertyPathList) {
                                if (!topicPropertyPathsPerTaxonomies.get(taxonomyName)
                                        .contains(topicPropertyPath)) {
                                    topicPropertyPathsPerTaxonomies.get(taxonomyName).add(topicPropertyPath);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public void facet(XSFacet arg0) {
    }

    public void identityConstraint(XSIdentityConstraint arg0) {

    }

    public void notation(XSNotation arg0) {

    }

    public void schema(XSSchema schema) {

        //      Do not process schema of XML Schema!!!
        if (!XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(schema.getTargetNamespace())) {
            //Collect all attributeGroups
            if (MapUtils.isNotEmpty(schema.getAttGroupDecls()))
                attributeGroupDeclarations.putAll(schema.getAttGroupDecls());

            //Collect all elements
            if (MapUtils.isNotEmpty(schema.getElementDecls()))
                elementDeclarations.putAll(schema.getElementDecls());

            //Collect all complexTypes
            if (MapUtils.isNotEmpty(schema.getComplexTypes()))
                complexTypeDeclarations.putAll(schema.getComplexTypes());

        }
    }

    public void xpath(XSXPath arg0) {
    }

    public void elementDecl(XSElementDecl element) {
        //NOTE in this method only global elements are processed
        if (element.isGlobal()) {
            CmsPropertyVisitor contentObjectPropertyVisitor = new CmsPropertyVisitor(builtInAttributes, null, false,
                    false, 0, this);
            element.visit(contentObjectPropertyVisitor);

            LocalizableCmsDefinition definition = contentObjectPropertyVisitor.getDefinition();

            cacheDefinition(definition);

            //Check if this element refers to a complex type 
            //In this case complexType Definition should be removed from ComlpexTypeDeclaration map
            if (definition != null && element.getType() != null) {
                String complexTypeRefName = element.getType().getName();

                if (complexTypeRefName != null)
                    complexTypeDeclarations.remove(complexTypeRefName);

            }
        }
    }

    public void modelGroup(XSModelGroup arg0) {

    }

    public void modelGroupDecl(XSModelGroupDecl arg0) {

    }

    public void wildcard(XSWildcard arg0) {

    }

    public void empty(XSContentType arg0) {

    }

    public void particle(XSParticle arg0) {

    }

    public void simpleType(XSSimpleType arg0) {

    }

    public void clear() {

        logger.debug("Clearing Definition Visitor");

        try {
            definitionCacheRegion.removeRegion();
        } catch (Exception e) {
            logger.warn("Failed to clear cache region", e);
        }

        attributeGroupDeclarations.clear();
        complexTypeDeclarations.clear();
        elementDeclarations.clear();
        builtInAttributes.clear();

        topicPropertyPathsPerTaxonomies.clear();
        xmlSchemaDefinitionsPerFilename.clear();
        xmlSchemaDefinitionURLsPerQName.clear();
        multivalueProperties.clear();
        contentTypeHierarchy.clear();
        internalDefinitionsCache.clear();

        definitionsUnderProcess.clear();

        objectReferencePropertyDefinitions.clear();
    }

    public void addXMLSchemaDefinitionForFileName(byte[] fileContent, String filename) {

        logger.debug("Adding schema content for file {}", filename);
        xmlSchemaDefinitionsPerFilename.put(filename, fileContent);
    }

    public void addXMLSchemaDefinitionForFileName(URL definitionURL) {

        InputStream builtInStream = null;
        try {
            if (definitionURL == null) {
                logger.warn("Found no XML schema file for definition ");
                return;
            }

            String filename = StringUtils.substringAfterLast(definitionURL.toExternalForm(),
                    CmsConstants.FORWARD_SLASH);

            builtInStream = definitionURL.openStream();

            logger.debug("Adding schema content for file {}", filename);
            xmlSchemaDefinitionsPerFilename.put(filename, IOUtils.toByteArray(builtInStream));

        } catch (Exception e) {
            logger.error("", e);
            throw new CmsException(e.getMessage());
        } finally {
            IOUtils.closeQuietly(builtInStream);
        }

    }

    public void addXMLSchemaDefinitions(Map<String, byte[]> builtInModelAndApiSchemaFiles) {
        if (MapUtils.isNotEmpty(builtInModelAndApiSchemaFiles)) {
            logger.debug("Adding schema content for files {}", builtInModelAndApiSchemaFiles.keySet());
            xmlSchemaDefinitionsPerFilename.putAll(builtInModelAndApiSchemaFiles);
        }

    }

    public DefinitionCacheRegion getDefinitionCacheRegion() {
        return definitionCacheRegion;
    }

    public void cacheDefinitionWhichIsUnderProcess(LocalizableCmsDefinition definition) {

        if (definition != null) {
            String typeQName = getDefinitionQName(definition);

            if (!definitionsUnderProcess.contains(typeQName)) {
                definitionsUnderProcess.add(typeQName);
            }
        }
    }

    private String getDefinitionQName(LocalizableCmsDefinition definition) {
        String typeQName = "{" + definition.getQualifiedName().getNamespaceURI() + "}" + definition.getName();
        return typeQName;
    }

    public boolean isDefinitionUnderProcess(String typeName, String typeNamespace) {
        return typeName != null && typeNamespace != null
                && definitionsUnderProcess.contains("{" + typeNamespace + "}" + typeName);
    }

    public XSComplexType getComplexType(String complexTypeName) {

        if (complexTypeName == null) {
            return null;
        }

        return complexTypeDeclarations.get(complexTypeName);

    }
}