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