com.smartitengineering.cms.api.impl.content.ContentLoaderImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.smartitengineering.cms.api.impl.content.ContentLoaderImpl.java

Source

/*
 *
 * This is a simple Content Management System (CMS)
 * Copyright (C) 2010  Imran M Yousuf (imyousuf@smartitengineering.com)
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.smartitengineering.cms.api.impl.content;

import com.smartitengineering.cms.api.content.BooleanFieldValue;
import com.smartitengineering.cms.api.content.CollectionFieldValue;
import com.smartitengineering.cms.api.content.CompositeFieldValue;
import com.smartitengineering.cms.api.content.Content;
import com.smartitengineering.cms.api.content.ContentFieldValue;
import com.smartitengineering.cms.api.content.ContentId;
import com.smartitengineering.cms.api.content.FieldValue;
import com.smartitengineering.cms.api.content.MutableCompositeFieldValue;
import com.smartitengineering.cms.api.content.MutableRepresentation;
import com.smartitengineering.cms.api.content.MutableVariation;
import com.smartitengineering.cms.api.content.Variation;
import com.smartitengineering.cms.api.factory.content.ContentLoader;
import com.smartitengineering.cms.api.content.DateTimeFieldValue;
import com.smartitengineering.cms.api.content.Field;
import com.smartitengineering.cms.api.content.Filter;
import com.smartitengineering.cms.api.content.MutableBooleanFieldValue;
import com.smartitengineering.cms.api.content.MutableCollectionFieldValue;
import com.smartitengineering.cms.api.content.MutableContentFieldValue;
import com.smartitengineering.cms.api.content.MutableDateTimeFieldValue;
import com.smartitengineering.cms.api.content.MutableField;
import com.smartitengineering.cms.api.content.MutableNumberFieldValue;
import com.smartitengineering.cms.api.content.MutableOtherFieldValue;
import com.smartitengineering.cms.api.content.MutableStringFieldValue;
import com.smartitengineering.cms.api.content.NumberFieldValue;
import com.smartitengineering.cms.api.content.OtherFieldValue;
import com.smartitengineering.cms.api.common.SearchResult;
import com.smartitengineering.cms.api.content.StringFieldValue;
import com.smartitengineering.cms.api.factory.content.WriteableContent;
import com.smartitengineering.cms.api.factory.write.Lock;
import com.smartitengineering.cms.api.impl.type.CompositionDataTypeImpl.EmbeddedContentDataTypeImpl;
import com.smartitengineering.cms.api.type.CollectionDataType;
import com.smartitengineering.cms.api.type.CompositeDataType;
import com.smartitengineering.cms.api.type.CompositeDataType.EmbeddedContentDataType;
import com.smartitengineering.cms.api.type.ContentDataType;
import com.smartitengineering.cms.api.type.ContentType;
import com.smartitengineering.cms.api.type.ContentTypeId;
import com.smartitengineering.cms.api.type.DataType;
import com.smartitengineering.cms.api.type.EnumDataType;
import com.smartitengineering.cms.api.type.FieldDef;
import com.smartitengineering.cms.api.type.FieldValueType;
import com.smartitengineering.cms.api.workspace.WorkspaceId;
import com.smartitengineering.cms.spi.SmartContentSPI;
import com.smartitengineering.cms.spi.content.PersistableContent;
import com.smartitengineering.dao.common.queryparam.QueryParameter;
import com.smartitengineering.dao.common.queryparam.QueryParameterFactory;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author kaisar
 */
public class ContentLoaderImpl implements ContentLoader {

    protected final transient Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public MutableField createMutableField(ContentId contentId, FieldDef fieldDef) {
        if (fieldDef != null) {
            FieldImpl fieldImpl = new FieldImpl();
            fieldImpl.setFieldDef(fieldDef);
            fieldImpl.setName(fieldDef.getName());
            fieldImpl.setContent(contentId);
            return fieldImpl;
        } else {
            throw new IllegalArgumentException("Argument can not be null.");

        }
    }

    @Override
    public MutableField createMutableField(Field field) {
        if (field.getFieldDef() != null) {
            FieldImpl fieldImpl = new FieldImpl();
            fieldImpl.setName(field.getName());
            fieldImpl.setValue(field.getValue());
            fieldImpl.setFieldDef(field.getFieldDef());
            return fieldImpl;
        } else {
            throw new IllegalArgumentException("FieldDef can not be null.");
        }
    }

    @Override
    public MutableDateTimeFieldValue createDateTimeFieldValue(DateTimeFieldValue fieldValue) {
        if (fieldValue.getValue() != null) {
            DateTimeFieldValueImpl dateTimeFieldValueImpl = new DateTimeFieldValueImpl();
            dateTimeFieldValueImpl.setValue(fieldValue.getValue());
            return dateTimeFieldValueImpl;
        } else {
            throw new IllegalArgumentException("Argument can not be null.");
        }
    }

    @Override
    public MutableBooleanFieldValue createBooleanFieldValue(BooleanFieldValue fieldValue) {
        BooleanFieldValueImpl booleanFieldValueImpl = new BooleanFieldValueImpl();
        booleanFieldValueImpl.setValue(fieldValue.getValue());
        return booleanFieldValueImpl;
    }

    @Override
    public MutableCollectionFieldValue createCollectionFieldValue(CollectionFieldValue fieldValue) {
        if (fieldValue.getValue() != null) {
            CollectionFieldValueImpl collectionFieldValueImpl = new CollectionFieldValueImpl();
            collectionFieldValueImpl.setValue(fieldValue.getValue());
            return collectionFieldValueImpl;
        } else {
            throw new IllegalArgumentException("Argument can not be null.");
        }
    }

    public MutableCompositeFieldValue createCompositeFieldValue(CompositeFieldValue fieldValue) {
        if (fieldValue != null) {
            CompositeFieldValueImpl valueImpl = new CompositeFieldValueImpl();
            if (fieldValue.getValue() != null) {
                valueImpl.setValue(fieldValue.getValue());
            }
            return valueImpl;
        } else {
            throw new IllegalArgumentException("Argument can not be null.");
        }
    }

    @Override
    public MutableContentFieldValue createContentFieldValue(ContentFieldValue fieldValue) {
        if (fieldValue.getValue() != null) {
            ContentFieldValueImpl contentFieldValueImpl = new ContentFieldValueImpl();
            contentFieldValueImpl.setValue(fieldValue.getValue());
            return contentFieldValueImpl;
        } else {
            throw new IllegalArgumentException("Argument can not be null.");
        }
    }

    @Override
    public MutableNumberFieldValue createNumberFieldValue(NumberFieldValue fieldValue) {
        if (fieldValue.getValue() != null) {
            NumberFieldValueImpl fieldValueImpl = new NumberFieldValueImpl();
            fieldValueImpl.setValue(fieldValue.getValue());
            fieldValueImpl.setFieldValueType(fieldValue.getDataType());
            return fieldValueImpl;
        } else {
            throw new IllegalArgumentException("Argument can not be null.");
        }
    }

    @Override
    public MutableOtherFieldValue createOtherFieldValue(OtherFieldValue fieldValue) {
        if (fieldValue.getValue() != null) {
            OtherFieldValueImpl otherFieldValueImpl = new OtherFieldValueImpl();
            otherFieldValueImpl.setValue(fieldValue.getValue());
            return otherFieldValueImpl;
        } else {
            throw new IllegalArgumentException("Argument can not be null.");
        }
    }

    @Override
    public MutableStringFieldValue createStringFieldValue(StringFieldValue fieldValue) {
        if (fieldValue.getValue() != null) {
            StringFieldValueImpl stringFieldValueImpl = new StringFieldValueImpl();
            stringFieldValueImpl.setValue(fieldValue.getValue());
            return stringFieldValueImpl;
        } else {
            throw new IllegalArgumentException("Argument can not be null.");
        }
    }

    @Override
    public ContentId createContentId(WorkspaceId workspaceId, byte[] id) {
        if (workspaceId != null && id != null) {
            ContentIdImpl contentIdImpl = new ContentIdImpl();
            contentIdImpl.setId(id);
            contentIdImpl.setWorkspaceId(workspaceId);
            return contentIdImpl;
        } else {
            throw new IllegalArgumentException("Argument can not be null.");
        }
    }

    @Override
    public Content loadContent(ContentId contentId) {
        final Collection<Content> contents = SmartContentSPI.getInstance().getContentReader()
                .readContentsFromPersistentStorage(contentId);
        if (contents == null || contents.isEmpty()) {
            return null;
        }
        return contents.iterator().next();
    }

    @Override
    public Filter craeteFilter() {
        return new FilterImpl();
    }

    @Override
    public SearchResult<Content> search(Filter filter) {
        return SmartContentSPI.getInstance().getContentSearcher().search(filter);
    }

    @Override
    public String escapeStringForSearch(String string) {
        return SmartContentSPI.getInstance().getContentSearcher().escapeStringForSearch(string);
    }

    @Override
    public MutableDateTimeFieldValue createDateTimeFieldValue() {
        final DateTimeFieldValueImpl dateTimeFieldValueImpl = new DateTimeFieldValueImpl();
        dateTimeFieldValueImpl.setFieldValueType(FieldValueType.DATE_TIME);
        return dateTimeFieldValueImpl;
    }

    @Override
    public MutableBooleanFieldValue createBooleanFieldValue() {
        final BooleanFieldValueImpl booleanFieldValueImpl = new BooleanFieldValueImpl();
        booleanFieldValueImpl.setFieldValueType(FieldValueType.BOOLEAN);
        return booleanFieldValueImpl;
    }

    @Override
    public MutableCollectionFieldValue createCollectionFieldValue() {
        final CollectionFieldValueImpl collectionFieldValueImpl = new CollectionFieldValueImpl();
        collectionFieldValueImpl.setFieldValueType(FieldValueType.COLLECTION);
        return collectionFieldValueImpl;
    }

    public MutableCompositeFieldValue createCompositeFieldValue() {
        return new CompositeFieldValueImpl();
    }

    @Override
    public MutableContentFieldValue createContentFieldValue() {
        final ContentFieldValueImpl contentFieldValueImpl = new ContentFieldValueImpl();
        contentFieldValueImpl.setFieldValueType(FieldValueType.CONTENT);
        return contentFieldValueImpl;
    }

    @Override
    public MutableNumberFieldValue createDoubleFieldValue() {
        final NumberFieldValueImpl numberFieldValueImpl = new NumberFieldValueImpl();
        numberFieldValueImpl.setFieldValueType(FieldValueType.DOUBLE);
        return numberFieldValueImpl;
    }

    @Override
    public MutableNumberFieldValue createIntegerFieldValue() {
        final NumberFieldValueImpl numberFieldValueImpl = new NumberFieldValueImpl();
        numberFieldValueImpl.setFieldValueType(FieldValueType.INTEGER);
        return numberFieldValueImpl;
    }

    @Override
    public MutableNumberFieldValue createLongFieldValue() {
        final NumberFieldValueImpl numberFieldValueImpl = new NumberFieldValueImpl();
        numberFieldValueImpl.setFieldValueType(FieldValueType.LONG);
        return numberFieldValueImpl;
    }

    @Override
    public MutableOtherFieldValue createOtherFieldValue() {
        final OtherFieldValueImpl otherFieldValueImpl = new OtherFieldValueImpl();
        otherFieldValueImpl.setFieldValueType(FieldValueType.OTHER);
        return otherFieldValueImpl;
    }

    @Override
    public MutableStringFieldValue createStringFieldValue() {
        final StringFieldValueImpl stringFieldValueImpl = new StringFieldValueImpl();
        stringFieldValueImpl.setFieldValueType(FieldValueType.STRING);
        return stringFieldValueImpl;
    }

    @Override
    public Variation getVariation(Content content, Field field, String name) {
        return SmartContentSPI.getInstance().getVariationProvider().getVariation(name, content, field);
    }

    @Override
    public WriteableContent createContent(ContentType contentType) {
        return createContent(contentType, false);
    }

    @Override
    public WriteableContent createContent(ContentType contentType, boolean supressChecking) {
        PersistableContent content = SmartContentSPI.getInstance().getPersistableDomainFactory()
                .createPersistableContent(supressChecking);
        content.setContentDefinition(contentType);
        return content;
    }

    @Override
    public WriteableContent getWritableContent(Content content) {
        if (content instanceof WriteableContent) {
            return (WriteableContent) content;
        } else {
            return getWritableContent(content, false);
        }
    }

    @Override
    public WriteableContent getWritableContent(Content content, boolean supressChecking) {
        return getWritableContent(content, null, supressChecking);
    }

    public WriteableContent getWritableContent(Content content, Lock encapsulatingLock) {
        if (content instanceof PersistableContent) {
            final PersistableContent mutableContent = (PersistableContent) content;
            if (encapsulatingLock != null) {
                mutableContent.setLock(encapsulatingLock);
            }
            return mutableContent;
        } else {
            return getWritableContent(content, encapsulatingLock, false);
        }
    }

    public WriteableContent getWritableContent(Content content, Lock encapsulatingLock, boolean supressChecking) {
        final PersistableContent mutableContent;
        mutableContent = SmartContentSPI.getInstance().getPersistableDomainFactory()
                .createPersistableContent(supressChecking);
        mutableContent.setContentDefinition(content.getContentDefinition());
        mutableContent.setContentId(content.getContentId());
        mutableContent.setCreationDate(content.getCreationDate());
        mutableContent.setLastModifiedDate(content.getLastModifiedDate());
        mutableContent.setParentId(content.getParentId());
        mutableContent.setStatus(content.getStatus());
        for (Field field : content.getFields().values()) {
            mutableContent.setField(field);
        }
        if (encapsulatingLock != null) {
            mutableContent.setLock(encapsulatingLock);
        }
        return mutableContent;

    }

    @Override
    public FieldValue getValueFor(String value, DataType dataType) {
        final FieldValue result;
        final FieldValueType type = dataType.getType();
        if (logger.isDebugEnabled()) {
            logger.debug("Getting field value as " + type.name() + " from " + value);
        }
        switch (type) {
        case COMPOSITE:
            logger.debug("Getting as composite");
            MutableCompositeFieldValue compositeFieldValue = createCompositeFieldValue();
            try {
                JsonNode node = CollectionFieldValueImpl.MAPPER.readTree(value);
                if (node instanceof ObjectNode) {
                    ObjectNode objectNode = (ObjectNode) node;
                    CompositeDataType compositeDataType = (CompositeDataType) dataType;
                    Map<String, FieldDef> defs = compositeDataType.getComposedFieldDefs();
                    Collection<Field> compositeValues = new ArrayList<Field>(defs.size());
                    for (Entry<String, FieldDef> def : defs.entrySet()) {
                        final JsonNode fNode = objectNode.get(def.getKey());
                        final DataType itemDataType = def.getValue().getValueDef();
                        final String textValue;
                        if (fNode != null) {
                            if (itemDataType.getType().equals(FieldValueType.COLLECTION)
                                    || itemDataType.getType().equals(FieldValueType.COMPOSITE)) {
                                textValue = fNode.toString();
                            } else {
                                textValue = fNode.getTextValue();
                            }
                        } else {
                            textValue = "";
                        }
                        final FieldValue valueFor = org.apache.commons.lang.StringUtils.isNotBlank(textValue)
                                ? getValueFor(textValue, itemDataType)
                                : null;
                        final MutableField mutableField = createMutableField(null, def.getValue());
                        mutableField.setValue(valueFor);
                        compositeValues.add(mutableField);
                    }
                    compositeFieldValue.setValue(compositeValues);
                } else {
                    throw new IllegalStateException("Collection must be of array of strings!");
                }
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
            result = compositeFieldValue;
            break;
        case COLLECTION:
            logger.debug("Getting as collection");
            MutableCollectionFieldValue collectionFieldValue = createCollectionFieldValue();
            try {
                JsonNode node = CollectionFieldValueImpl.MAPPER.readTree(value);
                if (node instanceof ArrayNode) {
                    ArrayNode arrayNode = (ArrayNode) node;
                    int size = arrayNode.size();
                    ArrayList<FieldValue> values = new ArrayList<FieldValue>(size);
                    for (int i = 0; i < size; ++i) {
                        final DataType itemDataType = ((CollectionDataType) dataType).getItemDataType();
                        if (itemDataType.getType().equals(FieldValueType.COMPOSITE)) {
                            final String stringValue = arrayNode.get(i).toString();
                            values.add(getValueFor(stringValue, itemDataType));
                        } else {
                            final String stringValue = arrayNode.get(i).getTextValue();
                            values.add(getValueFor(stringValue, itemDataType));
                        }
                    }
                    collectionFieldValue.setValue(values);
                } else {
                    throw new IllegalStateException("Collection must be of array of strings!");
                }
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
            result = collectionFieldValue;
            break;
        default:
            logger.debug("Getting as all other than collection");
            result = getSimpleValueFor(value, type);
        }
        return result;
    }

    public FieldValue getSimpleValueFor(String value, FieldValueType type) {
        final FieldValue result;
        switch (type) {
        case BOOLEAN:
            logger.debug("Getting as boolean");
            MutableBooleanFieldValue booleanFieldValue = createBooleanFieldValue();
            result = booleanFieldValue;
            booleanFieldValue.setValue(Boolean.parseBoolean(value));
            break;
        case CONTENT:
            logger.debug("Getting as content");
            MutableContentFieldValue contentFieldValue = createContentFieldValue();
            if (logger.isDebugEnabled()) {
                logger.debug("Content value: " + value);
            }
            try {
                DataInputStream inputStream = new DataInputStream(
                        new ByteArrayInputStream(StringUtils.getBytesUtf8(value)));
                ContentIdImpl idImpl = new ContentIdImpl();
                idImpl.readExternal(inputStream);
                contentFieldValue.setValue(idImpl);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
            result = contentFieldValue;
            break;
        case INTEGER:
            logger.debug("Getting as integer");
            MutableNumberFieldValue integerFieldValue = createIntegerFieldValue();
            integerFieldValue.setValue(NumberUtils.toInt(value, Integer.MIN_VALUE));
            result = integerFieldValue;
            break;
        case DOUBLE:
            logger.debug("Getting as double");
            MutableNumberFieldValue doubleFieldValue = createDoubleFieldValue();
            doubleFieldValue.setValue(NumberUtils.toDouble(value, Double.MIN_VALUE));
            result = doubleFieldValue;
            break;
        case LONG:
            logger.debug("Getting as long");
            MutableNumberFieldValue longFieldValue = createLongFieldValue();
            longFieldValue.setValue(NumberUtils.toLong(value, Long.MIN_VALUE));
            result = longFieldValue;
            break;
        case DATE_TIME:
            logger.debug("Getting as date time");
            MutableDateTimeFieldValue valueOf;
            try {
                valueOf = DateTimeFieldValueImpl.valueOf(value);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
            result = valueOf;
            break;
        case OTHER:
            logger.debug("Getting as other");
            MutableOtherFieldValue otherFieldValue = createOtherFieldValue();
            otherFieldValue.setValue(Base64.decodeBase64(value));
            result = otherFieldValue;
            break;
        case STRING:
        default:
            logger.debug("Getting as else or string");
            MutableStringFieldValue fieldValue = createStringFieldValue();
            fieldValue.setValue(value);
            result = fieldValue;
        }
        return result;
    }

    @Override
    public ContentId generateContentId(WorkspaceId workspaceId) {
        UUID uid = UUID.randomUUID();
        return createContentId(workspaceId, StringUtils.getBytesUtf8(uid.toString()));
    }

    @Override
    public String getEntityTagValueForContent(Content content) {
        return DigestUtils.md5Hex(new StringBuilder(
                DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(content.getLastModifiedDate())).append('~')
                        .append(content.getOwnFields().toString()).append('~').append(content.getStatus())
                        .toString());
    }

    @Override
    public MutableRepresentation createMutableRepresentation(ContentId contentId) {
        return new RepresentationImpl(contentId);
    }

    @Override
    public MutableVariation createMutableVariation(ContentId contentId, FieldDef fieldDef) {
        return new VariationImpl(contentId, fieldDef);
    }

    @Override
    public boolean isValidContent(Content content) {
        if (!isValid(content)) {
            return false;
        }
        if (!checkForImpliedValidations(content)) {
            return false;
        }
        if (!isValidByCustomValidators(content)) {
            return false;
        }
        return true;
    }

    protected boolean isValid(Content content) {
        if (logger.isDebugEnabled()) {
            logger.debug("Content: " + content);
            if (content != null) {
                logger.debug("Content ID: " + content.getContentId());
                logger.debug("Content Definition: " + content.getContentDefinition());
                if (content.getContentDefinition() != null) {
                    logger.debug("Required fields present: " + isMandatoryFieldsPresent(content));
                }
            }
        }
        if (logger.isWarnEnabled()
                && !(content != null && content.getContentId() != null && content.getContentDefinition() != null)) {
            logger.warn("Content or its ID or content definition is missing!");
        }
        return content != null
                && content.getContentId() != null && content.getContentDefinition() != null && content
                        .getContentDefinition().getDefinitionType().equals(ContentType.DefinitionType.CONCRETE_TYPE)
                && isMandatoryFieldsPresent(content);
    }

    protected boolean isMandatoryFieldsPresent(Content content) {
        ContentType type = content.getContentDefinition();
        return isMandatoryFieldsPresent(type.getFieldDefs().values(), content.getFields());
    }

    protected boolean isMandatoryFieldsPresent(Collection<FieldDef> defs, Map<String, Field> fields) {
        boolean valid = true;
        for (FieldDef def : defs) {
            if (!valid) {
                logger.debug("Skipping field as already invalid! - " + def.getName());
                continue;
            }
            final Field cField = fields.get(def.getName());
            if (logger.isDebugEnabled()) {
                logger.debug(def.getName() + " is required: " + def.isRequired());
                logger.debug(def.getName() + ": " + cField);
                if (cField != null) {
                    logger.debug(def.getName() + ": " + cField.getValue());
                    if (cField.getValue() != null) {
                        logger.debug(def.getName() + ": " + cField.getValue().getValue());
                    }
                }
            }
            if (def.isRequired()
                    && (cField == null || cField.getValue() == null || cField.getValue().getValue() == null)) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Required field not present " + def.getName());
                }
                valid = false;
            }
            if (valid) {
                if (def.getValueDef().getType().equals(FieldValueType.COMPOSITE)) {
                    final Collection<FieldDef> nestedDefs;
                    final Map<String, Field> nestedFields;
                    nestedDefs = ((CompositeDataType) def.getValueDef()).getComposition();
                    if (fields.containsKey(def.getName()) && cField != null) {
                        Field myField = cField;
                        CompositeFieldValue compositeFieldValue = (CompositeFieldValue) myField.getValue();
                        if (compositeFieldValue != null) {
                            nestedFields = compositeFieldValue.getValueAsMap();
                        } else {
                            nestedFields = null;
                        }
                    } else {
                        nestedFields = null;
                    }
                    if (nestedDefs != null && nestedFields != null) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Going to validate collection's item mandatory fields from "
                                    + cField.getName());
                        }
                        valid = isMandatoryFieldsPresent(nestedDefs, nestedFields);
                    }
                } else if (def.getValueDef().getType().equals(FieldValueType.COLLECTION)
                        && ((CollectionDataType) def.getValueDef()).getItemDataType().getType()
                                .equals(FieldValueType.COMPOSITE)) {
                    final Collection<FieldDef> nestedDefs;
                    nestedDefs = ((CompositeDataType) ((CollectionDataType) def.getValueDef()).getItemDataType())
                            .getComposition();
                    if (fields.containsKey(def.getName()) && cField != null) {
                        final Field myField = cField;
                        CollectionFieldValue collectionFieldValue = (CollectionFieldValue) myField.getValue();
                        if (collectionFieldValue != null) {
                            Collection<FieldValue> values = collectionFieldValue.getValue();
                            if (values != null) {
                                for (FieldValue fieldValue : values) {
                                    if (!valid) {
                                        continue;
                                    }
                                    CompositeFieldValue compositeFieldValue = (CompositeFieldValue) fieldValue;
                                    final Map<String, Field> nestedFields;
                                    if (compositeFieldValue != null) {
                                        nestedFields = compositeFieldValue.getValueAsMap();
                                    } else {
                                        nestedFields = null;
                                    }
                                    if (nestedDefs != null && nestedFields != null) {
                                        if (logger.isDebugEnabled()) {
                                            logger.debug(
                                                    "Going to validate collection's item mandatory fields from "
                                                            + myField.getName());
                                        }
                                        valid = valid && isMandatoryFieldsPresent(nestedDefs, nestedFields);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return valid;
    }

    protected boolean isValidByCustomValidators(Content content) {
        final Collection<Field> values = content.getFields().values();
        return isValidByCustomValidators(values, content);
    }

    protected boolean isValidByCustomValidators(final Collection<Field> values, final Content content) {
        boolean valid = true;
        for (Field field : values) {
            if (!valid) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping field as already invalid! - " + field.getName());
                }
                continue;
            }
            if (field == null || field.getFieldDef() == null || field.getValue() == null) {
                if (field != null && logger.isWarnEnabled()) {
                    if (field.getFieldDef() != null) {
                        logger.warn("The field def is null for " + field.getName());
                    } else if (field.getValue() != null) {
                        logger.warn("The field value is null for " + field.getName());
                    }
                } else {
                    logger.warn("Field is NULL");
                }
                continue;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Attempting custom validation for " + field.getName());
            }
            FieldDef def = field.getFieldDef();
            if (def.getValueDef().getType().equals(FieldValueType.COMPOSITE)) {
                final Map<String, Field> nestedFields;
                CompositeFieldValue compositeFieldValue = (CompositeFieldValue) field.getValue();
                if (compositeFieldValue != null) {
                    nestedFields = compositeFieldValue.getValueAsMap();
                } else {
                    nestedFields = null;
                }
                if (nestedFields != null) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Going to validate composite fields from " + field.getName());
                    }
                    valid = isValidByCustomValidators(nestedFields.values(), content);
                }
            } else if (def.getValueDef().getType().equals(FieldValueType.COLLECTION)
                    && ((CollectionDataType) def.getValueDef()).getItemDataType().getType()
                            .equals(FieldValueType.COMPOSITE)) {
                CollectionFieldValue collectionFieldValue = (CollectionFieldValue) field.getValue();
                Collection<FieldValue> collectionValues = collectionFieldValue.getValue();
                if (collectionValues != null) {
                    for (FieldValue fieldValue : collectionValues) {
                        if (!valid) {
                            logger.debug("Skipping iteration over collection field as already invalid! - "
                                    + field.getName());
                            continue;
                        }
                        CompositeFieldValue compositeFieldValue = (CompositeFieldValue) fieldValue;
                        final Map<String, Field> nestedFields;
                        if (compositeFieldValue != null) {
                            nestedFields = compositeFieldValue.getValueAsMap();
                        } else {
                            nestedFields = null;
                        }
                        if (nestedFields != null) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Going to validate colletion's item composite fields from "
                                        + field.getName());
                            }
                            valid = isValidByCustomValidators(nestedFields.values(), content);
                        }
                    }
                }
            }
            if (valid) {
                //Check current field validation after the nested fields are checked
                final boolean validField = SmartContentSPI.getInstance().getValidatorProvider()
                        .isValidField(content, field);
                if (!validField && logger.isWarnEnabled()) {
                    logger.warn("Custom field validation failed for " + field.getName());
                }
                valid = validField;
            }
        }
        return valid;
    }

    protected void addQueryForContentId(final ContentId contentId, ContentTypeId instanceOfId, Filter filter) {
        QueryParameter<String> param = QueryParameterFactory.getStringLikePropertyParam("id",
                new StringBuilder().append(contentId.toString()).toString());
        filter.addFieldFilter(param);
        filter.addContentTypeToFilter(instanceOfId);
    }

    protected boolean checkForImpliedValidations(Collection<Field> fields) {
        boolean valid = true;
        for (Field field : fields) {
            if (field != null && field.getFieldDef() != null && field.getValue() != null) {
                FieldDef def = field.getFieldDef();
                switch (def.getValueDef().getType()) {
                case COLLECTION:
                    final CollectionDataType collDataType = (CollectionDataType) def.getValueDef();
                    if (collDataType.getItemDataType().getType().equals(FieldValueType.CONTENT)) {
                        DataType contentDataType = collDataType.getItemDataType();
                        for (FieldValue val : ((CollectionFieldValue) field.getValue()).getValue()) {
                            final boolean checkContentTypeValidity = checkContentTypeValidity(val, contentDataType);
                            if (!checkContentTypeValidity && logger.isWarnEnabled()) {
                                logger.warn("Content relation failed in " + field.getName());
                            }
                            valid = valid && checkContentTypeValidity;
                        }
                    } else if (collDataType.getItemDataType().getType().equals(FieldValueType.COMPOSITE)) {
                        for (FieldValue val : ((CollectionFieldValue) field.getValue()).getValue()) {
                            final boolean checkForEnumValidity = checkForImpliedValidations(
                                    ((CompositeFieldValue) val).getFields().values());
                            if (!checkForEnumValidity && logger.isWarnEnabled()) {
                                logger.warn("Composite relation check in collection failed for " + field.getName());
                            }
                            valid = valid && checkForEnumValidity;
                        }
                    } else if (collDataType.getItemDataType().getType().equals(FieldValueType.ENUM)) {
                        EnumDataType enumDataType = (EnumDataType) collDataType.getItemDataType();
                        Collection<String> choices = enumDataType.getChoices();
                        for (FieldValue val : ((CollectionFieldValue) field.getValue()).getValue()) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Enum field value instance of string field value "
                                        + (val instanceof StringFieldValue));
                            }
                            if (val instanceof StringFieldValue) {
                                String strval = ((StringFieldValue) val).getValue();
                                final boolean checkForEnumValidity = choices.contains(strval);
                                if (!checkForEnumValidity && logger.isWarnEnabled()) {
                                    logger.warn("Enum value in collection failed for " + field.getName());
                                }
                                valid = valid && checkForEnumValidity;
                            }
                        }
                    }
                    break;
                case CONTENT: {
                    final boolean checkContentTypeValidity = checkContentTypeValidity(field.getValue(),
                            def.getValueDef());
                    if (!checkContentTypeValidity && logger.isWarnEnabled()) {
                        logger.warn("Content relation failed in " + field.getName());
                    }
                    valid = valid && checkContentTypeValidity;
                }
                    break;
                case COMPOSITE: {
                    final boolean checkForEnumValidity = checkForImpliedValidations(
                            ((CompositeFieldValue) field.getValue()).getFields().values());
                    if (!checkForEnumValidity && logger.isWarnEnabled()) {
                        logger.warn("Composite relation check in collection failed for " + field.getName());
                    }
                    valid = valid && checkForEnumValidity;
                }
                case ENUM: {
                    if (field.getValue() instanceof StringFieldValue) {
                        Collection<String> choices = ((EnumDataType) def.getValueDef()).getChoices();
                        String strval = ((StringFieldValue) field.getValue()).getValue();
                        final boolean checkForEnumValidity = choices.contains(strval);
                        if (!checkForEnumValidity && logger.isWarnEnabled()) {
                            logger.warn("Enum value failed for " + field.getName());
                        }
                        valid = valid && checkForEnumValidity;
                    }
                }
                    break;
                default:
                    break;
                }
            }
            if (!valid) {
                return valid;
            }
        }
        return valid;
    }

    protected boolean checkForImpliedValidations(Content content) {
        final Collection<Field> values = content.getFields().values();
        return checkForImpliedValidations(values);
    }

    protected boolean checkContentTypeValidity(FieldValue val, DataType contentDataType) {
        Filter filter = craeteFilter();
        addQueryForContentId(((ContentFieldValue) val).getValue(), ((ContentDataType) contentDataType).getTypeDef(),
                filter);
        filter.setDisjunction(false);
        Collection<Content> contents = search(filter).getResult();
        return !contents.isEmpty();
    }

    @Override
    public SearchResult<Content> createSearchResult(Collection<Content> result, long totalResultsCount) {
        SearchResultImpl<Content> resultImpl = new SearchResultImpl<Content>();
        resultImpl.setResult(result);
        resultImpl.setTotalResultsCount(totalResultsCount);
        return resultImpl;
    }

    @Override
    public void reIndex(ContentId contentId) {
        SmartContentSPI.getInstance().getContentSearcher().reIndex(contentId);
    }

    @Override
    public void reIndex(WorkspaceId workspaceId) {
        SmartContentSPI.getInstance().getContentSearcher().reIndex(workspaceId);
    }

    @Override
    public ContentId parseContentId(String contentIdStr) {
        byte[] idBytes = org.apache.commons.codec.binary.StringUtils.getBytesUtf8(contentIdStr);
        DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(idBytes));
        ContentIdImpl idImpl = new ContentIdImpl();
        try {
            idImpl.readExternal(inputStream);
        } catch (Exception ex) {
            logger.warn("Could not parse content id string " + contentIdStr, ex);
            return null;
        }
        return idImpl;
    }

    public EmbeddedContentDataType createEmbeddedContentDataType(FieldDef def, ContentDataType type) {
        return new EmbeddedContentDataTypeImpl(def, type);
    }
}