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}