/* * 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 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Astroboa. If not, see <http://www.gnu.org/licenses/>. */ package org.betaconceptframework.astroboa.engine.jcr.io.contenthandler; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List; import javax.xml.XMLConstants; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import org.apache.commons.codec.binary.Base64; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.betaconceptframework.astroboa.api.model.BinaryChannel; import org.betaconceptframework.astroboa.api.model.BinaryProperty; import org.betaconceptframework.astroboa.api.model.CmsProperty; import org.betaconceptframework.astroboa.api.model.CmsRepositoryEntity; import org.betaconceptframework.astroboa.api.model.ComplexCmsProperty; import org.betaconceptframework.astroboa.api.model.ComplexCmsRootProperty; import org.betaconceptframework.astroboa.api.model.ContentObject; import org.betaconceptframework.astroboa.api.model.ObjectReferenceProperty; import org.betaconceptframework.astroboa.api.model.RepositoryUser; import org.betaconceptframework.astroboa.api.model.SimpleCmsProperty; import org.betaconceptframework.astroboa.api.model.Space; import org.betaconceptframework.astroboa.api.model.Taxonomy; import org.betaconceptframework.astroboa.api.model.Topic; import org.betaconceptframework.astroboa.api.model.TopicReferenceProperty; import org.betaconceptframework.astroboa.api.model.ValueType; import org.betaconceptframework.astroboa.api.model.definition.CmsPropertyDefinition; import org.betaconceptframework.astroboa.api.model.definition.Localization; import org.betaconceptframework.astroboa.api.model.definition.SimpleCmsPropertyDefinition; import org.betaconceptframework.astroboa.api.model.exception.CmsException; import org.betaconceptframework.astroboa.engine.jcr.io.Deserializer; import org.betaconceptframework.astroboa.engine.jcr.io.ImportContext; import org.betaconceptframework.astroboa.engine.jcr.io.ImportedEntity; import org.betaconceptframework.astroboa.engine.model.jaxb.Repository; import org.betaconceptframework.astroboa.model.factory.CmsRepositoryEntityFactoryForActiveClient; import org.betaconceptframework.astroboa.model.impl.RepositoryUserImpl; import org.betaconceptframework.astroboa.model.impl.item.CmsBuiltInItem; import org.betaconceptframework.astroboa.util.CmsConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; /** * * Main import content handler responsible to import XML. * * For JSON import use JsonImportContentHandler * * @author Gregory Chomatas (gchomatas@betaconcept.com) * @author Savvas Triantafyllou (striantafyllou@betaconcept.com) * */ public class ImportContentHandler<T> implements ContentHandler{ private Logger logger = LoggerFactory.getLogger(getClass()); private ImportContext importContext; private StringBuilder elementContent = new StringBuilder(); private Deque<ImportedEntity> cmsRepositoryEntityQueue = new ArrayDeque<ImportedEntity>(); private T importResult; private Class<T> resultType; /* * During import some elements may be ignored because they * have already been processed. This stands for topics or repository users * which may be encountered more than once in side an XML or JSON * * This variable holds the main element whose content (child elements) are ignored. * Only when this elements is closed, import is back to normal. */ private String elementNameToBeIgnored = null; private DatatypeFactory df; private Deserializer deserializer; public ImportContentHandler(Class<T> resultType, Deserializer deserializer){ this(new ImportContext(), resultType, deserializer); } public ImportContentHandler(ImportContext importContext, Class<T> resultType, Deserializer deserializer){ this.importContext = importContext; this.resultType = resultType; this.deserializer = deserializer; try { df = DatatypeFactory.newInstance(); } catch (DatatypeConfigurationException e) { throw new CmsException(e); } } private boolean doNotProcessEvent() { return elementNameToBeIgnored != null; } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (doNotProcessEvent()){ return; } elementContent.append(ch, start, length); } @Override public void endDocument() throws SAXException { elementContent = null; elementNameToBeIgnored = null; importContext.dispose(); importContext = null; } @Override public void startDocument() throws SAXException { clearElementContent(); elementNameToBeIgnored = null; cmsRepositoryEntityQueue.clear(); importResult = null; } private void createRootEntity(String uri, String rootElementName, Attributes atts) throws SAXException { if (Topic.class.isAssignableFrom(resultType)){ importResult = (T) createNewTopic(atts, rootElementName); } else if (Taxonomy.class.isAssignableFrom(resultType)){ importResult = (T) createNewTaxonomy(atts, rootElementName); } else if (RepositoryUser.class.isAssignableFrom(resultType)){ importResult = (T) createNewRepositoryUser(atts, rootElementName); } else if (Repository.class.isAssignableFrom(resultType)){ importResult = (T) new Repository(); } else if (Space.class.isAssignableFrom(resultType)){ importResult = (T) createNewSpace(atts, rootElementName); } else if (ContentObject.class.isAssignableFrom(resultType)){ importResult = (T) createNewContentObject(atts, rootElementName, uri, null); } else if (List.class.isAssignableFrom(resultType)){ importResult = (T) new ArrayList(); } else{ throw new SAXException("Unsupported type of imported Entity "+ resultType.getName()); } pushEntity(rootElementName, importResult, atts); elementNameToBeIgnored = null; } private void importAttributes(Attributes atts) throws SAXException { if (atts != null && atts.getLength() >0){ for (int i=0;i<atts.getLength();i++){ //Ignore attributes with specific namespaces String uri = atts.getURI(i); if (StringUtils.equals(uri, XMLConstants.XMLNS_ATTRIBUTE_NS_URI) || StringUtils.equals(uri, XMLConstants.XML_NS_URI) || StringUtils.equals(uri, XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI) || StringUtils.equals(uri, XMLConstants.W3C_XML_SCHEMA_NS_URI) || StringUtils.equals(uri, XMLConstants.W3C_XPATH_DATATYPE_NS_URI)){ continue; } addAttributeToImportedEntity(atts.getLocalName(i), atts.getValue(i)); } } } @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { logger.debug("Importing element name {}, uri {}", localName, uri); if (doNotProcessEvent()){ return; } if (importResult == null){ createRootEntity(uri, localName, atts); } else{ //Queue should not be empty throwExceptionIfQeueIsEmpty(uri, localName); Object currentEntity = getCurrentEntityImported(); // Element corresponds to a cms property if (currentEntity instanceof ContentObject){ if (CmsConstants.OWNER_ELEMENT_NAME.equals(localName)){ if (processOwner(atts)){ return; } } else{ processCmsProperty(localName, atts, uri); return ; } } else if (currentEntity instanceof CmsProperty ){ processCmsProperty(localName, atts, uri); return ; } else if (currentEntity instanceof BinaryChannel && CmsConstants.CONTENT_ELEMENT_NAME.equals(localName)){ //Element is content , which is defined in context of BinaryChannel //It will be processed when closing this element cmsRepositoryEntityQueue.peek().processingRawData(true); return; } else if (CmsConstants.LOCALIZED_LABEL_ELEMENT_NAME.equals(localName)){ //Depending on source XML or JSON attribute for lang may appear differently String langValue = getValueForAttribute(CmsConstants.LANG_ATTRIBUTE_NAME_WITH_PREFIX, atts); if (langValue == null){ langValue = getValueForAttribute(CmsConstants.LANG_ATTRIBUTE_NAME, atts); } if (langValue != null){ addAttributeToImportedEntity(CmsConstants.LANG_ATTRIBUTE_NAME_WITH_PREFIX, langValue); } else{ //Found no lang attribute. if (atts.getLength() > 0 && currentEntity instanceof Localization){ addLabelsForLocaleToImportedEntity(atts); } } return; } else if (CmsConstants.OWNER_ELEMENT_NAME.equals(localName)){ if (processOwner(atts)){ return; } } else if (shouldIgnoreElement(localName)){ return ; } else{ if (currentEntity instanceof Topic){ //Expecting taxonomy, parentTopic or child topic final Topic currentlyImportedTopic = (Topic)currentEntity; if (CmsBuiltInItem.Taxonomy.getLocalPart().equals(localName)){ Taxonomy taxonomy = createNewTaxonomy(atts, localName); currentlyImportedTopic.setTaxonomy(taxonomy); pushEntity(localName, taxonomy, atts); return; } else if (CmsBuiltInItem.Topic.getLocalPart().equals(localName)){ //Perform an initial check if topic name already exists if (atts!=null && importContext.isEntityCached(getValueForAttribute(CmsBuiltInItem.Name.getLocalPart(), atts))){ throw new SAXException("Topic with name "+getValueForAttribute(CmsBuiltInItem.Name.getLocalPart(), atts) +" has been already imported"); } Topic childTopic = createNewTopic(atts, localName); currentlyImportedTopic.addChild(childTopic); if (childTopic.getTaxonomy() == null && currentlyImportedTopic.getTaxonomy() != null){ childTopic.setTaxonomy(currentlyImportedTopic.getTaxonomy()); } pushEntity(localName, childTopic, atts); return; } else if (CmsConstants.PARENT_TOPIC.equals(localName)){ Topic parentTopic = createNewTopic(atts, localName); currentlyImportedTopic.setParent(parentTopic); pushEntity(localName, parentTopic, atts); return; } } else if (currentEntity instanceof Space){ //Expecting parentSpace or child space if (CmsBuiltInItem.Space.getLocalPart().equals(localName)){ Space space = createNewSpace(atts, localName); ((Space)currentEntity).addChild(space); pushEntity(localName, space, atts); return; } else if (CmsConstants.PARENT_SPACE.equals(localName)){ Space space = createNewSpace(atts, localName); ((Space)currentEntity).setParent(space); pushEntity(localName, space, atts); return; } } else if (currentEntity instanceof Taxonomy){ //Expecting child topic if (CmsBuiltInItem.Topic.getLocalPart().equals(localName)){ if (atts!=null && importContext.isEntityCached(getValueForAttribute(CmsBuiltInItem.Name.getLocalPart(), atts))){ throw new SAXException("Topic with name "+getValueForAttribute(CmsBuiltInItem.Name.getLocalPart(), atts) +" has been already imported"); } Topic topic = createNewTopic(atts, localName); ((Taxonomy)currentEntity).addRootTopic(topic); pushEntity(localName, topic, atts); return; } } else if (currentEntity instanceof RepositoryUser){ if (CmsBuiltInItem.Space.getLocalPart().equals(localName)){ Space space = createNewSpace(atts, localName); ((RepositoryUserImpl)currentEntity).setSpace(space); pushEntity(localName, space, atts); return; } else if (CmsBuiltInItem.Taxonomy.getLocalPart().equals(localName)){ Taxonomy taxonomy = createNewTaxonomy(atts, localName); ((RepositoryUserImpl)currentEntity).setFolksonomy(taxonomy); pushEntity(localName, taxonomy, atts); return; } } else if (currentEntity instanceof Repository){ if (CmsBuiltInItem.Taxonomy.getLocalPart().equals(localName)){ Taxonomy taxonomy = createNewTaxonomy(atts, localName); pushEntity(localName, taxonomy, atts); return; } else if (CmsBuiltInItem.Topic.getLocalPart().equals(localName)){ Topic topic = createNewTopic(atts, localName); pushEntity(localName, topic, atts); return; } else if (CmsBuiltInItem.OrganizationSpace.getLocalPart().equals(localName)){ Space space = createNewSpace(atts, localName); pushEntity(localName, space, atts); return; } else if (CmsBuiltInItem.RepositoryUser.getLocalPart().equals(localName)){ RepositoryUser repositoryUser = createNewRepositoryUser(atts, localName); pushEntity(localName, repositoryUser, atts); return; } else{ ContentObject contentObject = createNewContentObject(atts, localName, uri, null); pushEntity(localName, contentObject, atts); return ; } } else if (currentEntity instanceof List){ if (CmsBuiltInItem.Taxonomy.getLocalPart().equals(localName)){ Taxonomy taxonomy = createNewTaxonomy(atts, localName); ((List)currentEntity).add(taxonomy); pushEntity(localName, taxonomy, atts); return; } else if (CmsBuiltInItem.Space.getLocalPart().equals(localName)){ Space space = createNewSpace(atts, localName); ((List)currentEntity).add(space); pushEntity(localName, space, atts); return; } else if (CmsBuiltInItem.RepositoryUser.getLocalPart().equals(localName)){ RepositoryUser repositoryUser = createNewRepositoryUser(atts, localName); ((List)currentEntity).add(repositoryUser); pushEntity(localName, repositoryUser, atts); return; } else if (CmsBuiltInItem.Topic.getLocalPart().equals(localName)){ Topic topic = createNewTopic(atts, localName); ((List)currentEntity).add(topic); pushEntity(localName, topic, atts); return; } else{ ContentObject contentObject = createNewContentObject(atts, localName, uri, null); ((List)currentEntity).add(contentObject); pushEntity(localName, contentObject, atts); return ; } } } throw new SAXException("Unexpected element "+localName); } } public Object getCurrentEntityImported() { if (cmsRepositoryEntityQueue.isEmpty()){ return null; } return cmsRepositoryEntityQueue.peek().getEntity(); } private boolean shouldIgnoreElement(String localName) { return StringUtils.equals(localName, CmsBuiltInItem.Localization.getLocalPart()) || StringUtils.equals(localName, CmsConstants.ROOT_TOPICS) || StringUtils.equals(localName, CmsConstants.CHILD_SPACES) || StringUtils.equals(localName, CmsConstants.CHILD_TOPICS) || (cmsRepositoryEntityQueue.peek().getEntity() instanceof Repository && (StringUtils.equals(localName, CmsConstants.OBJECTS_ELEMENT_NAME) || StringUtils.equals(localName, CmsConstants.TAXONOMIES_ELEMENT_NAME) || StringUtils.equals(localName, CmsConstants.TOPICS_ELEMENT_NAME) || StringUtils.equals(localName, CmsConstants.REPOSITORY_USERS_ELEMENT_NAME))) || (cmsRepositoryEntityQueue.peek().getEntity() instanceof List && ( StringUtils.equals(localName, CmsConstants.RESOURCE_COLLECTION) || StringUtils.equals(localName, CmsConstants.RESOURCE_RESPONSE) || StringUtils.equals(localName, CmsConstants.TOTAL_RESOURCE_COUNT) || StringUtils.equals(localName, CmsConstants.OFFSET) || StringUtils.equals(localName, CmsConstants.LIMIT) ) ); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { logger.debug("Ending element {}",localName); ImportedEntity entityWhoseImportHasFinished = null; if (elementNameToBeIgnored != null){ //Ending element belongs to a part of source which is ignored //Check whether this part has ended or not //In case ignored part has ended, current imported entity is returned //in order to be further processed entityWhoseImportHasFinished = checkIgnoredPartHasEnded(localName); } else if (StringUtils.equals(CmsConstants.LOCALIZED_LABEL_ELEMENT_NAME, localName)){ addLocalizedLabelToImportedEntity(retrieveElementContentValue()); clearElementContent(); return ; } else if (StringUtils.equals(CmsConstants.CONTENT_ELEMENT_NAME, localName) && ! cmsRepositoryEntityQueue.isEmpty() && cmsRepositoryEntityQueue.peek().entityIsABinaryChannelAndRawDataElementIsBeingProcessed()){ //Content element is ending. Add content to binaryChannel addContentToBinaryChannel(retrieveElementContentValue()); clearElementContent(); cmsRepositoryEntityQueue.peek().processingRawData(false); return; } else if (entityHasEnded(localName)){ entityWhoseImportHasFinished = removeEntityFromQueue(); } if (entityWhoseImportHasFinished != null){ entityWhoseImportHasFinished.completeEntityImport((retrieveElementContentValue())); if (entityWhoseImportHasFinished.getEntity() instanceof CmsRepositoryEntity){ CmsRepositoryEntity cmsRepositoryEntity = (CmsRepositoryEntity)entityWhoseImportHasFinished.getEntity(); boolean cacheEntity = true; if (! cmsRepositoryEntityQueue.isEmpty() && ( cmsRepositoryEntityQueue.peek().getEntity() instanceof Repository || cmsRepositoryEntityQueue.peek().getEntity() instanceof List) ){ //Repository or Resource Collection import under way. Send entity to importer in order to be saved accordingly cmsRepositoryEntity = deserializer.save(cmsRepositoryEntity); } else if (cmsRepositoryEntity instanceof BinaryChannel){ deserializer.loadBinaryChannelContent((BinaryChannel)cmsRepositoryEntity); } else{ //Special case. If entity currently imported is a complex property but has no child properties //then remove this property as user has specified a NULL value (in JSON) or an empty tag (in XML), //marking this property for removal. if ( (cmsRepositoryEntity instanceof ComplexCmsProperty) && (!(cmsRepositoryEntity instanceof ComplexCmsRootProperty))){ ComplexCmsProperty complexProperty = ((ComplexCmsProperty)cmsRepositoryEntity); if (complexProperty.getChildProperties() == null || complexProperty.getChildProperties().isEmpty()){ ((ComplexCmsProperty)complexProperty).getParentProperty().removeChildProperty(complexProperty.getPath()); cacheEntity = false; } } } if (cacheEntity){ importContext.cacheEntity(cmsRepositoryEntity); } } } clearElementContent(); } private String retrieveElementContentValue() { return StringUtils.trimToNull(elementContent.toString()); } private boolean processOwner(Attributes atts) throws SAXException { ImportedEntity currentImportedEntity = cmsRepositoryEntityQueue.peek(); if (currentImportedEntity == null){ throw new CmsException("No parent entity found for element 'owner' with attributes "+ atts.toString()); } if (currentImportedEntity.hasOwnerElement()){ RepositoryUser owner = createNewRepositoryUser(atts, CmsConstants.OWNER_ELEMENT_NAME); if (currentImportedEntity.getEntity() instanceof ContentObject){ ((ContentObject)currentImportedEntity.getEntity()).setOwner(owner); } else if (currentImportedEntity.getEntity() instanceof Topic){ ((Topic)currentImportedEntity.getEntity()).setOwner(owner); } else if (currentImportedEntity .getEntity() instanceof Space){ ((Space)currentImportedEntity.getEntity()).setOwner(owner); } else{ logger.debug("Owner {} has not been assigned to parent entity {}", owner.toString(), currentImportedEntity.getEntity()); } pushEntity(CmsConstants.OWNER_ELEMENT_NAME, owner, atts); return true; } return false; } private RepositoryUser createNewRepositoryUser(Attributes atts, String entityLocalName) { RepositoryUser repositoryUser = retrieveOrCreateEntity(RepositoryUser.class, atts, entityLocalName); repositoryUser.setExternalId(getValueForAttribute(CmsBuiltInItem.ExternalId.getLocalPart(), atts)); repositoryUser.setLabel(getValueForAttribute(CmsBuiltInItem.Label.getLocalPart(), atts)); return repositoryUser; } private <T extends CmsRepositoryEntity> T retrieveOrCreateEntity(Class<T> entityClass, Attributes atts, String entityLocalName){ T entity = null; boolean referenceIdNotFoundInMap = false; String cacheKey = null; boolean cacheKeyIsId = false; if (atts != null && atts.getLength() > 0){ cacheKey = getValueForAttribute(CmsBuiltInItem.CmsIdentifier.getLocalPart(), atts); if (cacheKey == null){ cacheKey = getValueForAttribute(CmsBuiltInItem.Name.getLocalPart(), atts); } else{ cacheKeyIsId =true; } if (cacheKey == null && ContentObject.class.isAssignableFrom(entityClass)){ cacheKey = getValueForAttribute(CmsBuiltInItem.SystemName.getLocalPart(), atts); } else if (cacheKey == null && RepositoryUser.class.isAssignableFrom(entityClass)){ cacheKey = getValueForAttribute(CmsBuiltInItem.ExternalId.getLocalPart(), atts); } if (cacheKey != null){ if (importContext.isEntityCached(cacheKey)){ elementNameToBeIgnored = entityLocalName; return (T) importContext.getEntityFromCache(cacheKey); } else{ referenceIdNotFoundInMap = true; } } } if (entityClass == RepositoryUser.class){ entity = (T) CmsRepositoryEntityFactoryForActiveClient.INSTANCE.getFactory().newRepositoryUser(); } else if (entityClass == Topic.class){ entity = (T) CmsRepositoryEntityFactoryForActiveClient.INSTANCE.getFactory().newTopic(); } else if (entityClass == Space.class){ entity = (T) CmsRepositoryEntityFactoryForActiveClient.INSTANCE.getFactory().newSpace(); } else if (entityClass == Taxonomy.class){ entity = (T) CmsRepositoryEntityFactoryForActiveClient.INSTANCE.getFactory().newTaxonomy(); } else if (entityClass == BinaryChannel.class){ entity = (T) CmsRepositoryEntityFactoryForActiveClient.INSTANCE.getFactory().newBinaryChannel(); } else { throw new CmsException("Unknown entity type "+ entityClass); } if (referenceIdNotFoundInMap){ if (entity.getId() == null && cacheKeyIsId){ entity.setId(cacheKey); importContext.cacheEntity(entity); } else if (cacheKey !=null){ importContext.cacheEntity(cacheKey, entity); } } return entity; } private void throwExceptionIfQeueIsEmpty(String uri, String localName) throws SAXException { if (cmsRepositoryEntityQueue.isEmpty()){ throw new SAXException("Element {"+uri+"}"+localName+" was not imported. Entity queue is empty"); } } private ContentObject createNewContentObject(Attributes atts, String localName, String uri, String expectedContentType) { String contentType = getValueForAttribute(CmsBuiltInItem.ContentObjectTypeName.getLocalPart(), atts); if (StringUtils.isBlank(contentType) && StringUtils.isNotBlank(expectedContentType)){ contentType = expectedContentType; } if (contentType == null){ logger.info("Could not identify the type of the object reference of the element "+localName+"."); return null; } try{ ContentObject contentObject = CmsRepositoryEntityFactoryForActiveClient.INSTANCE.getFactory().newObjectForType(contentType); contentObject.setContentObjectType(getValueForAttribute(CmsBuiltInItem.ContentObjectTypeName.getLocalPart(), atts)); contentObject.setSystemName(getValueForAttribute(CmsBuiltInItem.SystemName.getLocalPart(), atts)); return contentObject; } catch(Exception e){ logger.warn("Element "+localName+"'s corresponds to an unknown content type "+contentType+".", e); return null; } } private String getValueForAttribute(String attributeName, Attributes atts){ final int index = atts.getIndex(attributeName); if (index > -1){ return atts.getValue(index); } return null; } private void pushEntity(String entityLocalName, Object entity, Attributes atts) throws SAXException { pushEntity(entityLocalName, entity, atts, false); } private void pushEntity(String entityLocalName, Object entity, Attributes atts, boolean entityRepresentsAContentObjectReference) throws SAXException { ImportedEntity importedEntity = new ImportedEntity(entityLocalName,entity, df,entityRepresentsAContentObjectReference); cmsRepositoryEntityQueue.push(importedEntity); logger.debug("Adding to queue name {} entity {}", entityLocalName, entity); importAttributes(atts); } private boolean entityHasEnded(String localName) { return ! cmsRepositoryEntityQueue.isEmpty() && StringUtils.equals(localName, cmsRepositoryEntityQueue.peek().getName()); } private void addContentToBinaryChannel(String content) { if (content != null){ ((BinaryChannel)cmsRepositoryEntityQueue.peek().getEntity()).setContent(Base64.decodeBase64(content)); } } private void addLabelsForLocaleToImportedEntity(Attributes atts) { if (!cmsRepositoryEntityQueue.isEmpty()){ if (atts != null && atts.getLength() >0){ for (int i=0;i<atts.getLength();i++){ cmsRepositoryEntityQueue.peek().addLocalizedLabel(atts.getLocalName(i), atts.getValue(i)); } } } else{ logger.warn("No entity found in queue. Labels were not imported", atts); } } private void addLocalizedLabelToImportedEntity(String localizedLabel) { if (!cmsRepositoryEntityQueue.isEmpty()){ cmsRepositoryEntityQueue.peek().addLocalizedLabel(localizedLabel); } else{ logger.warn("No entity found in queue. Label {} was not imported", localizedLabel); } } public void addAttributeToImportedEntity(String attributeName, String attributeValue) throws SAXException { boolean attributeHasBeenAdded = false; if (!cmsRepositoryEntityQueue.isEmpty()){ final ImportedEntity firstEntityInQueue = cmsRepositoryEntityQueue.peek(); final Object entity = firstEntityInQueue.getEntity(); attributeHasBeenAdded = firstEntityInQueue.addAttribute(attributeName, attributeValue); if (attributeHasBeenAdded && ( entity instanceof ContentObject || entity instanceof Topic || entity instanceof Taxonomy || entity instanceof Space || entity instanceof RepositoryUser ) ){ if (CmsBuiltInItem.CmsIdentifier.getLocalPart().equals(attributeName)){ importContext.cacheEntity((CmsRepositoryEntity) entity); } } if (! attributeHasBeenAdded && attributeName != null && ( entity instanceof ContentObject || entity instanceof CmsProperty )){ //Probably a cms property startElement(null, attributeName, null, null); if (attributeValue != null){ characters(attributeValue.toCharArray(), 0, attributeValue.length()); } endElement(null, attributeName, null); attributeHasBeenAdded = true; } } if (! attributeHasBeenAdded){ logger.warn("Attribute {} with value {} was not imported. Entity in queue {}", new Object[]{attributeName, attributeValue, cmsRepositoryEntityQueue.isEmpty()? " NONE ": cmsRepositoryEntityQueue.peek().toString()}); } } private void clearElementContent() { elementContent.delete(0, elementContent.length()); elementContent.trimToSize(); } private ImportedEntity checkIgnoredPartHasEnded(String localName) { if (StringUtils.equals(elementNameToBeIgnored, localName)){ elementNameToBeIgnored = null; return removeEntityFromQueue(); } else{ clearElementContent(); return null; } } private ImportedEntity removeEntityFromQueue() { ImportedEntity removedEntity = cmsRepositoryEntityQueue.poll(); logger.debug("Removing entity name {} {} from queue", removedEntity != null ? removedEntity.getName() : " null", removedEntity != null ? removedEntity.getEntity().toString() : null); return removedEntity; } private void processCmsProperty(String localName, Attributes atts, String uri) throws SAXException { ImportedEntity importedEntity = cmsRepositoryEntityQueue.peek(); if (importedEntity == null){ throw new CmsException("No parent property found for element "+localName); } CmsPropertyDefinition cmsPropertyDefinition = null; CmsProperty cmsProperty = null; ComplexCmsProperty parentComplexCmsProperty = null; if (importedEntity.getEntity() instanceof ContentObject){ //Special case. Element 'title' of a content object reference might be imported if (importedEntity.representsAReferenceToAContentObject() && CmsConstants.TITLE_ELEMENT_NAME.equals(localName)){ cmsPropertyDefinition = ((ContentObject)importedEntity.getEntity()).getTypeDefinition().getCmsPropertyDefinition("profile.title"); cmsProperty = ((ContentObject)importedEntity.getEntity()).getCmsProperty("profile.title"); } else{ cmsPropertyDefinition = ((ContentObject)importedEntity.getEntity()).getTypeDefinition().getCmsPropertyDefinition(localName); } if (cmsPropertyDefinition == null){ //Property is an aspect. It is safe to call getCmsProperty cmsProperty = ((ContentObject)importedEntity.getEntity()).getCmsProperty(localName); } else{ parentComplexCmsProperty = ((ContentObject)importedEntity.getEntity()).getComplexCmsRootProperty(); } } else if (importedEntity.getEntity() instanceof ComplexCmsProperty){ cmsPropertyDefinition = ((ComplexCmsProperty)importedEntity.getEntity()).getPropertyDefinition().getChildCmsPropertyDefinition(localName); if (cmsPropertyDefinition == null){ throw new CmsException("Invalid property "+localName+ " for parent property "+ ((ComplexCmsProperty)importedEntity.getEntity()).getFullPath()); } parentComplexCmsProperty = (ComplexCmsProperty)importedEntity.getEntity(); } else if (importedEntity.getEntity() instanceof BinaryProperty){ //Leave entity as is elementNameToBeIgnored = localName; return; } else { throw new CmsException("Expected to find either a ComplexCmsProperty or a BinaryProperty or a ContentObject as a parent for element "+ localName + " " + " but found "+ importedEntity.getName()+ " as "+ importedEntity.getEntity().toString()+ " instead"); } if (cmsProperty == null){ if (cmsPropertyDefinition == null){ throw new CmsException("Invalid child property "+localName+ " parent entity "+ importedEntity.getEntity().toString()); } if (cmsPropertyDefinition.isMultiple() && cmsPropertyDefinition.getValueType() == ValueType.Complex){ cmsProperty = parentComplexCmsProperty.createNewValueForMulitpleComplexCmsProperty(localName); } else{ boolean propertyAlreadyLoaded = parentComplexCmsProperty.isChildPropertyLoaded(localName); cmsProperty = parentComplexCmsProperty.getChildProperty(localName); if (cmsProperty != null && cmsPropertyDefinition instanceof SimpleCmsPropertyDefinition && ((SimpleCmsPropertyDefinition)cmsPropertyDefinition).getDefaultValue() != null && ! propertyAlreadyLoaded){ //remove default value. Property has been loaded for the first time and contains its default value //Default value should be removed ((SimpleCmsProperty)cmsProperty).removeValues(); } } if (cmsProperty == null){ throw new CmsException("Invalid property "+localName+ " for parent property "+ cmsPropertyDefinition.getFullPath()); } } if (cmsProperty.getValueType() == ValueType.Complex){ pushEntity(localName, cmsProperty, atts); } else if (cmsProperty.getValueType() == ValueType.Binary){ BinaryChannel binaryChannel = populateBinaryChannelProperty(((BinaryProperty)cmsProperty), atts); pushEntity(localName, binaryChannel, atts); } else if (cmsProperty.getValueType() == ValueType.TopicReference){ Topic topic = populateTopicProperty(((TopicReferenceProperty)cmsProperty), atts, localName); pushEntity(localName, topic, atts); } else if (cmsProperty.getValueType() == ValueType.ObjectReference){ ContentObject contentObject = populateContentObjectProperty(((ObjectReferenceProperty)cmsProperty), atts, localName, uri); if (contentObject!=null){ pushEntity(localName, contentObject, atts, true); } } else { pushEntity(localName, cmsProperty, atts); } } private ContentObject populateContentObjectProperty( ObjectReferenceProperty contentObjectProperty, Attributes atts, String localName, String uri) { String expectedContentType = null; if (contentObjectProperty != null && contentObjectProperty.getPropertyDefinition() != null && CollectionUtils.isNotEmpty(contentObjectProperty.getPropertyDefinition().getExpandedAcceptedContentTypes()) && contentObjectProperty.getPropertyDefinition().getExpandedAcceptedContentTypes().size() == 1){ expectedContentType = contentObjectProperty.getPropertyDefinition().getExpandedAcceptedContentTypes().get(0); } ContentObject contentObject = createNewContentObject(atts, localName, uri, expectedContentType); if (contentObject!=null){ contentObjectProperty.addSimpleTypeValue(contentObject); } return contentObject; } private Topic populateTopicProperty(TopicReferenceProperty topicProperty, Attributes atts, String entityLocalName) { Topic topic = createNewTopic(atts, entityLocalName); topicProperty.addSimpleTypeValue(topic); return topic; } private Topic createNewTopic(Attributes atts, String entityLocalName) { Topic topic = retrieveOrCreateEntity(Topic.class, atts, entityLocalName); if (elementNameToBeIgnored == null){ topic.setName(getValueForAttribute(CmsBuiltInItem.Name.getLocalPart(), atts)); } return topic; } private Space createNewSpace(Attributes atts, String entityLocalName) { Space space = retrieveOrCreateEntity(Space.class, atts, entityLocalName); if (elementNameToBeIgnored == null){ space.setName(getValueForAttribute(CmsBuiltInItem.Name.getLocalPart(), atts)); } return space; } private Taxonomy createNewTaxonomy(Attributes atts, String entityLocalName) { Taxonomy taxonomy = retrieveOrCreateEntity(Taxonomy.class, atts, entityLocalName); if (elementNameToBeIgnored == null){ taxonomy.setName(getValueForAttribute(CmsBuiltInItem.Name.getLocalPart(), atts)); } return taxonomy; } private BinaryChannel populateBinaryChannelProperty( BinaryProperty binaryProperty, Attributes atts) { BinaryChannel binaryChannel = retrieveOrCreateEntity(BinaryChannel.class, atts, binaryProperty.getName()); binaryChannel.setName(binaryProperty.getName()); binaryProperty.addSimpleTypeValue(binaryChannel); return binaryChannel; } public T getImportResult(){ return importResult; } /* * Unused methods */ @Override public void endPrefixMapping(String prefix) throws SAXException { // TODO Auto-generated method stub } @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { } @Override public void processingInstruction(String target, String data) throws SAXException { } @Override public void setDocumentLocator(Locator locator) { } @Override public void skippedEntity(String name) throws SAXException { } @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { } }