/* * 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 org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.betaconceptframework.astroboa.api.model.RepositoryUser; import org.betaconceptframework.astroboa.api.model.definition.Localization; import org.betaconceptframework.astroboa.api.model.exception.CmsException; import org.betaconceptframework.astroboa.engine.jcr.io.Deserializer; import org.betaconceptframework.astroboa.engine.jcr.io.IOUtils; import org.betaconceptframework.astroboa.engine.jcr.io.ImportContext; 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; import org.xml.sax.helpers.AttributesImpl; /** * ContentHandler responsible to listen to SAX events during * JSON import. * * Its aim is to identify which elements represent attributes * in order to feed ImportContentHandler with the correct information * * * @author Gregory Chomatas (gchomatas@betaconcept.com) * @author Savvas Triantafyllou (striantafyllou@betaconcept.com) * */ public class JsonImportContentHandler<T> implements ContentHandler{ private Logger logger = LoggerFactory.getLogger(getClass()); private Deque<XMLElement> elementQueue = new ArrayDeque<XMLElement>(); private ImportContentHandler<T> importContentHandler; private StringBuilder elementContent = new StringBuilder(); private ImportContext importContext; public JsonImportContentHandler(Class<T> resultType, Deserializer deserializer) { importContext = new ImportContext(); importContentHandler = new ImportContentHandler<T>(importContext, resultType, deserializer); } @Override public void characters(char[] ch, int start, int length) throws SAXException { elementContent.append(ch, start, length); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { XMLElement finishedElement = elementQueue.poll(); if (finishedElement != null){ finishedElement.content = elementContent.toString(); //Special cases. These built in attributes must be known when creating the actual //repository entity which will represent this element if (StringUtils.equalsIgnoreCase(CmsBuiltInItem.ContentObjectTypeName.getLocalPart(),localName) || StringUtils.equalsIgnoreCase(CmsBuiltInItem.CmsIdentifier.getLocalPart(),localName) || StringUtils.equalsIgnoreCase(CmsBuiltInItem.Name.getLocalPart(),localName) || StringUtils.equalsIgnoreCase(CmsBuiltInItem.SystemName.getLocalPart(),localName) || StringUtils.equalsIgnoreCase(CmsBuiltInItem.ExternalId.getLocalPart(),localName)){ if (elementQueue.isEmpty()){ throw new CmsException("Element "+localName + " represents a built in attribute but no parent element found"); } else{ if (StringUtils.isBlank(finishedElement.content)){ if (! StringUtils.equalsIgnoreCase(CmsBuiltInItem.Name.getLocalPart(),localName)){ throw new CmsException("Element "+localName+ " must have a value"); } } else{ XMLElement parentElement = elementQueue.peek(); IOUtils.addAttribute(parentElement.attributes, localName, finishedElement.content.trim()); parentElement.childElements.remove(finishedElement); } } } else{ //Forward Element to be imported normally if (elementQueue.isEmpty()){ importElement(uri, localName, qName, finishedElement); } } logger.debug("Removed element {} from the queue", localName); } clearElementContent(); } /* private void printAttributes(Attributes atts) throws SAXException { if (atts != null && atts.getLength() >0){ for (int i=0;i<atts.getLength();i++){ logger.debug(atts.getLocalName(i)+ ":"+ atts.getValue(i)); } } }*/ private void importElement(String uri, String localName, String qName, XMLElement element) throws SAXException { importContentHandler.startElement(uri, localName, qName, element.attributes); if (! element.childElements.isEmpty()){ Object entityCurrentlyImported = importContentHandler.getCurrentEntityImported(); boolean elementContainsTheLabelsOfAnEntity = CmsBuiltInItem.Localization.getLocalPart().equalsIgnoreCase(localName) && entityCurrentlyImported != null && entityCurrentlyImported instanceof Localization ; for (XMLElement childElement : element.childElements){ entityCurrentlyImported = importContentHandler.getCurrentEntityImported(); //Check if element represents a built in attribute if (elementContainsTheLabelsOfAnEntity && CmsConstants.LOCALIZED_LABEL_ELEMENT_NAME.equalsIgnoreCase(childElement.localName) && CollectionUtils.isNotEmpty(childElement.childElements)){ for (XMLElement childElementOfLabel : childElement.childElements){ ((Localization) entityCurrentlyImported).addLocalizedLabel(childElementOfLabel.localName, childElementOfLabel.content); } } else if (childElement.childElements.isEmpty() && importContext.nameCorrespondsToAnAttribute(childElement.localName, entityCurrentlyImported)){ importContentHandler.addAttributeToImportedEntity(childElement.localName, childElement.content); } else if (entityCurrentlyImported != null && entityCurrentlyImported instanceof RepositoryUser && CmsConstants.URL_ATTRIBUTE_NAME.equalsIgnoreCase(childElement.localName)){ continue; //Do not import attribute URL for RepositoryUser entity as it is not yet supported } else{ importElement(uri, childElement.localName, childElement.localName, childElement); } } } if (element.content != null){ importContentHandler.characters(element.content.toCharArray(), 0, element.content.length()); } importContentHandler.endElement(uri, localName, qName); } @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { XMLElement element = new XMLElement(); element.localName = localName; if (! elementQueue.isEmpty()){ XMLElement parentElement = elementQueue.peek(); parentElement.childElements.add(element); } elementQueue.push(element); logger.debug("Pushed element {} to queue",localName); } @Override public void endPrefixMapping(String prefix) throws SAXException { importContentHandler.endPrefixMapping(prefix); } @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { importContentHandler.ignorableWhitespace(ch, start, length); } @Override public void processingInstruction(String target, String data) throws SAXException { importContentHandler.processingInstruction(target, data); } @Override public void setDocumentLocator(Locator locator) { importContentHandler.setDocumentLocator(locator); } @Override public void skippedEntity(String name) throws SAXException { importContentHandler.skippedEntity(name); } @Override public void startDocument() throws SAXException { importContentHandler.startDocument(); } @Override public void endDocument() throws SAXException { elementQueue.clear(); importContentHandler.endDocument(); } @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { importContentHandler.startPrefixMapping(prefix, uri); } public T getResult(){ return importContentHandler.getImportResult(); } private class XMLElement{ private String localName; private String content; //Built in attribute values private AttributesImpl attributes = new AttributesImpl(); private List<XMLElement> childElements = new ArrayList<XMLElement>(); public String toString(){ return localName; } } private void clearElementContent() { elementContent.delete(0, elementContent.length()); } }