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