/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.core.content.contentdata.custom.support; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.jdom.Attribute; import org.jdom.Document; import org.jdom.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.enonic.esl.util.DateUtil; import com.enonic.cms.core.content.ContentKey; import com.enonic.cms.core.content.binary.BinaryDataKey; import com.enonic.cms.core.content.contentdata.custom.BinaryDataEntry; import com.enonic.cms.core.content.contentdata.custom.BooleanDataEntry; import com.enonic.cms.core.content.contentdata.custom.CustomContentData; import com.enonic.cms.core.content.contentdata.custom.DataEntry; import com.enonic.cms.core.content.contentdata.custom.DataEntrySet; import com.enonic.cms.core.content.contentdata.custom.DataEntryType; import com.enonic.cms.core.content.contentdata.custom.DateDataEntry; import com.enonic.cms.core.content.contentdata.custom.GroupDataEntry; import com.enonic.cms.core.content.contentdata.custom.KeywordsDataEntry; import com.enonic.cms.core.content.contentdata.custom.MultipleChoiceAlternative; import com.enonic.cms.core.content.contentdata.custom.MultipleChoiceDataEntry; import com.enonic.cms.core.content.contentdata.custom.RelationDataEntry; import com.enonic.cms.core.content.contentdata.custom.contentkeybased.FileDataEntry; import com.enonic.cms.core.content.contentdata.custom.contentkeybased.ImageDataEntry; import com.enonic.cms.core.content.contentdata.custom.contentkeybased.RelatedContentDataEntry; import com.enonic.cms.core.content.contentdata.custom.relationdataentrylistbased.FilesDataEntry; import com.enonic.cms.core.content.contentdata.custom.relationdataentrylistbased.ImagesDataEntry; import com.enonic.cms.core.content.contentdata.custom.relationdataentrylistbased.RelatedContentsDataEntry; import com.enonic.cms.core.content.contentdata.custom.stringbased.HtmlAreaDataEntry; import com.enonic.cms.core.content.contentdata.custom.stringbased.SelectorDataEntry; import com.enonic.cms.core.content.contentdata.custom.stringbased.TextAreaDataEntry; import com.enonic.cms.core.content.contentdata.custom.stringbased.TextDataEntry; import com.enonic.cms.core.content.contentdata.custom.stringbased.UrlDataEntry; import com.enonic.cms.core.content.contentdata.custom.xmlbased.XmlDataEntry; import com.enonic.cms.core.content.contenttype.ContentTypeConfig; import com.enonic.cms.core.content.contenttype.CtySetConfig; import com.enonic.cms.core.content.contenttype.dataentryconfig.DataEntryConfig; import com.enonic.cms.core.content.contenttype.dataentryconfig.DataEntryConfigType; import com.enonic.cms.core.content.contenttype.dataentryconfig.RelatedContentDataEntryConfig; public class CustomContentDataXmlParser { private final static Logger LOG = LoggerFactory.getLogger( CustomContentDataXmlParser.class ); private Document contentDataXml; private ContentTypeConfig contentTypeConfig; private List<BinaryDataKey> binaryDatas; private KeywordsDataEntryParser keywordsDataEntryParser = new KeywordsDataEntryParser(); private XmlDataEntryParser xmlDataEntryParser = new XmlDataEntryParser(); private HtmlAreaDataEntryParser htmlAreaDataEntryParser = new HtmlAreaDataEntryParser(); public static CustomContentData parse( Document contentDataXml, ContentTypeConfig contentTypeConfig ) { CustomContentDataXmlParser xmlParser = new CustomContentDataXmlParser( contentDataXml, contentTypeConfig ); return xmlParser.parse(); } public static CustomContentData parse( Document contentDataXml, ContentTypeConfig contentTypeConfig, List<BinaryDataKey> binaryDatas ) { CustomContentDataXmlParser xmlParser = new CustomContentDataXmlParser( contentDataXml, contentTypeConfig ); xmlParser.setBinaryDatas( binaryDatas ); return xmlParser.parse(); } public CustomContentDataXmlParser( Document contentDataXml, ContentTypeConfig contentTypeConfig ) { if ( contentDataXml == null ) { throw new IllegalArgumentException( "Given contentDataXml cannot be null" ); } if ( contentTypeConfig == null ) { throw new IllegalArgumentException( "Given contentTypeConfig cannot be null" ); } this.contentDataXml = contentDataXml; this.contentTypeConfig = contentTypeConfig; } public void setBinaryDatas( List<BinaryDataKey> list ) { this.binaryDatas = list; } public CustomContentData parse() { CustomContentData contentData = new CustomContentData( contentTypeConfig ); Element contentdataEl = contentDataXml.getRootElement(); List<Element> children = contentdataEl.getChildren(); for ( Element dataEntryEl : children ) { parseElement( dataEntryEl, contentData, "contentdata" ); } populateGroupDataEntryIndexes( contentData ); return contentData; } private void populateGroupDataEntryIndexes( CustomContentData customContentData ) { Map<String, Integer> groupIndexCount = new HashMap<String, Integer>(); for ( DataEntry dataEntry : customContentData.getEntries() ) { if ( dataEntry instanceof GroupDataEntry ) { Integer count = groupIndexCount.get( dataEntry.getName() ); if ( count == null ) { count = 1; } GroupDataEntry groupDataEntry = (GroupDataEntry) dataEntry; groupDataEntry.setGroupIndex( count ); groupIndexCount.put( dataEntry.getName(), count + 1 ); } } } @SuppressWarnings({"unchecked"}) private void parseElement( Element element, DataEntrySet dataEntrySet, String parentXPath ) { String elementName = element.getName(); StringBuffer relativeXPath = new StringBuffer(); if ( parentXPath != null ) { relativeXPath.append( parentXPath ).append( "/" ); } relativeXPath.append( elementName ); String xpath = relativeXPath.toString(); if ( dataEntrySet.getType() == DataEntryType.GROUP && xpath.startsWith( dataEntrySet.getXPath() ) ) { xpath = xpath.substring( dataEntrySet.getXPath().length() + 1 ); } DataEntryConfig inputConfig = dataEntrySet.getInputConfigByRelateiveXPath( xpath ); if ( inputConfig == null ) { CtySetConfig setConfig = dataEntrySet.getConfig().getSetConfigByRelativeXPath( xpath ); if ( setConfig != null && dataEntrySet instanceof CustomContentData ) { GroupDataEntry groupDataEntry = new GroupDataEntry( setConfig.getName(), xpath ); dataEntrySet.add( groupDataEntry ); List<Element> children = element.getChildren(); for ( Element childElement : children ) { parseElement( childElement, groupDataEntry, xpath ); } } else if ( setConfig != null && dataEntrySet instanceof GroupDataEntry ) { throw new IllegalArgumentException( "Groups in groups is not allowed" ); } } if ( inputConfig == null ) { List<Element> children = element.getChildren(); for ( Element childElement : children ) { parseElement( childElement, dataEntrySet, xpath ); } return; } DataEntryConfigType type = inputConfig.getType(); if ( type == DataEntryConfigType.TEXT ) { final String value = getTextFromElement( element ); final TextDataEntry entry = new TextDataEntry( inputConfig, value ); dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.KEYWORDS ) { final KeywordsDataEntry entry = keywordsDataEntryParser.parse( element, inputConfig ); dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.TEXT_AREA ) { final String value = getValueFromElement( element ); final TextAreaDataEntry entry = new TextAreaDataEntry( inputConfig, value ); dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.HTMLAREA ) { final HtmlAreaDataEntry entry = htmlAreaDataEntryParser.parse( element, inputConfig ); dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.XML ) { final XmlDataEntry entry = xmlDataEntryParser.parse( element, inputConfig ); dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.BINARY ) { String keyStr = element.getAttributeValue( "key" ); Element possibleSubBinarydataEl = element.getChild( "binarydata" ); if ( possibleSubBinarydataEl != null ) { keyStr = parseString( possibleSubBinarydataEl.getAttributeValue( "key" ), keyStr ); } BinaryDataEntry entry; if ( keyStr != null ) { if ( keyStr.startsWith( "%" ) ) { Integer binaryIndex = new Integer( keyStr.substring( 1 ) ); if ( binaryDatas != null ) { final BinaryDataKey binaryDataKey2 = binaryDatas.get( binaryIndex ); if ( binaryDataKey2 != null ) { entry = new BinaryDataEntry( inputConfig, binaryDataKey2.toInt() ); } else { entry = new BinaryDataEntry( inputConfig, keyStr ); } } else { entry = new BinaryDataEntry( inputConfig, keyStr ); } } else { final Integer binaryDataKey = new Integer( keyStr ); entry = new BinaryDataEntry( inputConfig, binaryDataKey ); } } else { entry = new BinaryDataEntry( inputConfig ); } dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.RELATEDCONTENT ) { final RelatedContentDataEntryConfig relConfig = (RelatedContentDataEntryConfig) inputConfig; if ( !relConfig.isMultiple() ) { Integer key = parseInteger( element.getAttributeValue( "key" ) ); // Do a extra check if the key can be put in a inner content element final Element contentEl = element.getChild( "content" ); if ( contentEl != null ) { final Integer keyInContentElement = parseInteger( contentEl.getAttributeValue( "key" ) ); if ( keyInContentElement != null ) { // Let the key specified on the inner content element override key = keyInContentElement; } } ContentKey contentKey = null; if ( key != null ) { contentKey = new ContentKey( key ); } final RelatedContentDataEntry entry = new RelatedContentDataEntry( inputConfig, contentKey ); parseDeletedMark( element, entry ); dataEntrySet.add( entry ); } else { final RelatedContentsDataEntry entry = new RelatedContentsDataEntry( inputConfig ); final List<Element> contentEls = element.getChildren( "content" ); for ( final Element contentEl : contentEls ) { final Integer key = parseInteger( contentEl.getAttributeValue( "key" ) ); if ( key != null ) { final RelatedContentDataEntry relatedContentDataEntry = new RelatedContentDataEntry( inputConfig, new ContentKey( key ) ); parseDeletedMark( contentEl, relatedContentDataEntry ); entry.add( relatedContentDataEntry ); } } dataEntrySet.add( entry ); } } else if ( type == DataEntryConfigType.FILE ) { ContentKey contentKey = null; final Element fileEl = element.getChild( "file" ); if ( fileEl != null ) { final Integer key = parseInteger( fileEl.getAttributeValue( "key" ) ); if ( key != null ) { contentKey = new ContentKey( key ); } } final FileDataEntry entry = new FileDataEntry( inputConfig, contentKey ); parseDeletedMark( fileEl, entry ); dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.FILES ) { final FilesDataEntry entry = new FilesDataEntry( inputConfig ); final List<Element> filesEls = element.getChildren( "file" ); for ( final Element fileEl : filesEls ) { final Integer key = parseInteger( fileEl.getAttributeValue( "key" ) ); if ( key != null ) { final FileDataEntry fileDataEntry = new FileDataEntry( inputConfig, new ContentKey( key ) ); parseDeletedMark( fileEl, fileDataEntry ); entry.add( fileDataEntry ); } } dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.IMAGE ) { final Integer key = parseInteger( element.getAttributeValue( "key" ) ); ContentKey contentKey = null; if ( key != null ) { contentKey = new ContentKey( key ); } final ImageDataEntry entry = new ImageDataEntry( inputConfig, contentKey ); parseDeletedMark( element, entry ); dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.IMAGES ) { final ImagesDataEntry entry = new ImagesDataEntry( inputConfig ); final List<Element> imageEls = element.getChildren( "image" ); for ( final Element imageEl : imageEls ) { final Integer key = parseInteger( imageEl.getAttributeValue( "key" ) ); String text = parseText( imageEl.getChild( "text" ) ); if ( "".equals( text ) ) { text = null; } final ImageDataEntry imageDataEntry = new ImageDataEntry( inputConfig, new ContentKey( key ), text ); parseDeletedMark( imageEl, imageDataEntry ); entry.add( imageDataEntry ); } dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.URL ) { final String value = getTextFromElement( element ); final UrlDataEntry entry = new UrlDataEntry( inputConfig, value ); dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.DATE ) { final Date value = parseDate( getTextFromElement( element ) ); final DateDataEntry entry = new DateDataEntry( inputConfig, value ); dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.CHECKBOX ) { final Boolean value = parseBoolean( getTextFromElement( element ) ); final BooleanDataEntry entry = new BooleanDataEntry( inputConfig, value ); dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.RADIOBUTTON || type == DataEntryConfigType.DROPDOWN ) { final String value = getTextFromElement( element ); final SelectorDataEntry entry = new SelectorDataEntry( inputConfig, value ); dataEntrySet.add( entry ); } else if ( type == DataEntryConfigType.MULTIPLE_CHOICE ) { final MultipleChoiceDataEntry entry; if ( hasValueIsFalse( element ) || ( element.getChildren().size() == 0 && element.getChild( "text" ) == null ) ) { entry = new MultipleChoiceDataEntry( inputConfig, null, null ); } else { final String value = getTextFromChildElement( element, "text" ); List children = element.getChildren( "alternative" ); List<MultipleChoiceAlternative> alternatives = new ArrayList<MultipleChoiceAlternative>(); for ( Object child : children ) { alternatives.add( createMultipleChoiceAlternative( (Element) child ) ); } entry = new MultipleChoiceDataEntry( inputConfig, value, alternatives ); } dataEntrySet.add( entry ); } else { LOG.warn( "Input type currently not supported: " + type ); } } private void parseDeletedMark( final Element contentEl, final RelationDataEntry entry ) { if ( contentEl != null ) { final String deletedInContentElement = contentEl.getAttributeValue( "deleted" ); final Boolean deletedValue = deletedInContentElement != null && parseBoolean( deletedInContentElement ); boolean deleted = deletedValue != null && deletedValue; if ( deleted ) { entry.markAsDeleted(); } } } private MultipleChoiceAlternative createMultipleChoiceAlternative( Element childElem ) { return new MultipleChoiceAlternative( childElem.getText(), Boolean.parseBoolean( childElem.getAttributeValue( "correct" ) ) ); } private String getTextFromElement( final Element element ) { if ( hasValueIsFalse( element ) ) { return null; } return element.getText(); } private String getTextFromChildElement( Element element, String childName ) { if ( hasValueIsFalse( element ) ) { return null; } Element child = element.getChild( childName ); if ( child == null ) { return null; } return child.getText(); } private boolean hasValueIsFalse( Element element ) { final Attribute hasValueAtr = element.getAttribute( "has-value" ); if ( hasValueAtr != null && hasValueAtr.getValue().equalsIgnoreCase( "false" ) ) { return true; } else { return false; } } private String getValueFromElement( final Element element ) { final Attribute hasValueAtr = element.getAttribute( "has-value" ); if ( hasValueAtr != null && hasValueAtr.getValue().equalsIgnoreCase( "false" ) ) { return null; } return element.getValue(); } private Boolean parseBoolean( String text ) { if ( StringUtils.isBlank( text ) ) { return null; } return text.equalsIgnoreCase( "true" ); } private Date parseDate( String s ) { if ( s == null ) { return null; } try { return DateUtil.parseISODate( s ); } catch ( Exception e ) { return null; } } private String parseText( Element element ) { if ( element == null ) { return null; } return element.getText(); } private String parseString( String s, String defaultValue ) { if ( s == null ) { return defaultValue; } return s; } private Integer parseInteger( String s ) { if ( s == null ) { return null; } try { return new Integer( s ); } catch ( NumberFormatException e ) { return null; } } }