/*
* 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:
* bstefanescu
*
* $Id: ExportedDocumentImpl.java 29029 2008-01-14 18:38:14Z ldoguin $
*/
package org.eclipse.ecr.core.io.impl;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.QName;
import org.eclipse.ecr.core.api.Blob;
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.DocumentLocation;
import org.eclipse.ecr.core.api.DocumentModel;
import org.eclipse.ecr.core.api.IdRef;
import org.eclipse.ecr.core.api.impl.DocumentLocationImpl;
import org.eclipse.ecr.core.api.security.ACE;
import org.eclipse.ecr.core.api.security.ACL;
import org.eclipse.ecr.core.api.security.ACP;
import org.eclipse.ecr.core.io.ExportConstants;
import org.eclipse.ecr.core.io.ExportedDocument;
import org.eclipse.ecr.core.schema.Namespace;
import org.eclipse.ecr.core.schema.SchemaManager;
import org.eclipse.ecr.core.schema.TypeConstants;
import org.eclipse.ecr.core.schema.types.ComplexType;
import org.eclipse.ecr.core.schema.types.Field;
import org.eclipse.ecr.core.schema.types.ListType;
import org.eclipse.ecr.core.schema.types.Schema;
import org.eclipse.ecr.core.schema.types.Type;
import org.eclipse.ecr.runtime.api.Framework;
import org.nuxeo.common.collections.PrimitiveArrays;
import org.nuxeo.common.utils.Base64;
import org.nuxeo.common.utils.Path;
/**
* A representation for an exported document.
* <p>
* It contains all the information needed to restore document data and state.
*
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
@SuppressWarnings("unchecked")
public class ExportedDocumentImpl implements ExportedDocument {
private static final Log log = LogFactory.getLog(ExportedDocumentImpl.class);
private static final Random random = new Random();
private DocumentLocation srcLocation;
// document unique ID
private String id;
// document path
private Path path;
// the main document
private Document document;
// the external blobs if any
private final Map<String, Blob> blobs = new HashMap<String, Blob>(4);
// the optional attached documents
private final Map<String, Document> documents = new HashMap<String, Document>(
4);
public ExportedDocumentImpl() {
}
/**
* @param doc
* @param path the path to use for this document this is used to remove full
* paths
*/
public ExportedDocumentImpl(DocumentModel doc, Path path,
boolean inlineBlobs) throws IOException {
id = doc.getId();
if (path == null) {
this.path = new Path("");
} else {
this.path = path.makeRelative();
}
try {
readDocument(doc, inlineBlobs);
} catch (ClientException e) {
throw new ClientRuntimeException(e);
}
srcLocation = new DocumentLocationImpl(doc);
}
public ExportedDocumentImpl(DocumentModel doc) throws IOException {
this(doc, false);
}
public ExportedDocumentImpl(DocumentModel doc, boolean inlineBlobs)
throws IOException {
this(doc, doc.getPath(), inlineBlobs);
}
/**
* @return the source DocumentLocation
*/
@Override
public DocumentLocation getSourceLocation() {
return srcLocation;
}
@Override
public Path getPath() {
return path;
}
@Override
public void setPath(Path path) {
this.path = path;
}
@Override
public String getId() {
return id;
}
@Override
public void setId(String id) {
this.id = id;
}
@Override
public String getType() {
return document.getRootElement().element(ExportConstants.SYSTEM_TAG).elementText(
"type");
}
@Override
public Document getDocument() {
return document;
}
@Override
public void setDocument(Document document) {
this.document = document;
id = document.getRootElement().attributeValue(ExportConstants.ID_ATTR);
String repName = document.getRootElement().attributeValue(
ExportConstants.REP_NAME);
srcLocation = new DocumentLocationImpl(repName, new IdRef(id));
}
@Override
public Map<String, Blob> getBlobs() {
return blobs;
}
@Override
public void putBlob(String id, Blob blob) {
blobs.put(id, blob);
}
@Override
public Blob removeBlob(String id) {
return blobs.remove(id);
}
@Override
public Blob getBlob(String id) {
return blobs.get(id);
}
@Override
public boolean hasExternalBlobs() {
return !blobs.isEmpty();
}
@Override
public Map<String, Document> getDocuments() {
return documents;
}
@Override
public Document getDocument(String id) {
return documents.get(id);
}
@Override
public void putDocument(String id, Document doc) {
documents.put(id, doc);
}
@Override
public Document removeDocument(String id) {
return documents.remove(id);
}
/**
* @return the number of files describing the document.
*/
@Override
public int getFilesCount() {
return 1 + documents.size() + blobs.size();
}
private void readDocument(DocumentModel doc, boolean inlineBlobs)
throws IOException, ClientException {
document = DocumentFactory.getInstance().createDocument();
document.setName(doc.getName());
Element rootElement = document.addElement(ExportConstants.DOCUMENT_TAG);
rootElement.addAttribute(ExportConstants.REP_NAME,
doc.getRepositoryName());
rootElement.addAttribute(ExportConstants.ID_ATTR,
doc.getRef().toString());
Element systemElement = rootElement.addElement(ExportConstants.SYSTEM_TAG);
systemElement.addElement(ExportConstants.TYPE_TAG).addText(
doc.getType());
systemElement.addElement(ExportConstants.PATH_TAG).addText(
path.toString());
// lifecycle
try {
String lifeCycleState = doc.getCurrentLifeCycleState();
if (lifeCycleState != null && lifeCycleState.length() > 0) {
systemElement.addElement(ExportConstants.LIFECYCLE_STATE_TAG).addText(
lifeCycleState);
}
String lifeCyclePolicy = doc.getLifeCyclePolicy();
if (lifeCyclePolicy != null && lifeCyclePolicy.length() > 0) {
systemElement.addElement(ExportConstants.LIFECYCLE_POLICY_TAG).addText(
lifeCyclePolicy);
}
} catch (Exception e) {
log.error(e, e);
} // end of lifecycle
// write security
Element acpElement = systemElement.addElement(ExportConstants.ACCESS_CONTROL_TAG);
ACP acp = doc.getACP();
if (acp != null) {
readACP(acpElement, acp);
}
// write schemas
SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class);
String[] schemaNames = doc.getSchemas();
for (String schemaName : schemaNames) {
Element schemaElement = rootElement.addElement(
ExportConstants.SCHEMA_TAG).addAttribute("name", schemaName);
Schema schema = schemaManager.getSchema(schemaName);
Namespace targetNs = schema.getNamespace();
schemaElement.addNamespace(targetNs.prefix, targetNs.uri);
DataModel dataModel = doc.getDataModel(schemaName);
for (Field field : schema.getFields()) {
Object value = dataModel.getData(field.getName().getLocalName());
readProperty(schemaElement, targetNs, field, value, inlineBlobs);
}
}
}
private void readProperty(Element parent, Namespace targetNs, Field field,
Object value, boolean inlineBlobs) throws IOException {
Type type = field.getType();
QName name = QName.get(field.getName().getLocalName(), targetNs.prefix,
targetNs.uri);
Element element = parent.addElement(name);
if (value == null) {
return; // have no content
}
// extract the element content
if (type.isSimpleType()) {
element.addText(type.encode(value));
} else if (type.isComplexType()) {
ComplexType ctype = (ComplexType) type;
if (TypeConstants.isContentType(ctype)) {
readBlob(element, ctype, (Blob) value, inlineBlobs);
} else {
readComplex(element, ctype, (Map) value, inlineBlobs);
}
} else if (type.isListType()) {
if (value instanceof List) {
readList(element, (ListType) type, (List) value, inlineBlobs);
} else if (value.getClass().getComponentType() != null) {
readList(element, (ListType) type,
PrimitiveArrays.toList(value), inlineBlobs);
} else {
throw new IllegalArgumentException(
"A value of list type is neither list neither array: "
+ value);
}
}
}
private void readBlob(Element element, ComplexType ctype, Blob blob,
boolean inlineBlobs) throws IOException {
String blobPath = Integer.toHexString(random.nextInt()) + ".blob";
element.addElement(ExportConstants.BLOB_ENCODING).addText(
blob.getEncoding() != null ? blob.getEncoding() : "");
element.addElement(ExportConstants.BLOB_MIME_TYPE).addText(
blob.getMimeType() != null ? blob.getMimeType() : "");
element.addElement(ExportConstants.BLOB_FILENAME).addText(
blob.getFilename() != null ? blob.getFilename() : "");
Element data = element.addElement(ExportConstants.BLOB_DATA);
if (inlineBlobs) {
String content = Base64.encodeBytes(blob.getByteArray());
data.setText(content);
} else {
data.setText(blobPath);
blobs.put(blobPath, blob);
}
}
private void readComplex(Element element, ComplexType ctype, Map map,
boolean inlineBlobs) throws IOException {
Iterator<Map.Entry> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
readProperty(element, ctype.getNamespace(),
ctype.getField(entry.getKey().toString()),
entry.getValue(), inlineBlobs);
}
}
private void readList(Element element, ListType ltype, List list,
boolean inlineBlobs) throws IOException {
Field field = ltype.getField();
for (Object obj : list) {
readProperty(element, Namespace.DEFAULT_NS, field, obj, inlineBlobs);
}
}
private static void readACP(Element element, ACP acp) {
ACL[] acls = acp.getACLs();
for (ACL acl : acls) {
Element aclElement = element.addElement(ExportConstants.ACL_TAG);
aclElement.addAttribute(ExportConstants.NAME_ATTR, acl.getName());
ACE[] aces = acl.getACEs();
for (ACE ace : aces) {
Element aceElement = aclElement.addElement(ExportConstants.ACE_TAG);
aceElement.addAttribute(ExportConstants.PRINCIPAL_ATTR,
ace.getUsername());
aceElement.addAttribute(ExportConstants.PERMISSION_ATTR,
ace.getPermission());
aceElement.addAttribute(ExportConstants.GRANT_ATTR,
String.valueOf(ace.isGranted()));
}
}
}
}