001/*
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Thomas Roger
011 *     Dragos Mihalache
012 *     Florent Guillaume
013 */
014
015package org.nuxeo.ecm.core.api.impl;
016
017import static org.nuxeo.ecm.core.schema.types.ComplexTypeImpl.canonicalXPath;
018
019import java.io.Serializable;
020import java.util.Arrays;
021import java.util.Collection;
022import java.util.HashMap;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027
028import org.nuxeo.common.collections.ScopeType;
029import org.nuxeo.common.collections.ScopedMap;
030import org.nuxeo.common.utils.Path;
031import org.nuxeo.ecm.core.api.CoreSession;
032import org.nuxeo.ecm.core.api.DataModel;
033import org.nuxeo.ecm.core.api.DataModelMap;
034import org.nuxeo.ecm.core.api.DocumentModel;
035import org.nuxeo.ecm.core.api.DocumentRef;
036import org.nuxeo.ecm.core.api.Lock;
037import org.nuxeo.ecm.core.api.PathRef;
038import org.nuxeo.ecm.core.api.PropertyException;
039import org.nuxeo.ecm.core.api.VersioningOption;
040import org.nuxeo.ecm.core.api.model.DocumentPart;
041import org.nuxeo.ecm.core.api.model.Property;
042import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
043import org.nuxeo.ecm.core.api.model.PropertyVisitor;
044import org.nuxeo.ecm.core.api.model.impl.DocumentPartImpl;
045import org.nuxeo.ecm.core.api.model.resolver.DocumentPropertyObjectResolverImpl;
046import org.nuxeo.ecm.core.api.model.resolver.PropertyObjectResolver;
047import org.nuxeo.ecm.core.api.security.ACP;
048import org.nuxeo.ecm.core.schema.DocumentType;
049import org.nuxeo.ecm.core.schema.SchemaManager;
050import org.nuxeo.ecm.core.schema.types.Schema;
051import org.nuxeo.runtime.api.Framework;
052
053/**
054 * A DocumentModel that can have any schema and is not made persistent by itself. A mockup to keep arbitrary schema
055 * data.
056 */
057public class SimpleDocumentModel implements DocumentModel {
058
059    private static final long serialVersionUID = 1L;
060
061    protected final boolean anySchema;
062
063    protected final DataModelMap dataModels = new DataModelMapImpl();
064
065    protected Set<String> schemas;
066
067    protected final ScopedMap contextData = new ScopedMap();
068
069    protected Path path;
070
071    protected String type;
072
073    public SimpleDocumentModel(List<String> schemas) {
074        this.schemas = new HashSet<>();
075        anySchema = false;
076        SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class);
077        for (String schema : schemas) {
078            Schema s = schemaManager.getSchema(schema);
079            DocumentPart part = new DocumentPartImpl(s);
080            dataModels.put(schema, new DataModelImpl(part));
081            this.schemas.add(schema);
082        }
083    }
084
085    public SimpleDocumentModel(String... schemas) {
086        this(Arrays.asList(schemas));
087    }
088
089    public SimpleDocumentModel() {
090        schemas = new HashSet<>();
091        anySchema = true;
092    }
093
094    /**
095     * A data model that is not tied to a particular schema, neither has anything to do with a session (CoreSession).
096     *
097     * @deprecated since 5.7.3. Use standard {@link DataModelImpl} instead.
098     */
099    @Deprecated
100    public static class SimpleDataModel implements DataModel {
101
102        private static final long serialVersionUID = 1L;
103
104        public final String schema;
105
106        public final Map<String, Object> data = new HashMap<String, Object>();
107
108        public SimpleDataModel(String schema) {
109            this.schema = schema;
110        }
111
112        @Override
113        public void setData(String key, Object value) throws PropertyException {
114            data.put(key, value);
115        }
116
117        @Override
118        public Object getData(String key) throws PropertyException {
119            return data.get(key);
120        }
121
122        @Override
123        public String getSchema() {
124            return schema;
125        }
126
127        @Override
128        public Map<String, Object> getMap() throws PropertyException {
129            return data;
130        }
131
132        @Override
133        public void setMap(Map<String, Object> data) throws PropertyException {
134            data = new HashMap<String, Object>(data);
135        }
136
137        @Override
138        public boolean isDirty() {
139            return true;
140        }
141
142        @Override
143        public boolean isDirty(String name) throws PropertyNotFoundException {
144            return true;
145        }
146
147        @Override
148        public void setDirty(String name) throws PropertyNotFoundException {
149        }
150
151        @Override
152        public Collection<String> getDirtyFields() {
153            return data.keySet();
154        }
155
156        @Override
157        public Object getValue(String path) throws PropertyException {
158            throw new UnsupportedOperationException("getValue");
159        }
160
161        @Override
162        public Object setValue(String path, Object value) throws PropertyException {
163            throw new UnsupportedOperationException("setValue");
164        }
165    }
166
167    protected DataModel getDataModelInternal(String schema) {
168        DataModel dm = dataModels.get(schema);
169        if (dm == null && anySchema) {
170            SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class);
171            Schema s = schemaManager.getSchema(schema);
172            DocumentPart part = new DocumentPartImpl(s);
173            dm = new DataModelImpl(part);
174            dataModels.put(schema, dm);
175            schemas.add(schema);
176        }
177        return dm;
178    }
179
180    @Override
181    public String[] getSchemas() {
182        Set<String> keys = dataModels.keySet();
183        return keys.toArray(new String[keys.size()]);
184    }
185
186    @Override
187    public String[] getDeclaredSchemas() {
188        return getSchemas();
189    }
190
191    @Override
192    public Object getProperty(String schemaName, String name) {
193        DataModel dm = getDataModelInternal(schemaName);
194        return dm != null ? dm.getData(name) : null;
195    }
196
197    @Override
198    public void setProperty(String schemaName, String name, Object value) {
199        if (name.contains(":")) {
200            name = name.substring(name.indexOf(":"), name.length());
201        }
202        getDataModelInternal(schemaName).setData(name, value);
203    }
204
205    @Override
206    public Map<String, Object> getProperties(String schemaName) {
207        return getDataModelInternal(schemaName).getMap();
208    }
209
210    @Override
211    public void setProperties(String schemaName, Map<String, Object> data) {
212        DataModel dm = getDataModelInternal(schemaName);
213        dm.setMap(data);
214        // force dirty for updated properties
215        for (String field : data.keySet()) {
216            dm.setDirty(field);
217        }
218    }
219
220    @Override
221    public ScopedMap getContextData() {
222        return contextData;
223    }
224
225    @Override
226    public Serializable getContextData(ScopeType scope, String key) {
227        return contextData.getScopedValue(scope, key);
228    }
229
230    @Override
231    public void putContextData(ScopeType scope, String key, Serializable value) {
232        contextData.putScopedValue(scope, key, value);
233    }
234
235    @Override
236    public Serializable getContextData(String key) {
237        return contextData.getScopedValue(key);
238    }
239
240    @Override
241    public void putContextData(String key, Serializable value) {
242        contextData.putScopedValue(key, value);
243    }
244
245    @Override
246    public void copyContextData(DocumentModel otherDocument) {
247        ScopedMap otherMap = otherDocument.getContextData();
248        if (otherMap != null) {
249            contextData.putAll(otherMap);
250        }
251    }
252
253    @Override
254    public Property getProperty(String xpath) throws PropertyException {
255        if (xpath == null) {
256            throw new PropertyNotFoundException("null", "Invalid null xpath");
257        }
258        String cxpath = canonicalXPath(xpath);
259        if (cxpath.isEmpty()) {
260            throw new PropertyNotFoundException(xpath, "Schema not specified");
261        }
262        String schemaName = DocumentModelImpl.getXPathSchemaName(cxpath, schemas, null);
263        if (schemaName == null) {
264            if (cxpath.indexOf(':') != -1) {
265                throw new PropertyNotFoundException(xpath, "No such schema");
266            } else {
267                throw new PropertyNotFoundException(xpath);
268            }
269
270        }
271        DocumentPart part = getPart(schemaName);
272        if (part == null) {
273            throw new PropertyNotFoundException(xpath);
274        }
275        // cut prefix
276        String partPath = cxpath.substring(cxpath.indexOf(':') + 1);
277        try {
278            Property property = part.resolvePath(partPath);
279                    // force dirty for updated properties
280            property.setForceDirty(true);
281            return property;
282        } catch (PropertyNotFoundException e) {
283            throw new PropertyNotFoundException(xpath, e.getDetail());
284        }
285    }
286
287    @Override
288    public Serializable getPropertyValue(String xpath) throws PropertyException {
289        return getProperty(xpath).getValue();
290    }
291
292    @Override
293    public void setPropertyValue(String xpath, Serializable value) {
294        getProperty(xpath).setValue(value);
295    }
296
297    @Override
298    public DocumentType getDocumentType() {
299        throw new UnsupportedOperationException();
300    }
301
302    @Override
303    public String getSessionId() {
304        throw new UnsupportedOperationException();
305    }
306
307    @Override
308    public CoreSession getCoreSession() {
309        throw new UnsupportedOperationException();
310    }
311
312    @Override
313    public void detach(boolean loadAll) {
314    }
315
316    @Override
317    public void attach(String sid) {
318    }
319
320    @Override
321    public DocumentRef getRef() {
322        throw new UnsupportedOperationException();
323    }
324
325    @Override
326    public DocumentRef getParentRef() {
327        if (path == null) {
328            return null;
329        }
330        if (!path.isAbsolute()) {
331            return null;
332        }
333        return new PathRef(path.removeLastSegments(1).toString());
334    }
335
336    @Override
337    public String getId() {
338        throw new UnsupportedOperationException();
339    }
340
341    @Override
342    public String getName() {
343        return path == null ? null : path.lastSegment();
344    }
345
346    @Override
347    public Long getPos() {
348        return null;
349    }
350
351    @Override
352    public String getPathAsString() {
353        return path == null ? null : path.toString();
354    }
355
356    @Override
357    public Path getPath() {
358        return path;
359    }
360
361    @Override
362    public String getTitle() {
363        throw new UnsupportedOperationException();
364    }
365
366    @Override
367    public String getType() {
368        return type;
369    }
370
371    public void setType(String type) {
372        this.type = type;
373    }
374
375    @Override
376    public Set<String> getFacets() {
377        throw new UnsupportedOperationException();
378    }
379
380    @Override
381    public Set<String> getDeclaredFacets() {
382        return getFacets();
383    }
384
385    @Override
386    public Collection<DataModel> getDataModelsCollection() {
387        throw new UnsupportedOperationException();
388    }
389
390    @Override
391    public DataModelMap getDataModels() {
392        return dataModels;
393    }
394
395    @Override
396    public DataModel getDataModel(String schema) {
397        return getDataModelInternal(schema);
398    }
399
400    @Override
401    public void setPathInfo(String parentPath, String name) {
402        path = new Path(parentPath == null ? name : parentPath + '/' + name);
403    }
404
405    @Override
406    public String getLock() {
407        throw new UnsupportedOperationException();
408    }
409
410    @Override
411    public boolean isLocked() {
412        throw new UnsupportedOperationException();
413    }
414
415    @Override
416    public void setLock(String key) {
417        throw new UnsupportedOperationException();
418    }
419
420    @Override
421    public void unlock() {
422        throw new UnsupportedOperationException();
423    }
424
425    @Override
426    public Lock setLock() {
427        throw new UnsupportedOperationException();
428    }
429
430    @Override
431    public Lock getLockInfo() {
432        throw new UnsupportedOperationException();
433    }
434
435    @Override
436    public Lock removeLock() {
437        throw new UnsupportedOperationException();
438    }
439
440    @Override
441    public ACP getACP() {
442        throw new UnsupportedOperationException();
443    }
444
445    @Override
446    public void setACP(ACP acp, boolean overwrite) {
447        throw new UnsupportedOperationException();
448    }
449
450    @Override
451    public boolean hasSchema(String schema) {
452        throw new UnsupportedOperationException();
453    }
454
455    @Override
456    public boolean hasFacet(String facet) {
457        throw new UnsupportedOperationException();
458    }
459
460    @Override
461    public boolean addFacet(String facet) {
462        throw new UnsupportedOperationException();
463    }
464
465    @Override
466    public boolean removeFacet(String facet) {
467        throw new UnsupportedOperationException();
468    }
469
470    @Override
471    public boolean isFolder() {
472        throw new UnsupportedOperationException();
473    }
474
475    @Override
476    public boolean isVersionable() {
477        throw new UnsupportedOperationException();
478    }
479
480    @Override
481    public boolean isDownloadable() {
482        throw new UnsupportedOperationException();
483    }
484
485    @Override
486    public boolean isVersion() {
487        throw new UnsupportedOperationException();
488    }
489
490    @Override
491    public boolean isProxy() {
492        throw new UnsupportedOperationException();
493    }
494
495    @Override
496    public boolean isImmutable() {
497        throw new UnsupportedOperationException();
498    }
499
500    @Override
501    public boolean isDirty() {
502        throw new UnsupportedOperationException();
503    }
504
505    @Override
506    public void accept(PropertyVisitor visitor, Object arg) {
507        throw new UnsupportedOperationException();
508    }
509
510    @Override
511    public <T> T getAdapter(Class<T> itf) {
512        throw new UnsupportedOperationException();
513    }
514
515    @Override
516    public <T> T getAdapter(Class<T> itf, boolean refreshCache) {
517        throw new UnsupportedOperationException();
518    }
519
520    @Override
521    public String getCurrentLifeCycleState() {
522        throw new UnsupportedOperationException();
523    }
524
525    @Override
526    public String getLifeCyclePolicy() {
527        throw new UnsupportedOperationException();
528    }
529
530    @Override
531    public boolean followTransition(String transition) {
532        throw new UnsupportedOperationException();
533    }
534
535    @Override
536    public Collection<String> getAllowedStateTransitions() {
537        throw new UnsupportedOperationException();
538    }
539
540    @Override
541    public void copyContent(DocumentModel sourceDoc) {
542        throw new UnsupportedOperationException();
543    }
544
545    @Override
546    public String getRepositoryName() {
547        throw new UnsupportedOperationException();
548    }
549
550    @Override
551    public String getCacheKey() {
552        throw new UnsupportedOperationException();
553    }
554
555    @Override
556    public String getSourceId() {
557        throw new UnsupportedOperationException();
558    }
559
560    @Override
561    public String getVersionLabel() {
562        throw new UnsupportedOperationException();
563    }
564
565    @Override
566    public String getCheckinComment() {
567        throw new UnsupportedOperationException();
568    }
569
570    @Override
571    public boolean isPrefetched(String xpath) {
572        return false;
573    }
574
575    @Override
576    public boolean isPrefetched(String schemaName, String name) {
577        return false;
578    }
579
580    @Override
581    public void prefetchCurrentLifecycleState(String lifecycle) {
582        throw new UnsupportedOperationException();
583    }
584
585    @Override
586    public void prefetchLifeCyclePolicy(String lifeCyclePolicy) {
587        throw new UnsupportedOperationException();
588    }
589
590    @Override
591    public boolean isLifeCycleLoaded() {
592        return false;
593    }
594
595    @Override
596    public <T extends Serializable> T getSystemProp(String systemProperty, Class<T> type) {
597        throw new UnsupportedOperationException();
598    }
599
600    @Override
601    public DocumentPart getPart(String schema) {
602        DataModel dm = getDataModel(schema);
603        if (dm != null) {
604            return ((DataModelImpl) dm).getDocumentPart();
605        }
606        return null;
607    }
608
609    @Override
610    public DocumentPart[] getParts() {
611        throw new UnsupportedOperationException();
612    }
613
614    @Override
615    public void reset() {
616        throw new UnsupportedOperationException();
617    }
618
619    @Override
620    public void refresh(int refreshFlags, String[] schemas) {
621        throw new UnsupportedOperationException();
622    }
623
624    @Override
625    public void refresh() {
626        throw new UnsupportedOperationException();
627    }
628
629    @Override
630    public DocumentModel clone() throws CloneNotSupportedException {
631        throw new UnsupportedOperationException();
632    }
633
634    @Override
635    public boolean isCheckedOut() {
636        throw new UnsupportedOperationException();
637    }
638
639    @Override
640    public void checkOut() {
641        throw new UnsupportedOperationException();
642    }
643
644    @Override
645    public DocumentRef checkIn(VersioningOption option, String description) {
646        throw new UnsupportedOperationException();
647    }
648
649    @Override
650    public String getVersionSeriesId() {
651        throw new UnsupportedOperationException();
652    }
653
654    @Override
655    public boolean isLatestVersion() {
656        return false;
657    }
658
659    @Override
660    public boolean isMajorVersion() {
661        return false;
662    }
663
664    @Override
665    public boolean isLatestMajorVersion() {
666        return false;
667    }
668
669    @Override
670    public boolean isVersionSeriesCheckedOut() {
671        return true;
672    }
673
674    @Override
675    public String getChangeToken() {
676        return null;
677    }
678
679    @Override
680    public Map<String, String> getBinaryFulltext() {
681        return null;
682    }
683
684    @Override
685    public PropertyObjectResolver getObjectResolver(String xpath) {
686        return DocumentPropertyObjectResolverImpl.create(this, xpath);
687    }
688
689}