001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Florent Guillaume
018 */
019package org.nuxeo.ecm.core.opencmis.impl.client;
020
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.GregorianCalendar;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028import java.util.Map.Entry;
029import java.util.Set;
030
031import org.apache.chemistry.opencmis.client.api.CmisObject;
032import org.apache.chemistry.opencmis.client.api.ObjectId;
033import org.apache.chemistry.opencmis.client.api.ObjectType;
034import org.apache.chemistry.opencmis.client.api.Policy;
035import org.apache.chemistry.opencmis.client.api.Property;
036import org.apache.chemistry.opencmis.client.api.Relationship;
037import org.apache.chemistry.opencmis.client.api.Rendition;
038import org.apache.chemistry.opencmis.client.api.SecondaryType;
039import org.apache.chemistry.opencmis.client.runtime.RenditionImpl;
040import org.apache.chemistry.opencmis.commons.PropertyIds;
041import org.apache.chemistry.opencmis.commons.data.Ace;
042import org.apache.chemistry.opencmis.commons.data.Acl;
043import org.apache.chemistry.opencmis.commons.data.AllowableActions;
044import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement;
045import org.apache.chemistry.opencmis.commons.data.RenditionData;
046import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
047import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
048import org.apache.chemistry.opencmis.commons.enums.Action;
049import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
050import org.apache.chemistry.opencmis.commons.enums.ExtensionLevel;
051import org.apache.chemistry.opencmis.commons.enums.Updatability;
052import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException;
053import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
054import org.apache.chemistry.opencmis.commons.server.CmisService;
055import org.nuxeo.ecm.core.api.CoreSession;
056import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoCmisService;
057import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoObjectData;
058import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoPropertyData.NuxeoPropertyDataName;
059import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoPropertyDataBase;
060
061/**
062 * Base abstract live local CMIS Object, wrapping a {@link NuxeoSession} and a {@link NuxeoObjectData} which is backed
063 * by a Nuxeo document.
064 */
065public abstract class NuxeoObject implements CmisObject {
066
067    protected static final Set<Updatability> UPDATABILITY_READWRITE = Collections.singleton(Updatability.READWRITE);
068
069    protected final NuxeoSession session;
070
071    protected final CmisService service;
072
073    protected final NuxeoCmisService nuxeoCmisService;
074
075    protected final NuxeoObjectFactory objectFactory;
076
077    public final NuxeoObjectData data;
078
079    protected final ObjectType type;
080
081    protected final List<SecondaryType> secondaryTypes;
082
083    /** type + secondaryTypes */
084    protected final List<ObjectType> allTypes;
085
086    public static NuxeoObject construct(NuxeoSession session, NuxeoObjectData data, ObjectType type,
087            List<SecondaryType> secondaryTypes) {
088        BaseTypeId baseTypeId = type.getBaseTypeId();
089        switch (baseTypeId) {
090        case CMIS_FOLDER:
091            return new NuxeoFolder(session, data, type, secondaryTypes);
092        case CMIS_DOCUMENT:
093            return new NuxeoDocument(session, data, type, secondaryTypes);
094        case CMIS_POLICY:
095            throw new UnsupportedOperationException(baseTypeId.toString());
096        case CMIS_RELATIONSHIP:
097            return new NuxeoRelationship(session, data, type, secondaryTypes);
098        default:
099            throw new RuntimeException(baseTypeId.toString());
100        }
101    }
102
103    public NuxeoObject(NuxeoSession session, NuxeoObjectData data, ObjectType type,
104            List<SecondaryType> secondaryTypes) {
105        this.session = session;
106        service = session.getService();
107        nuxeoCmisService = NuxeoCmisService.extractFromCmisService(service);
108        objectFactory = session.getObjectFactory();
109        this.data = data;
110        this.type = type;
111        this.secondaryTypes = secondaryTypes;
112        allTypes = new ArrayList<>(1 + secondaryTypes.size());
113        allTypes.add(type);
114        allTypes.addAll(secondaryTypes);
115    }
116
117    @SuppressWarnings("unchecked")
118    @Override
119    public <T> T getAdapter(Class<T> adapterInterface) {
120        throw new CmisRuntimeException("Cannot adapt to " + adapterInterface.getName());
121    }
122
123    public String getRepositoryId() {
124        return session.getRepositoryId();
125    }
126
127    @Override
128    public String getId() {
129        return data.getId();
130    }
131
132    @Override
133    public ObjectType getType() {
134        return type;
135    }
136
137    @Override
138    public BaseTypeId getBaseTypeId() {
139        return data.getBaseTypeId();
140    }
141
142    @Override
143    public ObjectType getBaseType() {
144        return session.getTypeDefinition(getBaseTypeId().value());
145    }
146
147    @Override
148    public List<SecondaryType> getSecondaryTypes() {
149        return secondaryTypes;
150    }
151
152    @Override
153    public List<ObjectType> findObjectType(String id) {
154        List<ObjectType> types = new ArrayList<>(1);
155        for (ObjectType t : allTypes) {
156            if (t.getPropertyDefinitions().containsKey(id)) {
157                types.add(t);
158            }
159        }
160        return types;
161    }
162
163    @Override
164    public String getName() {
165        return NuxeoPropertyDataName.getValue(data.doc);
166    }
167
168    @Override
169    public String getChangeToken() {
170        return getPropertyValue(PropertyIds.CHANGE_TOKEN);
171    }
172
173    @Override
174    public String getCreatedBy() {
175        return getPropertyValue(PropertyIds.CREATED_BY);
176    }
177
178    @Override
179    public GregorianCalendar getCreationDate() {
180        return getPropertyValue(PropertyIds.CREATION_DATE);
181    }
182
183    @Override
184    public GregorianCalendar getLastModificationDate() {
185        return getPropertyValue(PropertyIds.LAST_MODIFICATION_DATE);
186    }
187
188    @Override
189    public String getLastModifiedBy() {
190        return getPropertyValue(PropertyIds.LAST_MODIFIED_BY);
191    }
192
193    @Override
194    public String getDescription() {
195        return getPropertyValue(PropertyIds.DESCRIPTION);
196    }
197
198    @Override
199    public void delete(boolean allVersions) {
200        service.deleteObject(getRepositoryId(), getId(), Boolean.valueOf(allVersions), null);
201    }
202
203    @Override
204    public void delete() {
205        delete(true);
206    }
207
208    @Override
209    public CmisObject updateProperties(Map<String, ?> properties) {
210        return updateProperties(properties, null, null);
211    }
212
213    @Override
214    public ObjectId updateProperties(Map<String, ?> properties, boolean refresh) {
215        return updateProperties(properties, null, null, refresh);
216    }
217
218    @Override
219    public CmisObject updateProperties(Map<String, ?> properties, List<String> addSecondaryTypeIds,
220            List<String> removeSecondaryTypeIds) {
221        ObjectId objectId = updateProperties(properties, addSecondaryTypeIds, removeSecondaryTypeIds, true);
222        return session.getObject(objectId);
223    }
224
225    @Override
226    public ObjectId updateProperties(Map<String, ?> properties, List<String> addSecondaryTypeIds,
227            List<String> removeSecondaryTypeIds, boolean refresh) {
228        // refresh is ignored
229        if (addSecondaryTypeIds != null) {
230            for (String facet : addSecondaryTypeIds) {
231                data.doc.addFacet(facet);
232            }
233        }
234        if (removeSecondaryTypeIds != null) {
235            for (String facet : removeSecondaryTypeIds) {
236                data.doc.removeFacet(facet);
237            }
238        }
239        if (properties != null) {
240            for (Entry<String, ?> en : properties.entrySet()) {
241                ((NuxeoPropertyDataBase<?>) data.getProperty(en.getKey())).setValue(en.getValue());
242            }
243        }
244        CoreSession coreSession = session.getCoreSession();
245        data.doc = coreSession.saveDocument(data.doc);
246        coreSession.save();
247        return this;
248    }
249
250    @Override
251    public CmisObject rename(String newName) {
252        if (newName == null || newName.length() == 0) {
253            throw new IllegalArgumentException("New name must not be empty!");
254        }
255
256        Map<String, Object> prop = new HashMap<String, Object>();
257        prop.put(PropertyIds.NAME, newName);
258
259        return updateProperties(prop);
260    }
261
262    @Override
263    public ObjectId rename(String newName, boolean refresh) {
264        if (newName == null || newName.length() == 0) {
265            throw new IllegalArgumentException("New name must not be empty!");
266        }
267
268        Map<String, Object> prop = new HashMap<String, Object>();
269        prop.put(PropertyIds.NAME, newName);
270
271        return updateProperties(prop, refresh);
272    }
273
274    @SuppressWarnings("unchecked")
275    @Override
276    public <T> T getPropertyValue(String id) {
277        return (T) data.getProperty(id).getValue();
278    }
279
280    @Override
281    public <T> Property<T> getProperty(String id) {
282        return new NuxeoProperty<T>(this, id);
283    }
284
285    @Override
286    public List<Property<?>> getProperties() {
287        List<Property<?>> list = new ArrayList<Property<?>>();
288        for (ObjectType t : allTypes) {
289            Collection<PropertyDefinition<?>> defs = t.getPropertyDefinitions().values();
290            for (PropertyDefinition<?> pd : defs) {
291                list.add(new NuxeoProperty<Object>(this, pd.getId()));
292            }
293        }
294        return list;
295    }
296
297    @Override
298    public Acl addAcl(List<Ace> addAces, AclPropagation aclPropagation) {
299        return service.applyAcl(getRepositoryId(), getId(), objectFactory.convertAces(addAces), null, aclPropagation,
300                null);
301    }
302
303    @Override
304    public Acl applyAcl(List<Ace> addAces, List<Ace> removeAces, AclPropagation aclPropagation) {
305        return service.applyAcl(getRepositoryId(), getId(), objectFactory.convertAces(addAces),
306                objectFactory.convertAces(removeAces), aclPropagation, null);
307    }
308
309    @Override
310    public Acl setAcl(List<Ace> aces) {
311        return service.applyAcl(getRepositoryId(), getId(), objectFactory.convertAces(aces),
312                AclPropagation.REPOSITORYDETERMINED);
313    }
314
315    @Override
316    public Acl getAcl() {
317        return data.getAcl();
318    }
319
320    @Override
321    public Acl removeAcl(List<Ace> removeAces, AclPropagation aclPropagation) {
322        return service.applyAcl(getRepositoryId(), getId(), null, objectFactory.convertAces(removeAces),
323                aclPropagation, null);
324    }
325
326    @Override
327    public AllowableActions getAllowableActions() {
328        // we don't call data.getAllowableActions as includeAllowableActions
329        // may be false
330        return NuxeoObjectData.getAllowableActions(data.doc, data.creation);
331    }
332
333    @Override
334    public List<Policy> getPolicies() {
335        return Collections.emptyList();
336    }
337
338    @Override
339    public void applyPolicy(ObjectId... policyIds) {
340        throw new CmisNotSupportedException();
341    }
342
343    @Override
344    public void removePolicy(ObjectId... policyIds) {
345        throw new CmisNotSupportedException();
346    }
347
348    @Override
349    public List<Relationship> getRelationships() {
350        throw new CmisNotSupportedException();
351    }
352
353    @Override
354    public List<Rendition> getRenditions() {
355        List<RenditionData> renditions = data.getRenditions();
356        if (renditions == null) {
357            return Collections.emptyList();
358        }
359        List<Rendition> res = new ArrayList<Rendition>(renditions.size());
360        for (RenditionData ren : renditions) {
361            long length = ren.getBigLength() == null ? -1 : ren.getBigLength().longValue();
362            int height = ren.getBigHeight() == null ? -1 : ren.getBigHeight().intValue();
363            int width = ren.getBigWidth() == null ? -1 : ren.getBigWidth().intValue();
364            RenditionImpl rendition = new RenditionImpl(session, getId(), ren.getStreamId(),
365                    ren.getRenditionDocumentId(), ren.getKind(), length, ren.getMimeType(), ren.getTitle(), height,
366                    width);
367            res.add(rendition);
368        }
369        return res;
370    }
371
372    @Override
373    public void refresh() {
374        data.doc.refresh();
375    }
376
377    @Override
378    public void refreshIfOld(long durationInMillis) {
379        // TODO Auto-generated method stub
380        throw new UnsupportedOperationException();
381    }
382
383    @Override
384    public long getRefreshTimestamp() {
385        // TODO Auto-generated method stub
386        throw new UnsupportedOperationException();
387    }
388
389    @Override
390    public List<CmisExtensionElement> getExtensions(ExtensionLevel level) {
391        // TODO Auto-generated method stub
392        throw new UnsupportedOperationException();
393    }
394
395    @Override
396    public boolean hasAllowableAction(Action action) {
397        // TODO Auto-generated method stub
398        throw new UnsupportedOperationException();
399    }
400
401    @Override
402    public Set<String> getPermissionsForPrincipal(String principalId) {
403        throw new UnsupportedOperationException();
404    }
405
406    @Override
407    public void applyPolicy(ObjectId policyId, boolean refresh) {
408        throw new UnsupportedOperationException();
409    }
410
411    @Override
412    public void removePolicy(ObjectId policyId, boolean refresh) {
413        throw new UnsupportedOperationException();
414    }
415
416}