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.Collections;
030import java.util.HashMap;
031import java.util.HashSet;
032import java.util.List;
033import java.util.Map;
034import java.util.Set;
035
036import org.nuxeo.common.collections.ScopeType;
037import org.nuxeo.common.collections.ScopedMap;
038import org.nuxeo.common.utils.Path;
039import org.nuxeo.ecm.core.api.CoreSession;
040import org.nuxeo.ecm.core.api.DataModel;
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 Map<String, DataModel> dataModels = new HashMap<>();
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 Property getPropertyObject(String schema, String name) {
206        DocumentPart part = getPart(schema);
207        return part == null ? null : part.get(name);
208    }
209
210    @Override
211    public void setProperty(String schemaName, String name, Object value) {
212        if (name.contains(":")) {
213            name = name.substring(name.indexOf(":"), name.length());
214        }
215        getDataModelInternal(schemaName).setData(name, value);
216    }
217
218    @Override
219    public Map<String, Object> getProperties(String schemaName) {
220        return getDataModelInternal(schemaName).getMap();
221    }
222
223    @Override
224    public void setProperties(String schemaName, Map<String, Object> data) {
225        DataModel dm = getDataModelInternal(schemaName);
226        dm.setMap(data);
227        // force dirty for updated properties
228        for (String field : data.keySet()) {
229            dm.setDirty(field);
230        }
231    }
232
233    @Override
234    public ScopedMap getContextData() {
235        return contextData;
236    }
237
238    @Override
239    public Serializable getContextData(ScopeType scope, String key) {
240        return contextData.getScopedValue(scope, key);
241    }
242
243    @Override
244    public void putContextData(ScopeType scope, String key, Serializable value) {
245        contextData.putScopedValue(scope, key, value);
246    }
247
248    @Override
249    public Serializable getContextData(String key) {
250        return contextData.getScopedValue(key);
251    }
252
253    @Override
254    public void putContextData(String key, Serializable value) {
255        contextData.putScopedValue(key, value);
256    }
257
258    @Override
259    public void copyContextData(DocumentModel otherDocument) {
260        ScopedMap otherMap = otherDocument.getContextData();
261        if (otherMap != null) {
262            contextData.putAll(otherMap);
263        }
264    }
265
266    @Override
267    public Property getProperty(String xpath) throws PropertyException {
268        if (xpath == null) {
269            throw new PropertyNotFoundException("null", "Invalid null xpath");
270        }
271        String cxpath = canonicalXPath(xpath);
272        if (cxpath.isEmpty()) {
273            throw new PropertyNotFoundException(xpath, "Schema not specified");
274        }
275        String schemaName = DocumentModelImpl.getXPathSchemaName(cxpath, schemas, null);
276        if (schemaName == null) {
277            if (cxpath.indexOf(':') != -1) {
278                throw new PropertyNotFoundException(xpath, "No such schema");
279            } else {
280                throw new PropertyNotFoundException(xpath);
281            }
282
283        }
284        DocumentPart part = getPart(schemaName);
285        if (part == null) {
286            throw new PropertyNotFoundException(xpath);
287        }
288        // cut prefix
289        String partPath = cxpath.substring(cxpath.indexOf(':') + 1);
290        try {
291            Property property = part.resolvePath(partPath);
292                    // force dirty for updated properties
293            property.setForceDirty(true);
294            return property;
295        } catch (PropertyNotFoundException e) {
296            throw new PropertyNotFoundException(xpath, e.getDetail());
297        }
298    }
299
300    @Override
301    public Serializable getPropertyValue(String xpath) throws PropertyException {
302        return getProperty(xpath).getValue();
303    }
304
305    @Override
306    public void setPropertyValue(String xpath, Serializable value) {
307        getProperty(xpath).setValue(value);
308    }
309
310    @Override
311    public DocumentType getDocumentType() {
312        throw new UnsupportedOperationException();
313    }
314
315    @Override
316    public String getSessionId() {
317        throw new UnsupportedOperationException();
318    }
319
320    @Override
321    public CoreSession getCoreSession() {
322        throw new UnsupportedOperationException();
323    }
324
325    @Override
326    public void detach(boolean loadAll) {
327    }
328
329    @Override
330    public void attach(String sid) {
331    }
332
333    @Override
334    public DocumentRef getRef() {
335        throw new UnsupportedOperationException();
336    }
337
338    @Override
339    public DocumentRef getParentRef() {
340        if (path == null) {
341            return null;
342        }
343        if (!path.isAbsolute()) {
344            return null;
345        }
346        return new PathRef(path.removeLastSegments(1).toString());
347    }
348
349    @Override
350    public String getId() {
351        throw new UnsupportedOperationException();
352    }
353
354    @Override
355    public String getName() {
356        return path == null ? null : path.lastSegment();
357    }
358
359    @Override
360    public Long getPos() {
361        return null;
362    }
363
364    @Override
365    public String getPathAsString() {
366        return path == null ? null : path.toString();
367    }
368
369    @Override
370    public Path getPath() {
371        return path;
372    }
373
374    @Override
375    public String getTitle() {
376        throw new UnsupportedOperationException();
377    }
378
379    @Override
380    public String getType() {
381        return type;
382    }
383
384    public void setType(String type) {
385        this.type = type;
386    }
387
388    @Override
389    public Set<String> getFacets() {
390        throw new UnsupportedOperationException();
391    }
392
393    @Override
394    public Set<String> getDeclaredFacets() {
395        return getFacets();
396    }
397
398    @Override
399    public Collection<DataModel> getDataModelsCollection() {
400        throw new UnsupportedOperationException();
401    }
402
403    @Override
404    public Map<String, DataModel> getDataModels() {
405        return dataModels;
406    }
407
408    @Override
409    public DataModel getDataModel(String schema) {
410        return getDataModelInternal(schema);
411    }
412
413    @Override
414    public void setPathInfo(String parentPath, String name) {
415        path = new Path(parentPath == null ? name : parentPath + '/' + name);
416    }
417
418    @Override
419    public String getLock() {
420        throw new UnsupportedOperationException();
421    }
422
423    @Override
424    public boolean isLocked() {
425        throw new UnsupportedOperationException();
426    }
427
428    @Override
429    public void setLock(String key) {
430        throw new UnsupportedOperationException();
431    }
432
433    @Override
434    public void unlock() {
435        throw new UnsupportedOperationException();
436    }
437
438    @Override
439    public Lock setLock() {
440        throw new UnsupportedOperationException();
441    }
442
443    @Override
444    public Lock getLockInfo() {
445        throw new UnsupportedOperationException();
446    }
447
448    @Override
449    public Lock removeLock() {
450        throw new UnsupportedOperationException();
451    }
452
453    @Override
454    public ACP getACP() {
455        throw new UnsupportedOperationException();
456    }
457
458    @Override
459    public void setACP(ACP acp, boolean overwrite) {
460        throw new UnsupportedOperationException();
461    }
462
463    @Override
464    public boolean hasSchema(String schema) {
465        throw new UnsupportedOperationException();
466    }
467
468    @Override
469    public boolean hasFacet(String facet) {
470        throw new UnsupportedOperationException();
471    }
472
473    @Override
474    public boolean addFacet(String facet) {
475        throw new UnsupportedOperationException();
476    }
477
478    @Override
479    public boolean removeFacet(String facet) {
480        throw new UnsupportedOperationException();
481    }
482
483    @Override
484    public boolean isFolder() {
485        throw new UnsupportedOperationException();
486    }
487
488    @Override
489    public boolean isVersionable() {
490        throw new UnsupportedOperationException();
491    }
492
493    @Override
494    public boolean isDownloadable() {
495        throw new UnsupportedOperationException();
496    }
497
498    @Override
499    public boolean isVersion() {
500        throw new UnsupportedOperationException();
501    }
502
503    @Override
504    public boolean isProxy() {
505        throw new UnsupportedOperationException();
506    }
507
508    @Override
509    public boolean isImmutable() {
510        throw new UnsupportedOperationException();
511    }
512
513    @Override
514    public boolean isDirty() {
515        throw new UnsupportedOperationException();
516    }
517
518    @Override
519    public void accept(PropertyVisitor visitor, Object arg) {
520        throw new UnsupportedOperationException();
521    }
522
523    @Override
524    public <T> T getAdapter(Class<T> itf) {
525        throw new UnsupportedOperationException();
526    }
527
528    @Override
529    public <T> T getAdapter(Class<T> itf, boolean refreshCache) {
530        throw new UnsupportedOperationException();
531    }
532
533    @Override
534    public String getCurrentLifeCycleState() {
535        throw new UnsupportedOperationException();
536    }
537
538    @Override
539    public String getLifeCyclePolicy() {
540        throw new UnsupportedOperationException();
541    }
542
543    @Override
544    public boolean followTransition(String transition) {
545        throw new UnsupportedOperationException();
546    }
547
548    @Override
549    public Collection<String> getAllowedStateTransitions() {
550        throw new UnsupportedOperationException();
551    }
552
553    @Override
554    public void copyContent(DocumentModel sourceDoc) {
555        throw new UnsupportedOperationException();
556    }
557
558    @Override
559    public String getRepositoryName() {
560        throw new UnsupportedOperationException();
561    }
562
563    @Override
564    public String getCacheKey() {
565        throw new UnsupportedOperationException();
566    }
567
568    @Override
569    public String getSourceId() {
570        throw new UnsupportedOperationException();
571    }
572
573    @Override
574    public String getVersionLabel() {
575        throw new UnsupportedOperationException();
576    }
577
578    @Override
579    public String getCheckinComment() {
580        throw new UnsupportedOperationException();
581    }
582
583    @Override
584    public boolean isPrefetched(String xpath) {
585        return false;
586    }
587
588    @Override
589    public boolean isPrefetched(String schemaName, String name) {
590        return false;
591    }
592
593    @Override
594    public void prefetchCurrentLifecycleState(String lifecycle) {
595        throw new UnsupportedOperationException();
596    }
597
598    @Override
599    public void prefetchLifeCyclePolicy(String lifeCyclePolicy) {
600        throw new UnsupportedOperationException();
601    }
602
603    @Override
604    public boolean isLifeCycleLoaded() {
605        return false;
606    }
607
608    @Override
609    public <T extends Serializable> T getSystemProp(String systemProperty, Class<T> type) {
610        throw new UnsupportedOperationException();
611    }
612
613    @Override
614    public DocumentPart getPart(String schema) {
615        DataModel dm = getDataModel(schema);
616        if (dm != null) {
617            return ((DataModelImpl) dm).getDocumentPart();
618        }
619        return null;
620    }
621
622    @Override
623    public DocumentPart[] getParts() {
624        throw new UnsupportedOperationException();
625    }
626
627    @Override
628    public Collection<Property> getPropertyObjects(String schema) {
629        DocumentPart part = getPart(schema);
630        return part == null ? Collections.emptyList() : part.getChildren();
631    }
632
633    @Override
634    public void reset() {
635        throw new UnsupportedOperationException();
636    }
637
638    @Override
639    public void refresh(int refreshFlags, String[] schemas) {
640        throw new UnsupportedOperationException();
641    }
642
643    @Override
644    public void refresh() {
645        throw new UnsupportedOperationException();
646    }
647
648    @Override
649    public DocumentModel clone() throws CloneNotSupportedException {
650        throw new UnsupportedOperationException();
651    }
652
653    @Override
654    public boolean isCheckedOut() {
655        throw new UnsupportedOperationException();
656    }
657
658    @Override
659    public void checkOut() {
660        throw new UnsupportedOperationException();
661    }
662
663    @Override
664    public DocumentRef checkIn(VersioningOption option, String description) {
665        throw new UnsupportedOperationException();
666    }
667
668    @Override
669    public String getVersionSeriesId() {
670        throw new UnsupportedOperationException();
671    }
672
673    @Override
674    public boolean isLatestVersion() {
675        return false;
676    }
677
678    @Override
679    public boolean isMajorVersion() {
680        return false;
681    }
682
683    @Override
684    public boolean isLatestMajorVersion() {
685        return false;
686    }
687
688    @Override
689    public boolean isVersionSeriesCheckedOut() {
690        return true;
691    }
692
693    @Override
694    public String getChangeToken() {
695        return null;
696    }
697
698    @Override
699    public Map<String, String> getBinaryFulltext() {
700        return null;
701    }
702
703    @Override
704    public PropertyObjectResolver getObjectResolver(String xpath) {
705        return DocumentPropertyObjectResolverImpl.create(this, xpath);
706    }
707
708}