/* * 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 */ package org.eclipse.ecr.automation.core.util; import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.Map; import org.eclipse.ecr.automation.OperationException; import org.eclipse.ecr.core.api.Blob; import org.eclipse.ecr.core.api.ClientException; import org.eclipse.ecr.core.api.CoreSession; import org.eclipse.ecr.core.api.DocumentModel; import org.eclipse.ecr.core.api.model.Property; import org.eclipse.ecr.core.api.model.PropertyException; import org.eclipse.ecr.core.api.model.impl.ListProperty; import org.eclipse.ecr.core.api.security.ACE; import org.eclipse.ecr.core.api.security.ACL; import org.eclipse.ecr.core.api.security.impl.ACLImpl; import org.eclipse.ecr.core.api.security.impl.ACPImpl; import org.eclipse.ecr.core.schema.types.ComplexType; import org.eclipse.ecr.core.schema.types.ListType; import org.eclipse.ecr.core.schema.types.SimpleType; import org.eclipse.ecr.core.schema.types.Type; import org.eclipse.ecr.core.schema.types.primitives.BinaryType; import org.eclipse.ecr.core.schema.types.primitives.BooleanType; import org.eclipse.ecr.core.schema.types.primitives.DateType; import org.eclipse.ecr.core.schema.types.primitives.DoubleType; import org.eclipse.ecr.core.schema.types.primitives.IntegerType; import org.eclipse.ecr.core.schema.types.primitives.LongType; import org.eclipse.ecr.core.schema.types.primitives.StringType; import org.nuxeo.common.utils.StringUtils; /** * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> */ public class DocumentHelper { private DocumentHelper() { } /** * Saves the document and clear context data to avoid incrementing version * in next operations if not needed. */ public static DocumentModel saveDocument(CoreSession session, DocumentModel doc) throws ClientException { doc = session.saveDocument(doc); return session.getDocument(doc.getRef()); } /** * Removes a property from a document given the xpath. If the xpath points * to a list property the list will be cleared. If the path points to a * blob in a list the property is removed from the list. Otherwise the * xpath should point to a non list property that will be removed. */ public static void removeProperty(DocumentModel doc, String xpath) throws ClientException { Property p = doc.getProperty(xpath); if (p.isList()) { ((ListProperty) p).clear(); } else { Property pp = p.getParent(); if (pp != null && pp.isList()) { // remove list entry ((ListProperty) pp).remove(p); } else { p.remove(); } } } /** * Given a document property, updates its value with the given blob. The * property can be a blob list or a blob. If a blob list the blob is * appended to the list, if a blob then it will be set as the property * value. Both blob list formats are supported: the file list (blob holder * list) and simple blob list. */ public static void addBlob(Property p, Blob blob) throws PropertyException { if (p.isList()) { // detect if a list of simple blobs or a list of files (blob // holder) Type ft = ((ListProperty) p).getType().getFieldType(); if (ft.isComplexType() && ((ComplexType) ft).getFieldsCount() == 2) { p.addValue(createBlobHolderMap(blob)); } else { p.addValue(blob); } } else { p.setValue(blob); } } public static HashMap<String, Serializable> createBlobHolderMap(Blob blob) { HashMap<String, Serializable> map = new HashMap<String, Serializable>(); map.put("file", (Serializable) blob); map.put("filename", blob.getFilename()); return map; } /** * Sets the properties given as a map of xpath:value to the given document. * There is one special property: ecm:acl that can be used to set the local * acl. The format of this property value is: [string username]:[string * permission]:[boolean grant], [string username]:[string * permission]:[boolean grant], ... TODO list properties are not yet * supported */ public static void setProperties(CoreSession session, DocumentModel doc, Map<String, String> values) throws Exception { for (Map.Entry<String, String> entry : values.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); setProperty(session, doc, key, value); } } public static void setProperty(CoreSession session, DocumentModel doc, String key, String value) throws Exception { if ("ecm:acl".equals(key)) { setLocalAcl(session, doc, value); } Property p = doc.getProperty(key); if (value == null || value.length() == 0) { p.setValue(null); return; } Type type = p.getField().getType(); if (!type.isSimpleType()) { if (type.isListType()) { ListType ltype = (ListType)type; if (ltype.isScalarList()) { p.setValue(readStringList(value, (SimpleType)ltype.getFieldType())); return; } } throw new OperationException( "Only scalar or scalar list types can be set using update operation"); } else { p.setValue(((SimpleType) type).getPrimitiveType().decode(value)); } } protected static void setLocalAcl(CoreSession session, DocumentModel doc, String value) throws ClientException { ACPImpl acp = new ACPImpl(); ACLImpl acl = new ACLImpl(ACL.LOCAL_ACL); acp.addACL(acl); String[] entries = StringUtils.split(value, ',', true); if (entries.length == 0) { return; } for (String entry : entries) { String[] ace = StringUtils.split(entry, ':', true); acl.add(new ACE(ace[0], ace[1], Boolean.parseBoolean(ace[2]))); } session.setACP(doc.getRef(), acp, false); } /** * Read an encoded string list as a comma separated list. To use comma inside list element values you need to escape them using '\'. * If the given type is different from {@link StringType#ID} then array elements will be converted to the actual type. * @param value * @param type * @return */ public static Object readStringList(String value, SimpleType type) { String[] ar = readStringList(value); if (ar == null) { return null; } if (StringType.INSTANCE == type) { return ar; } else if (DateType.INSTANCE == type) { Calendar[] r = new Calendar[ar.length]; for (int i=0; i<r.length; i++) { r[i] = (Calendar)type.decode(ar[i]); } return r; } else if (LongType.INSTANCE == type) { Long[] r = new Long[ar.length]; for (int i=0; i<r.length; i++) { r[i] = (Long)type.decode(ar[i]); } return r; } else if (IntegerType.INSTANCE == type) { Integer[] r = new Integer[ar.length]; for (int i=0; i<r.length; i++) { r[i] = (Integer)type.decode(ar[i]); } return r; } else if (DoubleType.INSTANCE == type) { Double[] r = new Double[ar.length]; for (int i=0; i<r.length; i++) { r[i] = (Double)type.decode(ar[i]); } return r; } else if (BooleanType.INSTANCE == type) { Boolean[] r = new Boolean[ar.length]; for (int i=0; i<r.length; i++) { r[i] = (Boolean)type.decode(ar[i]); } return r; } else if (BinaryType.INSTANCE == type) { InputStream[] r = new InputStream[ar.length]; for (int i=0; i<r.length; i++) { r[i] = (InputStream)type.decode(ar[i]); } return r; } throw new IllegalArgumentException("Unsupported type when updating document properties from string representation: "+type); } /** * Read an encoded string list as a comma separated list. To use comma inside list element values you need to escape them using '\'. * @param value * @return */ public static String[] readStringList(String value) { if (value == null) { return null; } if (value.length() == 0) { return new String[0]; } ArrayList<String> result = new ArrayList<String>(); char[] chars = value.toCharArray(); StringBuilder buf = new StringBuilder(); boolean esc = false; for (int i=0; i<chars.length; i++) { char c = chars[i]; if (c == '\\') { if (esc) { buf.append('\\'); esc = false; } else { esc = true; } } else if (c == ',') { if (esc) { buf.append(','); esc = false; } else { result.add(buf.toString()); buf = new StringBuilder(); } } else { buf.append(c); } } result.add(buf.toString()); return result.toArray(new String[result.size()]); } }