/* * Copyright (c) 2010-2012, 2016 Eike Stepper (Berlin, Germany) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Victor Roldan Betancort - initial API and implementation */ package org.eclipse.emf.cdo.server.internal.db4o; import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDExternal; import org.eclipse.emf.cdo.common.id.CDOIDUtil; import org.eclipse.emf.cdo.common.model.CDOClassInfo; import org.eclipse.emf.cdo.common.revision.CDOList; import org.eclipse.emf.cdo.common.revision.CDOListFactory; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionData; import org.eclipse.emf.cdo.common.revision.CDORevisionFactory; import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; import org.eclipse.emf.cdo.server.IRepository; import org.eclipse.emf.cdo.server.IStore; import org.eclipse.emf.cdo.spi.common.revision.CDOFeatureMapEntry; import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.util.FeatureMap; import java.util.ArrayList; import java.util.List; /** * @author Victor Roldan Betancort */ public class DB4ORevision { public static final String ATTRIBUTE_CLASS_NAME = "className"; public static final String ATTRIBUTE_PACKAGE_NS_URI = "packageNsURI"; private String packageNsURI; private String className; private long id; private int version; private long timeStamp; private long resourceID; /** * Can be an external ID! */ private Object containerID; private int containingFeatureID; private List<Object> values; // TODO enum RevisionType { NORMAL, ROOT_RESOURCE, RESOURCE, RESOURCE_FOLDER }?? private boolean isResource; private boolean isResourceFolder; public DB4ORevision(String packageURI, String className, long id, int version, long resourceID, Object containerID, int containingFeatureID, List<Object> values, long timestamp, boolean isResource, boolean isResourceFolder) { setPackageURI(packageURI); setClassName(className); setID(id); setVersion(version); setResourceID(resourceID); setContainerID(containerID); setContainingFeatureID(containingFeatureID); setValues(values); setTimeStamp(timestamp); setResource(isResource); setResourceFolder(isResourceFolder); } public void setPackageURI(String packageURI) { packageNsURI = packageURI; } public String getPackageURI() { return packageNsURI; } public void setClassName(String className) { this.className = className; } public String getClassName() { return className; } public void setID(long id) { this.id = id; } public long getID() { return id; } public int getBranchID() { return CDOBranch.MAIN_BRANCH_ID; } public void setVersion(int version) { this.version = version; } public int getVersion() { return version; } public long getRevised() { return CDORevision.UNSPECIFIED_DATE; } public void setResourceID(long resourceID) { this.resourceID = resourceID; } public long getResourceID() { return resourceID; } public void setContainerID(Object containerID) { this.containerID = containerID; } public Object getContainerID() { return containerID; } public void setContainingFeatureID(int containingFeatureID) { this.containingFeatureID = containingFeatureID; } public int getContainingFeatureID() { return containingFeatureID; } public void setValues(List<Object> values) { this.values = values; } public List<Object> getValues() { return values; } public void setTimeStamp(long timeStamp) { this.timeStamp = timeStamp; } public long getTimeStamp() { return timeStamp; } public void setResource(boolean isResource) { this.isResource = isResource; } public boolean isResource() { return isResource; } public void setResourceFolder(boolean isResourceFolder) { this.isResourceFolder = isResourceFolder; } public boolean isResourceFolder() { return isResourceFolder; } public boolean isResourceNode() { return isResource || isResourceFolder; } public static DB4ORevision getDB4ORevision(InternalCDORevision revision) { CDOClassInfo classInfo = revision.getClassInfo(); EClass eClass = classInfo.getEClass(); String packageURI = eClass.getEPackage().getNsURI(); String className = eClass.getName(); CDOID revisionID = revision.getID(); if (revisionID.isTemporary()) { throw new IllegalArgumentException("TEMPORARY CDOID: " + revisionID); } boolean isResource = revision.isResource(); boolean isResourceFolder = revision.isResourceFolder(); long id = CDOIDUtil.getLong(revisionID); int version = revision.getVersion(); long timeStamp = revision.getTimeStamp(); long resourceID = CDOIDUtil.getLong(revision.getResourceID()); Object containerID = getDB4OID((CDOID)revision.getContainerID()); int containingFeatureID = revision.getContainingFeatureID(); EStructuralFeature[] features = classInfo.getAllPersistentFeatures(); List<Object> values = new ArrayList<Object>(features.length); if (features.length > 0) { for (int i = 0; i < features.length; i++) { EStructuralFeature feature = features[i]; Object obj = revision.getValue(feature); // We will process CDOList for EReferences to get rid of CDOIDs (we want to get only primitive types, // otherwise the database will persist unwanted objects coming from Object-level relationships // Multi-valued EAttributes (also kept in CDOList) will be saved as is if (obj instanceof InternalCDOList && feature instanceof EReference) { InternalCDOList cdoList = (InternalCDOList)obj; List<Object> list = new ArrayList<Object>(); for (Object listElement : cdoList) { if (!(listElement instanceof CDOID)) { throw new IllegalStateException("CDOList should contain only CDOID instances but received " + listElement.getClass().getName() + " instead"); } list.add(getDB4OID((CDOID)listElement)); } values.add(i, list); } else if (listContainsInstancesOfClass(obj, CDOFeatureMapEntry.class)) // FeatureMap { values.add(i, DB4OFeatureMapEntry.getPrimitiveFeatureMapEntryList(obj)); } else { // Prevent the explicit null-ref "NIL" from being serialized! if (obj == CDORevisionData.NIL) { obj = new ExplicitNull(); } values.add(i, obj); } } } return new DB4ORevision(packageURI, className, id, version, resourceID, containerID, containingFeatureID, values, timeStamp, isResource, isResourceFolder); } public static InternalCDORevision getCDORevision(IStore store, DB4ORevision primitiveRevision) { IRepository repository = store.getRepository(); CDORevisionFactory factory = ((InternalCDORevisionManager)repository.getRevisionManager()).getFactory(); CDOBranch branch = repository.getBranchManager().getMainBranch(); String nsURI = primitiveRevision.getPackageURI(); String className = primitiveRevision.getClassName(); EPackage ePackage = repository.getPackageRegistry().getEPackage(nsURI); EClass eClass = (EClass)ePackage.getEClassifier(className); InternalCDORevision revision = (InternalCDORevision)factory.createRevision(eClass); revision.setID(getCDOID(primitiveRevision.getID())); revision.setVersion(primitiveRevision.getVersion()); revision.setBranchPoint(branch.getPoint(primitiveRevision.getTimeStamp())); revision.setRevised(primitiveRevision.getRevised()); revision.setResourceID(getCDOID(primitiveRevision.getResourceID())); revision.setContainerID(getCDOID(primitiveRevision.getContainerID())); revision.setContainingFeatureID(primitiveRevision.getContainingFeatureID()); EStructuralFeature[] features = revision.getClassInfo().getAllPersistentFeatures(); int i = 0; for (Object value : primitiveRevision.getValues()) { EStructuralFeature feature = features[i++]; if (feature instanceof EReference && value instanceof List<?>) { List<?> sourceList = (List<?>)value; CDOList list = CDOListFactory.DEFAULT.createList(sourceList.size(), sourceList.size(), CDORevision.UNCHUNKED); for (int j = 0; j < sourceList.size(); j++) { list.set(j, getCDOID(sourceList.get(j))); } value = list; } else if (listContainsInstancesOfClass(value, DB4OFeatureMapEntry.class)) { value = DB4OFeatureMapEntry.getCDOFeatureMapEntryList(eClass, value); } // Convert 'null' into the explicit null-ref "NIL" if appropriate if (value instanceof ExplicitNull) { value = CDORevisionData.NIL; } revision.setValue(feature, value); } return revision; } public static Object getDB4OID(CDOID id) { if (id.isExternal()) { return new String(((CDOIDExternal)id).getURI()); } return CDOIDUtil.getLong(id); } public static CDOID getCDOID(Object id) { if (id == null) { return CDOID.NULL; } if (id instanceof String) { return CDOIDUtil.createExternal((String)id); } if (id instanceof CDOID) { return (CDOID)id; } return CDOIDUtil.createLong((Long)id); } public static boolean listContainsInstancesOfClass(Object obj, Class<?> clazz) { if (obj instanceof List) { List<?> list = (List<?>)obj; for (Object potentialFeatureMap : list) { if (!clazz.isAssignableFrom(potentialFeatureMap.getClass())) { return false; } } return true; } return false; } private static final class DB4OFeatureMapEntry { private int featureID; private Object valueID; public DB4OFeatureMapEntry(int featureID, Object valueID) { setFeatureID(featureID); setValueID(valueID); } private void setFeatureID(int featureID) { this.featureID = featureID; } public int getFeatureID() { return featureID; } private void setValueID(Object valueID) { this.valueID = valueID; } public Object getValueID() { return valueID; } public static List<DB4OFeatureMapEntry> getPrimitiveFeatureMapEntryList(Object obj) { InternalCDOList cdoList = (InternalCDOList)obj; List<DB4OFeatureMapEntry> list = new ArrayList<DB4OFeatureMapEntry>(); for (Object listElement : cdoList) { if (listElement instanceof FeatureMap.Entry) { FeatureMap.Entry entry = (FeatureMap.Entry)listElement; EStructuralFeature entryFeature = entry.getEStructuralFeature(); CDOID entryValue = (CDOID)entry.getValue(); DB4OFeatureMapEntry db4oEntry = new DB4OFeatureMapEntry(entryFeature.getFeatureID(), getDB4OID(entryValue)); list.add(db4oEntry); } } return list; } public static CDOList getCDOFeatureMapEntryList(EClass eClass, Object value) { List<?> sourceList = (List<?>)value; CDOList list = CDOListFactory.DEFAULT.createList(sourceList.size(), sourceList.size(), CDORevision.UNCHUNKED); for (int j = 0; j < sourceList.size(); j++) { DB4OFeatureMapEntry mapEntry = (DB4OFeatureMapEntry)sourceList.get(j); EStructuralFeature entryFeature = eClass.getEStructuralFeature(mapEntry.getFeatureID()); CDOID valueID = getCDOID(mapEntry.getValueID()); list.set(j, CDORevisionUtil.createFeatureMapEntry(entryFeature, valueID)); } return list; } } /** * @author Caspar De Groot */ private static final class ExplicitNull { } }