/*
*
* 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.Content;
import com.smartitengineering.cms.api.content.ContentId;
import com.smartitengineering.cms.api.content.Field;
import com.smartitengineering.cms.api.content.Representation;
import com.smartitengineering.cms.api.content.FieldValue;
import com.smartitengineering.cms.api.content.template.ContentCoProcessor;
import com.smartitengineering.cms.api.event.Event;
import com.smartitengineering.cms.api.event.Event.EventType;
import com.smartitengineering.cms.api.event.Event.Type;
import com.smartitengineering.cms.api.factory.SmartContentAPI;
import com.smartitengineering.cms.api.factory.content.WriteableContent;
import com.smartitengineering.cms.api.impl.AbstractPersistableDomain;
import com.smartitengineering.cms.api.type.ContentCoProcessorDef;
import com.smartitengineering.cms.api.type.ContentStatus;
import com.smartitengineering.cms.api.type.ContentType;
import com.smartitengineering.cms.api.type.ContentType.ContentProcessingPhase;
import com.smartitengineering.cms.api.type.FieldDef;
import com.smartitengineering.cms.api.workspace.Workspace;
import com.smartitengineering.cms.api.workspace.WorkspaceId;
import com.smartitengineering.cms.spi.SmartContentSPI;
import com.smartitengineering.cms.spi.content.PersistableContent;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.lang.ObjectUtils;
/**
*
* @author kaisar
*/
public class ContentImpl extends AbstractPersistableDomain<WriteableContent, ContentId> implements PersistableContent {
private ContentId contentId;
private ContentId parentId;
private ContentType contentDef;
private ContentStatus contentStatus;
private Date creationDate;
private Date lastModifiedDate;
private Map<String, Field> map;
private Map<String, Field> cachedFieldMap;
private String entityTagValue;
private boolean privateContent;
private boolean supressChecking;
public ContentImpl() {
super(LOCK_KEY_PREFIX);
}
public boolean isSupressChecking() {
return supressChecking;
}
public void setSupressChecking(boolean supressChecking) {
this.supressChecking = supressChecking;
}
@Override
public void put() throws IOException {
final boolean attainedLockLocally;
if (isPersisted() && !isLockOwned()) {
lock();
attainedLockLocally = true;
}
else {
attainedLockLocally = false;
}
try {
triggerContentCoProcessors(ContentType.ContentProcessingPhase.WRITE);
if (!isValid()) {
getLogger().info("Content not in valid state!");
//First get contents indexed before attempting to use this validity!
//Uncomment the following line once indexing is ensured in testing
throw new IOException("Content is not in valid state!");
}
super.put();
}
finally {
if (attainedLockLocally) {
unlock();
}
}
}
protected void triggerContentCoProcessors(final ContentProcessingPhase phase) {
//Trigger content co processors
if (contentDef != null && contentId != null) {
Collection<ContentCoProcessorDef> defs = contentDef.getContentCoProcessorDefs().get(
phase);
if (defs != null && !defs.isEmpty()) {
List<ContentCoProcessorDef> list = new ArrayList<ContentCoProcessorDef>(defs);
Collections.sort(list, new Comparator<ContentCoProcessorDef>() {
public int compare(ContentCoProcessorDef o1, ContentCoProcessorDef o2) {
return o1.getPriority() - o2.getPriority();
}
});
for (ContentCoProcessorDef def : list) {
ContentCoProcessor processor = SmartContentAPI.getInstance().getWorkspaceApi().getContentCoProcessor(contentId.
getWorkspaceId(), def.getResourceUri().getValue());
if (processor != null) {
processor.processContent(this, def.getParameters());
}
}
}
}
}
@Override
public void setParentId(ContentId contentId) {
if (contentDef == null) {
throw new IllegalArgumentException("Content Type Definition must be set before setting parent content ID");
}
if (contentId != null && SmartContentAPI.getInstance().getContentLoader().loadContent(contentId) == null) {
throw new IllegalArgumentException("Parent must exist for it to be set!");
}
this.parentId = contentId;
}
@Override
public void setContentDefinition(ContentType contentType) {
this.contentDef = contentType;
}
@Override
public void setField(Field field) {
getMap().put(field.getName(), field);
cachedFieldMap = null;
}
@Override
public void setStatus(ContentStatus contentStatus) {
this.contentStatus = contentStatus;
}
@Override
public ContentId getContentId() {
return this.contentId;
}
@Override
public ContentId getParentId() {
return this.parentId;
}
@Override
public Content getParent() {
if (parentId != null) {
return SmartContentAPI.getInstance().getContentLoader().loadContent(parentId);
}
else {
return null;
}
}
@Override
public ContentType getContentDefinition() {
return this.contentDef;
}
@Override
public Map<String, Field> getFields() {
final Content parent = getParent();
final ContentType def = getContentDefinition();
if (cachedFieldMap == null) {
Map<String, Field> fields = new LinkedHashMap<String, Field>();
if (parent != null && def != null) {
Map<String, Field> parentFields = parent.getFields();
for (String fieldName : parentFields.keySet()) {
FieldDef myDef = def.getOwnFieldDefs().get(fieldName);
FieldDef thatDef = parent.getContentDefinition().getOwnFieldDefs().get(fieldName);
if (myDef != null && thatDef != null && ObjectUtils.equals(myDef, thatDef) && myDef.getValueDef().getType().
equals(thatDef.getValueDef().getType())) {
fields.put(fieldName, parent.getField(fieldName));
}
}
}
fields.putAll(getMap());
cachedFieldMap = fields;
}
return Collections.unmodifiableMap(cachedFieldMap);
}
public Map<String, Field> getMap() {
if (map == null) {
map = new LinkedHashMap<String, Field>(SmartContentSPI.getInstance().getContentReader().getFieldsForContent(
contentId));
triggerContentCoProcessors(ContentType.ContentProcessingPhase.READ);
}
return map;
}
@Override
public Map<String, Field> getOwnFields() {
return Collections.unmodifiableMap(getMap());
}
@Override
public Field getField(String fieldName) {
return getFields().get(fieldName);
}
@Override
public ContentStatus getStatus() {
return this.contentStatus;
}
@Override
public Representation getRepresentation(String repName) {
return SmartContentSPI.getInstance().getRepresentationProvider().getRepresentation(repName, getContentDefinition(),
this);
}
@Override
public Date getCreationDate() {
return this.creationDate;
}
@Override
public Date getLastModifiedDate() {
return this.lastModifiedDate;
}
@Override
public boolean isPersisted() {
return creationDate != null;
}
@Override
public ContentId getKeySpecimen() {
return contentId;
}
@Override
public void removeField(String fieldName) {
getMap().remove(fieldName);
}
@Override
public void setContentId(ContentId contentId) {
this.contentId = contentId;
}
@Override
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
@Override
public void setLastModifiedDate(Date lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
@Override
protected void create() throws IOException {
if (contentId != null) {
if (contentId.getWorkspaceId() == null || contentId.getId() == null) {
throw new IOException("Workspace ID or ID of content within wokrspace can not be null");
}
Workspace workspace = SmartContentAPI.getInstance().getWorkspaceApi().getWorkspace(contentId.getWorkspaceId());
if (workspace == null) {
throw new IOException("Non existance workspace Id " + contentId.getWorkspaceId());
}
}
if (contentId == null && contentDef != null && contentDef.getContentTypeID() != null) {
createContentId(contentDef.getContentTypeID().getWorkspace());
}
else if (contentId == null && (contentDef == null || contentDef.getContentTypeID() == null)) {
throw new IOException("Content ID and Content Type Definition is not set!");
}
super.create();
Event<Content> contentEvent = SmartContentAPI.getInstance().getEventRegistrar().<Content>createEvent(
EventType.CREATE, Type.CONTENT, this);
SmartContentAPI.getInstance().getEventRegistrar().notifyEvent(contentEvent);
}
@Override
public void delete() throws IOException {
super.delete();
Event<Content> contentEvent = SmartContentAPI.getInstance().getEventRegistrar().<Content>createEvent(
EventType.DELETE, Type.CONTENT, this);
SmartContentAPI.getInstance().getEventRegistrar().notifyEvent(contentEvent);
}
@Override
protected void update() throws IOException {
super.update();
Event<Content> contentEvent = SmartContentAPI.getInstance().getEventRegistrar().<Content>createEvent(
EventType.UPDATE, Type.CONTENT, this);
SmartContentAPI.getInstance().getEventRegistrar().notifyEvent(contentEvent);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!Content.class.isAssignableFrom(obj.getClass())) {
return false;
}
final Content other = (Content) obj;
if (this.contentId != other.getContentId() && (this.contentId == null ||
!this.contentId.equals(other.getContentId()))) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 29 * hash + (this.contentId != null ? this.contentId.hashCode() : 0);
return hash;
}
@Override
public void createContentId(WorkspaceId workspace) {
if (workspace == null) {
throw new IllegalArgumentException("Workspace ID can not be null!");
}
setContentId(SmartContentAPI.getInstance().getContentLoader().generateContentId(workspace));
}
@Override
public void setEntityTagValue(String eTagValue) {
this.entityTagValue = eTagValue;
}
@Override
public String getEntityTagValue() {
return this.entityTagValue;
}
@Override
public boolean isValid() {
boolean validContent = supressChecking || SmartContentAPI.getInstance().getContentLoader().isValidContent(this);
if (getLogger().isDebugEnabled()) {
getLogger().debug("!!! Checking Content " + validContent);
}
return validContent;
}
@Override
public URI getUri() {
return SmartContentSPI.getInstance().getUriProvider().getContentUri(contentId);
}
@Override
public String getEncodedUriString() {
if (getUri() == null) {
return null;
}
else {
return getUri().toASCIIString();
}
}
@Override
public void setPrivate(boolean privateContent) {
this.privateContent = privateContent;
}
@Override
public boolean isPrivate() {
return this.privateContent;
}
@Override
public ContentImpl clone() {
ContentImpl contentImpl = new ContentImpl();
contentImpl.contentDef = contentDef;
contentImpl.contentId = contentId;
contentImpl.parentId = parentId;
contentImpl.privateContent = privateContent;
contentImpl.contentStatus = contentStatus;
contentImpl.nextPerformToWaitForLock = nextPerformToWaitForLock;
contentImpl.supressChecking = supressChecking;
contentImpl.entityTagValue = entityTagValue;
if (creationDate != null) {
contentImpl.creationDate = new Date(creationDate.getTime());
}
if (lastModifiedDate != null) {
contentImpl.lastModifiedDate = new Date(lastModifiedDate.getTime());
}
contentImpl.map = map;
if (map != null) {
contentImpl.map = new LinkedHashMap<String, Field>();
cloneFields(map, contentImpl.map);
}
if (cachedFieldMap != null) {
contentImpl.cachedFieldMap = new LinkedHashMap<String, Field>();
cloneFields(cachedFieldMap, contentImpl.cachedFieldMap);
}
return contentImpl;
}
protected void cloneFields(Map<String, Field> fieldMap, Map<String, Field> destMap) {
for (Map.Entry<String, Field> oneField : fieldMap.entrySet()) {
FieldImpl fieldImpl = new FieldImpl();
final Field value = oneField.getValue();
fieldImpl.setContent(value.getContent());
fieldImpl.setFieldDef(value.getFieldDef());
fieldImpl.setName(value.getName());
final FieldValue fieldVal = value.getValue();
fieldImpl.setValue(fieldVal);
destMap.put(oneField.getKey(), fieldImpl);
}
}
}