001/* 002 * Copyright (c) 2006-2014 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 * Florent Guillaume 011 */ 012package org.nuxeo.ecm.core.storage.sql.coremodel; 013 014import java.io.Serializable; 015import java.util.Calendar; 016import java.util.Collection; 017import java.util.List; 018import java.util.Map; 019import java.util.Set; 020import java.util.function.Consumer; 021 022import org.nuxeo.ecm.core.api.LifeCycleException; 023import org.nuxeo.ecm.core.api.Lock; 024import org.nuxeo.ecm.core.api.PropertyException; 025import org.nuxeo.ecm.core.api.model.DocumentPart; 026import org.nuxeo.ecm.core.api.model.PropertyNotFoundException; 027import org.nuxeo.ecm.core.api.model.ReadOnlyPropertyException; 028import org.nuxeo.ecm.core.model.Document; 029import org.nuxeo.ecm.core.model.Session; 030import org.nuxeo.ecm.core.schema.DocumentType; 031import org.nuxeo.ecm.core.schema.SchemaManager; 032import org.nuxeo.ecm.core.schema.types.ComplexType; 033import org.nuxeo.ecm.core.schema.types.Schema; 034import org.nuxeo.ecm.core.storage.sql.Model; 035import org.nuxeo.ecm.core.storage.sql.Node; 036import org.nuxeo.runtime.api.Framework; 037 038/** 039 * A proxy is a shortcut to a target document (a version or normal document). 040 */ 041public class SQLDocumentProxy implements SQLDocument { 042 043 /** The proxy seen as a normal doc ({@link SQLDocument}). */ 044 private final Document proxy; 045 046 /** The target. */ 047 private Document target; 048 049 // private SQLDocumentVersion version; 050 051 protected SQLDocumentProxy(Document proxy, Document target) { 052 this.proxy = proxy; 053 this.target = target; 054 } 055 056 protected String getSchema(String xpath) { 057 int p = xpath.indexOf(':'); 058 if (p == -1) { 059 throw new PropertyNotFoundException(xpath, "Schema not specified"); 060 } 061 String prefix = xpath.substring(0, p); 062 SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); 063 Schema schema = schemaManager.getSchemaFromPrefix(prefix); 064 if (schema == null) { 065 schema = schemaManager.getSchema(prefix); 066 if (schema == null) { 067 throw new PropertyNotFoundException(xpath, "No schema for prefix"); 068 } 069 } 070 return schema.getName(); 071 } 072 073 /** 074 * Checks if the given schema should be resolved on the proxy or the target. 075 */ 076 protected boolean isSchemaForProxy(String schema) { 077 SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); 078 return schemaManager.isProxySchema(schema, getType().getName()); 079 } 080 081 /** 082 * Checks if the given property should be resolved on the proxy or the target. 083 */ 084 protected boolean isPropertyForProxy(String xpath) { 085 if (Model.MAIN_MINOR_VERSION_PROP.equals(xpath) || Model.MAIN_MAJOR_VERSION_PROP.equals(xpath)) { 086 return false; 087 } 088 return isSchemaForProxy(getSchema(xpath)); 089 } 090 091 /* 092 * ----- SQLDocument ----- 093 */ 094 095 @Override 096 public Node getNode() { 097 return ((SQLDocument) proxy).getNode(); 098 } 099 100 /* 101 * ----- Document ----- 102 */ 103 104 @Override 105 public boolean isProxy() { 106 return true; 107 } 108 109 @Override 110 public String getUUID() { 111 return proxy.getUUID(); 112 } 113 114 @Override 115 public String getName() { 116 return proxy.getName(); 117 } 118 119 @Override 120 public Long getPos() { 121 return proxy.getPos(); 122 } 123 124 @Override 125 public Document getParent() { 126 return proxy.getParent(); 127 } 128 129 @Override 130 public String getPath() { 131 return proxy.getPath(); 132 } 133 134 @Override 135 public void remove() { 136 proxy.remove(); 137 } 138 139 @Override 140 public DocumentType getType() { 141 return target.getType(); 142 } 143 144 @Override 145 public String getRepositoryName() { 146 return target.getRepositoryName(); 147 } 148 149 @Override 150 public Session getSession() { 151 return target.getSession(); 152 } 153 154 @Override 155 public boolean isFolder() { 156 return target.isFolder(); 157 } 158 159 @Override 160 public void setReadOnly(boolean readonly) { 161 target.setReadOnly(readonly); 162 } 163 164 @Override 165 public boolean isReadOnly() { 166 return target.isReadOnly(); 167 } 168 169 @Override 170 public void readDocumentPart(DocumentPart dp) throws PropertyException { 171 if (isSchemaForProxy(dp.getName())) { 172 proxy.readDocumentPart(dp); 173 } else { 174 target.readDocumentPart(dp); 175 } 176 } 177 178 @Override 179 public Map<String, Serializable> readPrefetch(ComplexType complexType, Set<String> xpaths) 180 throws PropertyException { 181 if (isSchemaForProxy(complexType.getName())) { 182 return proxy.readPrefetch(complexType, xpaths); 183 } else { 184 return target.readPrefetch(complexType, xpaths); 185 } 186 } 187 188 @Override 189 public WriteContext getWriteContext() { 190 // proxy or target doesn't matter, this is about typing 191 return proxy.getWriteContext(); 192 } 193 194 @Override 195 public boolean writeDocumentPart(DocumentPart dp, WriteContext writeContext) throws PropertyException { 196 if (isSchemaForProxy(dp.getName())) { 197 return proxy.writeDocumentPart(dp, writeContext); 198 } else { 199 return target.writeDocumentPart(dp, writeContext); 200 } 201 } 202 203 @Override 204 public void setSystemProp(String name, Serializable value) { 205 target.setSystemProp(name, value); 206 } 207 208 @Override 209 public <T extends Serializable> T getSystemProp(String name, Class<T> type) { 210 return target.getSystemProp(name, type); 211 } 212 213 @Override 214 public Set<String> getAllFacets() { 215 return target.getAllFacets(); // TODO proxy facets 216 } 217 218 @Override 219 public String[] getFacets() { 220 return target.getFacets(); // TODO proxy facets 221 } 222 223 @Override 224 public boolean hasFacet(String facet) { 225 return target.hasFacet(facet); // TODO proxy facets 226 } 227 228 @Override 229 public boolean addFacet(String facet) { 230 return target.addFacet(facet); // TODO proxy facets 231 } 232 233 @Override 234 public boolean removeFacet(String facet) { 235 return target.removeFacet(facet); // TODO proxy facets 236 } 237 238 /* 239 * ----- LifeCycle ----- 240 */ 241 242 @Override 243 public String getLifeCyclePolicy() { 244 return target.getLifeCyclePolicy(); 245 } 246 247 @Override 248 public void setLifeCyclePolicy(String policy) { 249 target.setLifeCyclePolicy(policy); 250 } 251 252 @Override 253 public String getLifeCycleState() { 254 return target.getLifeCycleState(); 255 } 256 257 @Override 258 public void setCurrentLifeCycleState(String state) { 259 target.setCurrentLifeCycleState(state); 260 } 261 262 @Override 263 public void followTransition(String transition) throws LifeCycleException { 264 target.followTransition(transition); 265 } 266 267 @Override 268 public Collection<String> getAllowedStateTransitions() { 269 return target.getAllowedStateTransitions(); 270 } 271 272 @Override 273 public Lock getLock() { 274 return target.getLock(); 275 } 276 277 @Override 278 public Lock setLock(Lock lock) { 279 return target.setLock(lock); 280 } 281 282 @Override 283 public Lock removeLock(String owner) { 284 return target.removeLock(owner); 285 } 286 287 @Override 288 public boolean isVersion() { 289 return false; 290 } 291 292 @Override 293 public Document getBaseVersion() { 294 return null; 295 } 296 297 @Override 298 public String getVersionSeriesId() { 299 return target.getVersionSeriesId(); 300 } 301 302 @Override 303 public Document getSourceDocument() { 304 // this is what the rest of Nuxeo expects for a proxy 305 return target; 306 } 307 308 @Override 309 public Document checkIn(String label, String checkinComment) { 310 return target.checkIn(label, checkinComment); 311 } 312 313 @Override 314 public void checkOut() { 315 target.checkOut(); 316 } 317 318 @Override 319 public boolean isCheckedOut() { 320 return target.isCheckedOut(); 321 } 322 323 @Override 324 public boolean isLatestVersion() { 325 return target.isLatestVersion(); 326 } 327 328 @Override 329 public boolean isMajorVersion() { 330 return target.isMajorVersion(); 331 } 332 333 @Override 334 public boolean isLatestMajorVersion() { 335 return target.isLatestMajorVersion(); 336 } 337 338 @Override 339 public boolean isVersionSeriesCheckedOut() { 340 return target.isVersionSeriesCheckedOut(); 341 } 342 343 @Override 344 public String getVersionLabel() { 345 return target.getVersionLabel(); 346 } 347 348 @Override 349 public String getCheckinComment() { 350 return target.getCheckinComment(); 351 } 352 353 @Override 354 public Document getWorkingCopy() { 355 return target.getWorkingCopy(); 356 } 357 358 @Override 359 public Calendar getVersionCreationDate() { 360 return target.getVersionCreationDate(); 361 } 362 363 @Override 364 public void restore(Document version) { 365 target.restore(version); 366 } 367 368 @Override 369 public List<String> getVersionsIds() { 370 return target.getVersionsIds(); 371 } 372 373 @Override 374 public Document getVersion(String label) { 375 return target.getVersion(label); 376 } 377 378 @Override 379 public List<Document> getVersions() { 380 return target.getVersions(); 381 } 382 383 @Override 384 public Document getLastVersion() { 385 return target.getLastVersion(); 386 } 387 388 @Override 389 public Document getChild(String name) { 390 return proxy.getChild(name); 391 } 392 393 @Override 394 public List<Document> getChildren() { 395 return proxy.getChildren(); 396 } 397 398 @Override 399 public List<String> getChildrenIds() { 400 return proxy.getChildrenIds(); 401 } 402 403 @Override 404 public boolean hasChild(String name) { 405 return proxy.hasChild(name); 406 } 407 408 @Override 409 public boolean hasChildren() { 410 return proxy.hasChildren(); 411 } 412 413 @Override 414 public Document addChild(String name, String typeName) { 415 return proxy.addChild(name, typeName); 416 } 417 418 @Override 419 public void orderBefore(String src, String dest) { 420 proxy.orderBefore(src, dest); 421 } 422 423 /* 424 * ----- DocumentProxy ----- 425 */ 426 427 @Override 428 public Document getTargetDocument() { 429 return target; 430 } 431 432 @Override 433 public void setTargetDocument(Document target) { 434 if (((SQLDocumentLive) proxy).isReadOnly()) { 435 throw new ReadOnlyPropertyException("Cannot write proxy: " + this); 436 } 437 if (!target.getVersionSeriesId().equals(getVersionSeriesId())) { 438 throw new ReadOnlyPropertyException("Cannot set proxy target to different version series"); 439 } 440 getSession().setProxyTarget(proxy, target); 441 this.target = target; 442 } 443 444 @Override 445 public Serializable getPropertyValue(String name) { 446 if (isPropertyForProxy(name)) { 447 return proxy.getPropertyValue(name); 448 } else { 449 return target.getPropertyValue(name); 450 } 451 } 452 453 @Override 454 public void setPropertyValue(String name, Serializable value) { 455 if (isPropertyForProxy(name)) { 456 proxy.setPropertyValue(name, value); 457 } else { 458 target.setPropertyValue(name, value); 459 } 460 } 461 462 @Override 463 public Object getValue(String xpath) throws PropertyException { 464 if (isPropertyForProxy(xpath)) { 465 return proxy.getValue(xpath); 466 } else { 467 return target.getValue(xpath); 468 } 469 } 470 471 @Override 472 public void setValue(String xpath, Object value) throws PropertyException { 473 if (isPropertyForProxy(xpath)) { 474 proxy.setValue(xpath, value); 475 } else { 476 target.setValue(xpath, value); 477 } 478 } 479 480 @Override 481 public void visitBlobs(Consumer<BlobAccessor> blobVisitor) throws PropertyException { 482 // visit all blobs from the proxy AND the target 483 proxy.visitBlobs(blobVisitor); 484 target.visitBlobs(blobVisitor); 485 } 486 487 /* 488 * ----- toString/equals/hashcode ----- 489 */ 490 491 @Override 492 public String toString() { 493 return getClass().getSimpleName() + '(' + target + ',' + proxy.getUUID() + ')'; 494 } 495 496 @Override 497 public boolean equals(Object other) { 498 if (other == this) { 499 return true; 500 } 501 if (other instanceof SQLDocumentProxy) { 502 return equals((SQLDocumentProxy) other); 503 } 504 return false; 505 } 506 507 private boolean equals(SQLDocumentProxy other) { 508 return proxy.equals(other.proxy) && target.equals(other.target); 509 } 510 511 @Override 512 public int hashCode() { 513 return proxy.hashCode() + target.hashCode(); 514 } 515 516}