/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Bogdan Stefanescu
* Florent Guillaume
*/
package org.eclipse.ecr.core.api;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.ecr.core.api.ClientException;
import org.eclipse.ecr.core.api.ClientRuntimeException;
import org.eclipse.ecr.core.api.DataModel;
import org.eclipse.ecr.core.api.DocumentException;
import org.eclipse.ecr.core.api.DocumentModel;
import org.eclipse.ecr.core.api.DocumentRef;
import org.eclipse.ecr.core.api.IdRef;
import org.eclipse.ecr.core.api.DocumentModel.DocumentModelRefresh;
import org.eclipse.ecr.core.api.impl.DataModelImpl;
import org.eclipse.ecr.core.api.impl.DocumentModelImpl;
import org.eclipse.ecr.core.api.model.DocumentPart;
import org.eclipse.ecr.core.api.model.impl.DocumentPartImpl;
import org.eclipse.ecr.core.api.repository.cache.DirtyUpdateChecker;
import org.eclipse.ecr.core.lifecycle.LifeCycleException;
import org.eclipse.ecr.core.model.Document;
import org.eclipse.ecr.core.model.NoSuchPropertyException;
import org.eclipse.ecr.core.model.Repository;
import org.eclipse.ecr.core.schema.DocumentType;
import org.eclipse.ecr.core.schema.FacetNames;
import org.eclipse.ecr.core.schema.PrefetchInfo;
import org.eclipse.ecr.core.schema.types.Field;
import org.eclipse.ecr.core.schema.types.Schema;
import org.nuxeo.common.utils.Null;
import org.nuxeo.common.utils.Path;
/**
* Bridge between a {@link DocumentModel} and a {@link Document} for creation /
* update.
*/
public class DocumentModelFactory {
private static final Log log = LogFactory.getLog(DocumentModelFactory.class);
// Utility class.
private DocumentModelFactory() {
}
/**
* @deprecated unused
*/
@Deprecated
public static DocumentModel newDocument(DocumentModel parent, String type) {
return newDocument(parent, null, type);
}
/**
* @deprecated unused
*/
@Deprecated
public static DocumentModel newDocument(DocumentModel parent, String name,
String type) {
DocumentType docType = parent.getCoreSession().getDocumentType(type);
return newDocument(parent, name, docType);
}
/**
* @deprecated unused
*/
@Deprecated
public static DocumentModel newDocument(DocumentModel parent,
DocumentType type) {
return newDocument(parent, null, type);
}
/**
* @deprecated unused
*/
@Deprecated
public static DocumentModel newDocument(DocumentModel parent, String name,
DocumentType type) {
return new DocumentModelImpl(null, type.getName(), null,
parent.getPath(), null, null, parent.getRef(), null, null,
null, parent.getRepositoryName());
}
/**
* @deprecated unused
*/
@Deprecated
public static Map<String, Serializable> updatePrefetch(
DocumentModel docModel) {
Map<String, Serializable> prefetchMap = new HashMap<String, Serializable>();
PrefetchInfo prefetchInfo = docModel.getDocumentType().getPrefetchInfo();
if (prefetchInfo != null) {
Field[] prefetchFields = prefetchInfo.getFields();
for (Field field : prefetchFields) {
String typeName = field.getDeclaringType().getName();
String typeLocalName = field.getName().getLocalName();
String fieldName = typeName + '.' + typeLocalName;
Object value;
try {
value = docModel.getProperty(typeName, typeLocalName);
} catch (ClientException e) {
continue;
}
prefetchMap.put(fieldName, (Serializable) value);
}
}
return prefetchMap;
}
/**
* @deprecated unused
*/
@Deprecated
public static DocumentModelImpl createDocumentModel(Document doc)
throws DocumentException {
DocumentType docType = doc.getType();
String[] schemas;
if (docType == null) {
schemas = null;
} else {
schemas = docType.getSchemaNames();
}
return createDocumentModel(doc, schemas);
}
/**
* Creates a document model for an existing document.
*
* @param doc the document
* @param schemas the schemas to prefetch (deprecated), or {@code null}
* @return the new document model
* @throws DocumentException
*/
public static DocumentModelImpl createDocumentModel(Document doc,
String[] schemas) throws DocumentException {
DocumentType type = doc.getType();
if (type == null) {
throw new DocumentException("Type not found for doc " + doc);
}
String sid = doc.getSession().getUserSessionId();
DocumentRef docRef = new IdRef(doc.getUUID());
Document parent = doc.getParent();
DocumentRef parentRef = parent == null ? null : new IdRef(
parent.getUUID());
// Compute document source id if exists
Document sourceDoc = doc.getSourceDocument();
String sourceId = sourceDoc == null ? null : sourceDoc.getUUID();
// Immutable flag
boolean immutable = doc.isVersion()
|| (doc.isProxy() && sourceDoc.isVersion());
// Instance facets
Set<String> facets = new HashSet<String>(Arrays.asList(doc.getFacets()));
if (immutable) {
facets.add(FacetNames.IMMUTABLE);
}
// Compute repository name.
Repository repository = doc.getRepository();
String repositoryName = repository == null ? null
: repository.getName();
// versions being imported before their live doc don't have a path
String p = doc.getPath();
Path path = p == null ? null : new Path(p);
// create the document model
// lock is unused
DocumentModelImpl docModel = new DocumentModelImpl(sid, type.getName(),
doc.getUUID(), path, null, docRef, parentRef, null, facets,
sourceId, repositoryName);
if (doc.isVersion()) {
docModel.setIsVersion(true);
}
if (doc.isProxy()) {
docModel.setIsProxy(true);
}
if (immutable) {
docModel.setIsImmutable(true);
}
// populate models
Schema[] prefetchSchemas = null;
PrefetchInfo prefetchInfo = type.getPrefetchInfo();
if (prefetchInfo != null) {
prefetchSchemas = prefetchInfo.getSchemas();
Field[] prefetchFields = prefetchInfo.getFields();
for (Field field : prefetchFields) {
// TODO: the document model don't know to work using prefixed
// names -this should be fixed and register the property here
// directly
// by its prefixed name and not by the "schema.field" id
try {
Object value = doc.getPropertyValue(field.getName().getPrefixedName());
docModel.prefetchProperty(
field.getDeclaringType().getName() + '.'
+ field.getName().getLocalName(), value);
} catch (NoSuchPropertyException e) {
// skip
} catch (DocumentException e) {
log.error("Error while building prefetch fields, "
+ "check the document configuration", e);
}
}
}
if (schemas != null) {
for (String schemaName : schemas) {
Schema schema = type.getSchema(schemaName);
if (schema == null) {
continue;
}
docModel.addDataModel(createDataModel(doc, schema));
}
} else if (prefetchSchemas != null) {
for (Schema schema : prefetchSchemas) {
docModel.addDataModel(createDataModel(doc, schema));
}
}
// prefetch lifecycle state
try {
String lifeCycleState = doc.getLifeCycleState();
docModel.prefetchCurrentLifecycleState(lifeCycleState);
String lifeCyclePolicy = doc.getLifeCyclePolicy();
docModel.prefetchLifeCyclePolicy(lifeCyclePolicy);
} catch (LifeCycleException e) {
log.debug("Cannot prefetch lifecycle for doc: " + doc.getName()
+ ". Error: " + e.getMessage());
}
DirtyUpdateChecker.check(docModel);
return docModel;
}
/**
* Creates a document model for a new document.
* <p>
* Initializes the proper data models according to the type info.
*
* @param sessionId the CoreSession id
* @param docType the document type
* @return the document model
* @throws DocumentException
*/
public static DocumentModelImpl createDocumentModel(String sessionId,
DocumentType docType) throws DocumentException {
DocumentModelImpl docModel = new DocumentModelImpl(sessionId,
docType.getName(), null, null, null, null, null, null, null,
null, null);
for (Schema schema : docType.getSchemas()) {
docModel.addDataModel(createDataModel(null, schema));
}
return docModel;
}
/**
* @deprecated unused
*/
@Deprecated
public static DocumentModelImpl createDocumentModel(DocumentType docType)
throws DocumentException {
return createDocumentModel(null, docType);
}
/**
* @deprecated unused
*/
@Deprecated
public static DocumentModelImpl createDocumentModel(String parentPath,
String id, DocumentType docType, String[] schemas)
throws DocumentException {
DocumentModelImpl docModel = new DocumentModelImpl(parentPath, id,
docType.getName());
// populate models
if (schemas != null) {
for (String schemaName : schemas) {
Schema schema = docType.getSchema(schemaName);
docModel.addDataModel(createDataModel(null, schema));
}
}
return docModel;
}
/**
* Creates a data model from a document and a schema. If the document is
* null, just creates empty data models.
*/
public static DataModel createDataModel(Document doc, Schema schema)
throws DocumentException {
DocumentPart part = new DocumentPartImpl(schema);
if (doc != null) {
try {
doc.readDocumentPart(part);
} catch (DocumentException e) {
throw e;
} catch (Exception e) {
throw new DocumentException("failed to read document part", e);
}
}
return new DataModelImpl(part);
}
/**
* Writes a document model to a document. Returns the re-read document
* model.
*/
public static DocumentModel writeDocumentModel(DocumentModel docModel,
Document doc) throws DocumentException, ClientException {
if (!(docModel instanceof DocumentModelImpl)) {
throw new ClientRuntimeException("Must be a DocumentModelImpl: "
+ docModel);
}
boolean changed = false;
// facets added/removed
Set<String> instanceFacets = ((DocumentModelImpl) docModel).instanceFacets;
Set<String> instanceFacetsOrig = ((DocumentModelImpl) docModel).instanceFacetsOrig;
Set<String> addedFacets = new HashSet<String>(instanceFacets);
addedFacets.removeAll(instanceFacetsOrig);
for (String facet : addedFacets) {
changed = doc.addFacet(facet) || changed;
}
Set<String> removedFacets = new HashSet<String>(instanceFacetsOrig);
removedFacets.removeAll(instanceFacets);
for (String facet : removedFacets) {
changed = doc.removeFacet(facet) || changed;
}
// write data models
DocumentPart[] parts = docModel.getParts(); // TODO only loaded ones
for (DocumentPart part : parts) {
if (part.isDirty()) {
try {
doc.writeDocumentPart(part);
} catch (ClientException e) {
throw e;
} catch (DocumentException e) {
throw e;
} catch (Exception e) {
throw new ClientException("failed to write document part",
e);
}
changed = true;
}
}
if (!changed) {
return docModel;
}
// TODO: here we can optimize document part doesn't need to be read
DocumentModel newModel = createDocumentModel(doc, null);
newModel.copyContextData(docModel);
return newModel;
}
/**
* Gets what's to refresh in a model (except for the ACPs, which need the
* session).
*/
public static DocumentModelRefresh refreshDocumentModel(Document doc,
int flags, String[] schemas) throws DocumentException,
LifeCycleException, Exception {
DocumentModelRefresh refresh = new DocumentModelRefresh();
if ((flags & DocumentModel.REFRESH_PREFETCH) != 0) {
PrefetchInfo info = doc.getType().getPrefetchInfo();
if (info != null) {
Schema[] pschemas = info.getSchemas();
if (pschemas != null) {
// TODO: this should be returned as document parts of
// the document
}
Field[] fields = info.getFields();
if (fields != null) {
Map<String, Serializable> prefetch = new HashMap<String, Serializable>();
// TODO : should use documentpartreader
for (Field field : fields) {
Object value = doc.getPropertyValue(field.getName().getPrefixedName());
prefetch.put(field.getDeclaringType().getName() + '.'
+ field.getName().getLocalName(),
value == null ? Null.VALUE
: (Serializable) value);
}
refresh.prefetch = prefetch;
}
}
}
if ((flags & DocumentModel.REFRESH_STATE) != 0) {
refresh.lifeCycleState = doc.getLifeCycleState();
refresh.lifeCyclePolicy = doc.getLifeCyclePolicy();
refresh.isCheckedOut = doc.isCheckedOut();
refresh.isLatestVersion = doc.isLatestVersion();
refresh.isMajorVersion = doc.isMajorVersion();
refresh.isLatestMajorVersion = doc.isLatestMajorVersion();
refresh.isVersionSeriesCheckedOut = doc.isVersionSeriesCheckedOut();
refresh.versionSeriesId = doc.getVersionSeriesId();
refresh.checkinComment = doc.getCheckinComment();
}
if ((flags & DocumentModel.REFRESH_CONTENT) != 0) {
if (schemas == null) {
schemas = doc.getType().getSchemaNames();
}
DocumentType type = doc.getType();
DocumentPart[] parts = new DocumentPart[schemas.length];
for (int i = 0; i < schemas.length; i++) {
DocumentPart part = new DocumentPartImpl(
type.getSchema(schemas[i]));
doc.readDocumentPart(part);
parts[i] = part;
}
refresh.documentParts = parts;
}
return refresh;
}
}