/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.core.resource.xmi;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.impl.BasicEObjectImpl;
import org.eclipse.emf.ecore.impl.EObjectImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.xmi.XMLHelper;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.XMLSave;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
import org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl;
import org.teiid.core.designer.ModelerCoreException;
import org.teiid.core.designer.ModelerCoreRuntimeException;
import org.teiid.core.designer.id.IDGenerator;
import org.teiid.core.designer.id.InvalidIDException;
import org.teiid.core.designer.id.ObjectID;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.util.CoreStringUtil;
import org.teiid.designer.common.xmi.XMIHeader;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.container.Container;
import org.teiid.designer.core.container.ContainerImpl;
import org.teiid.designer.core.container.ObjectManager;
import org.teiid.designer.core.metamodel.MetamodelRegistry;
import org.teiid.designer.core.resource.EmfResource;
import org.teiid.designer.core.resource.EmfResourceSet;
import org.teiid.designer.core.resource.XResource;
import org.teiid.designer.core.resource.XmlXResourceDelegate;
import org.teiid.designer.core.transaction.UnitOfWork;
import org.teiid.designer.core.util.ModelContents;
import org.teiid.designer.core.workspace.ModelFileUtil;
import org.teiid.designer.metamodels.core.Annotation;
import org.teiid.designer.metamodels.core.AnnotationContainer;
import org.teiid.designer.metamodels.core.Identifiable;
import org.teiid.designer.metamodels.core.ModelAnnotation;
import org.teiid.designer.metamodels.core.ModelType;
/**
* This class extends the XMIResourceImpl class to provide the capability to account for UUIDs (which is explicitly ignored in
* XMIResourceImpl). In addition the load methods are overridden to provide hooks into our loading mechanisms.
*
* @author Lance Phillips
* @since 8.0
*/
public class MtkXmiResourceImpl extends XMIResourceImpl implements EmfResource, XResource {
public static final char UUID_PROTOCOL_DELIMITER = '/';
/**
* The map from {@link EObject} to {@link #getUuid UUID}. It is used to store UUIDs for objects that have been detached.
*/
public static final Map<EObject, String> DETACHED_EOBJECT_TO_UUID_MAP = XMLResourceImpl.DETACHED_EOBJECT_TO_ID_MAP;
public static final Map<String, EObject> DETACHED_UUID_TO_EOBJECT_MAP = Collections.synchronizedMap(new WeakHashMap<String, EObject>());
// The Container, MetamodelRegistry and ProxiedObjectManager instances; may be null
private Container container;
private MetamodelRegistry registry;
private ObjectManager objectManager;
private List prefixesToURIs;
private ModelContents modelContents;
private XmlXResourceDelegate delegate = new XmlXResourceDelegate();
/**
* Constructor for MtkXMIResourceImpl.
*
* @param uri
*/
public MtkXmiResourceImpl( final URI uri ) {
super(uri);
if (uri == null) {
throw new IllegalArgumentException(
ModelerCore.Util.getString("MtkXmiResourceImpl.The_URI_reference_may_not_be_null_1")); //$NON-NLS-1$
}
this.modelContents = new ModelContents(this);
this.prefixesToURIs = new ArrayList();
delegate.initialize(this);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecore.resource.impl.ResourceImpl#attachedHelper(org.eclipse.emf.ecore.EObject)
*/
@Override
protected void attachedHelper( EObject eObject ) {
if (isTrackingModification()) {
eObject.eAdapters().add(modificationTrackingAdapter);
}
delegate.attachedHelper(this, eObject);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl#detachedHelper(org.eclipse.emf.ecore.EObject)
*/
@Override
protected void detachedHelper( EObject eObject ) {
delegate.detachedHelper(this, eObject);
if (isTrackingModification()) {
eObject.eAdapters().remove(modificationTrackingAdapter);
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl#isAttachedDetachedHelperRequired()
*/
@Override
protected boolean isAttachedDetachedHelperRequired() {
return true;
}
/**
* @see org.teiid.designer.core.resource.XResource#isLoading()
* @since 5.0.3
*/
@Override
public boolean isLoading() {
return delegate.isLoading();
}
/**
* @see org.teiid.designer.core.resource.XResource#isUnloading()
* @since 5.0.3
*/
@Override
public boolean isUnloading() {
return delegate.isUnloading();
}
/**
* @see org.eclipse.emf.ecore.resource.Resource#load(Map)
*/
@Override
public void load( final Map options ) throws IOException {
if (ModelerCore.DEBUG || ModelerCore.DEBUG_METAMODEL) {
ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("MtkXmiResourceImpl.DEBUG.MtkXMIResourceImpl.load(Map)_1")); //$NON-NLS-1$
}
// make sure to replace any existing ref to the model contents helper ...
this.modelContents = new ModelContents(this);
// If the target of the load operation can be scanned, then verify that
// it represents a valid Teiid Designer model file
if (this.uri != null && this.uri.isFile()) {
File f = new File(this.uri.toFileString());
if (f.exists()) {
XMIHeader header = ModelFileUtil.getXmiHeader(f);
// If the file is an XMI 1.x version file then we cannot process it
if (header != null && header.getXmiVersion() != null && header.getXmiVersion().startsWith("1.")) { //$NON-NLS-1$
Object[] params = new Object[] {this.uri};
String msg = ModelerCore.Util.getString("MtkXmiResourceImpl.The_file,_0,_is_an_older_model_format_that_must_be_converted._1", params); //$NON-NLS-1$
ModelerCore.Util.log(IStatus.ERROR, msg);
return;
}
}
}
// Get current txn : loads should be performed in txn due to all of the setValues that occur directly
// on ENotifyingLists. Lists are not proxies and thus will not create their own declarative txn via
// interceptor stack. However, they still try to process their notifications which causes an error since
// they are not in a txn. 6/4/03 LLP
UnitOfWork txn = null;
boolean selfStarted = false;
if (container != null) {
txn = container.getEmfTransactionProvider().getCurrent();
if (!txn.isStarted()) {
try {
txn.begin();
} catch (ModelerCoreException e) {
ModelerCore.Util.log(IStatus.ERROR, e, e.getMessage());
}
selfStarted = true;
}
}
// If the XMI file currently being read has a reference to a URI
// that is in the metamodel registry then force a load of this metamodel
final URI uri = super.getURI();
final MetamodelRegistry registry = this.getMetamodelRegistry();
if (registry != null && registry.containsURI(uri)) {
final Resource r = this.registry.getResource(uri);
CoreArgCheck.isTrue(r.isLoaded(), "Resource " + r.getURI() + " must be loaded"); //$NON-NLS-1$ //$NON-NLS-2$
}
// Otherwise load the resource using it's URI
if (ModelerCore.DEBUG || ModelerCore.DEBUG_METAMODEL) {
ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("MtkXmiResourceImpl.DEBUG.Loading_model_using_URI_3", new Object[] {uri})); //$NON-NLS-1$
}
super.load(options);
// commit the txn if we started it.
if (selfStarted) {
try {
txn.commit();
} catch (ModelerCoreException e) {
ModelerCore.Util.log(IStatus.ERROR, e, e.getMessage());
}
}
if (ModelerCore.DEBUG || ModelerCore.DEBUG_METAMODEL) {
ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("MtkXmiResourceImpl.DEBUG.Returning_from_MtkXMIResourceImpl.load(Map)_1")); //$NON-NLS-1$
}
}
/**
* @see org.eclipse.emf.ecore.resource.impl#doLoad(InputStream, Map)
*/
@Override
public void doLoad( final InputStream inputStream,
final Map options ) throws IOException {
if (inputStream == null) {
throw new IllegalArgumentException(
ModelerCore.Util.getString("MtkXmiResourceImpl.The_InputStream_reference_may_not_be_null_3")); //$NON-NLS-1$
}
if (delegate.isLoading()) {
return;
}
delegate.setLoading(true);
try {
if (ModelerCore.DEBUG || ModelerCore.DEBUG_METAMODEL) {
ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("MtkXmiResourceImpl.DEBUG.MtkXMIResourceImpl.doLoad(InputStream,Map)_4")); //$NON-NLS-1$
}
// make sure to replace any existing ref to the model contents helper ...
this.modelContents = new ModelContents(this);
// Get current txn : loads should be performed in txn due to all of the setValues that occur directly
// on ENotifyingLists. Lists are not proxies and thus will not create their own declarative txn via
// interceptor stack. However, they still try to process their notifications which causes an error since
// they are not in a txn. 6/4/03 LLP
UnitOfWork txn = null;
boolean selfStarted = false;
if (container != null) {
txn = container.getEmfTransactionProvider().getCurrent();
if (!txn.isStarted()) {
try {
txn.begin();
} catch (ModelerCoreException e) {
ModelerCore.Util.log(IStatus.ERROR, e, e.getMessage());
}
selfStarted = true;
}
}
XMLHelper xmiHelper = createXMLHelper();
MtkXmiResourceLoader loader = new MtkXmiResourceLoader(xmiHelper, getContainer());
loader.load(this, inputStream, options == null ? Collections.EMPTY_MAP : options);
// Loop through contents to ensure even transient objects created by EMF during the load have a UUID.
// This is a very inefficient way of handling this problem, but no other reasonable solution is currently apparent.
for (Iterator iter = getAllContents(); iter.hasNext();) {
EObject eObject = (EObject)iter.next();
if (getUuid(eObject) == null) {
String uuid = MtkXmiResourceImpl.DETACHED_EOBJECT_TO_UUID_MAP.remove(eObject);
if (uuid == null) {
uuid = IDGenerator.getInstance().create().toString();
} else {
MtkXmiResourceImpl.DETACHED_UUID_TO_EOBJECT_MAP.remove(uuid);
}
setID(eObject, uuid);
}
}
if (xmiHelper instanceof MtkXmiHelper) {
this.prefixesToURIs = ((MtkXmiHelper)xmiHelper).getPrefixesToURIs();
}
// commit the txn if we started it.
if (selfStarted) {
try {
txn.commit();
} catch (ModelerCoreException e) {
ModelerCore.Util.log(IStatus.ERROR, e, e.getMessage());
}
}
if (ModelerCore.DEBUG || ModelerCore.DEBUG_METAMODEL) {
ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("MtkXmiResourceImpl.DEBUG.Returning_from_MtkXMIResourceImpl.doLoad(InputStream,Map)_2")); //$NON-NLS-1$
}
// testUuidToEObjectMap("--> doLoad "+this.getURI()); //$NON-NLS-1$
} finally {
delegate.setLoading(false);
}
}
/**
* @see org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl#doSave(java.io.OutputStream, java.util.Map)
*/
@Override
public void doSave( OutputStream outputStream,
Map options ) throws IOException {
// Check that the model annotation still exists in the resource and is at element 0...
ModelAnnotation modelAnnotation = null;
if (this.modelContents != null && this.modelContents.getModelAnnotation() != null) {
modelAnnotation = this.modelContents.getModelAnnotation();
final Resource resource = modelAnnotation.eResource();
if (resource == null) {
// It was somehow yanked, but we always want to keep the ModelAnnotation
this.getContents().add(0, modelAnnotation);
}
}
if (modelAnnotation != null && !(this.getContents().get(0) instanceof ModelAnnotation)) {
this.getContents().move(0, modelAnnotation);
}
// Set the product name and version information on the ModelAnnotation
if (modelAnnotation != null) {
modelAnnotation.setProducerName(ModelerCore.ILicense.PRODUCER_NAME);
modelAnnotation.setProducerVersion(ModelerCore.ILicense.VERSION);
}
// Set the XML save option for dangling hrefs to RECORD. The default option for this
// is THROW which will save the rest of the model but throws an exception at the end.
// The option of RECORD will save the model, add the expection of the resource's
// error list, but will not throw an exception.
final Map saveOptions = options == null ? new HashMap() : new HashMap(options);
saveOptions.put(XMLResource.OPTION_PROCESS_DANGLING_HREF, XMLResource.OPTION_PROCESS_DANGLING_HREF_RECORD);
super.doSave(outputStream, saveOptions);
// testUuidToEObjectMap("--> doSave "+this.getURI()); //$NON-NLS-1$
}
/**
* @see org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl#createXMLHelper()
*/
@Override
protected XMLHelper createXMLHelper() {
return new MtkXmiHelper();
}
/**
* @see org.eclipse.emf.ecore.resource.impl.ResourceImpl#doUnload()
*/
@Override
protected void doUnload() {
if (delegate.isUnloading()) {
return;
}
delegate.setUnloading(true);
try {
super.doUnload();
setModified(false);
this.modelContents = null;
// testUuidToEObjectMap("--> doUnload "+this.getURI()); //$NON-NLS-1$
} finally {
delegate.setUnloading(false);
}
}
/**
* Records the UUID of the EObject instance. The implementation updates the {@link #uuidToEObjectMap}. If recursive is true,
* the method will traverse the contents of this EObject recording their UUIDs in the map
*
* @param eObject the object.
* @param recursive whether to recursively record the UUIDs of all contents of this EObject by calling
* EObject..eAllContents().
*/
void ensureUuid( final EObject eObject,
final boolean recursive ) {
if (eObject == null) {
throw new IllegalArgumentException(
ModelerCore.Util.getString("MtkXmiResourceImpl.The_EObject_reference_may_not_be_null")); //$NON-NLS-1$
}
String uuid = getID(eObject);
if (uuid == null) {
setID(eObject, IDGenerator.getInstance().create().toString());
if (recursive) {
for (final Iterator iter = eObject.eContents().iterator(); iter.hasNext();) {
final Object next = iter.next();
if (next instanceof EObject) {
final EObject nextEObj = (EObject)next;
ensureUuid(nextEObj, recursive);
}
}
}
}
}
/**
* @see org.eclipse.emf.ecore.resource.Resource.Internal#attached(org.eclipse.emf.ecore.EObject)
* @since 4.2
*/
@Override
public void attached( EObject eObject ) {
ensureUuid(eObject, true);
super.attached(eObject);
}
/**
* @see org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl#createXMLSave()
*/
@Override
protected XMLSave createXMLSave() {
return new MtkXmiSaveImpl(super.createXMLHelper(), this);
}
/**
* @see org.eclipse.emf.ecore.resource.Resource#getContents()
*/
@Override
public EList getContents() {
if (contents == null) {
contents = new MtkContentsEList(this);
}
return contents;
}
/**
* @see org.teiid.designer.core.resource.EmfResource#getDescription()
* @since 4.3
*/
@Override
public String getDescription() {
String description = null;
if (isLoaded() && getModelContents().getModelAnnotation() != null) {
description = getModelContents().getModelAnnotation().getDescription();
} else {
XMIHeader header = doGetXmiHeader();
if (header != null) {
description = header.getDescription();
}
}
return description;
}
/**
* @see org.teiid.designer.core.resource.EmfResource#getModelType()
* @since 4.3
*/
@Override
public ModelType getModelType() {
ModelType type = null;
if (isLoaded() && getModelContents().getModelAnnotation() != null) {
type = getModelContents().getModelAnnotation().getModelType();
} else {
XMIHeader header = doGetXmiHeader();
if (header != null) {
type = ModelType.get(header.getModelType());
}
}
return type;
}
/**
* @see org.teiid.designer.core.resource.EmfResource#getPrimaryMetamodelUri()
* @since 4.3
*/
@Override
public URI getPrimaryMetamodelUri() {
String primaryMetamodelUri = null;
if (isLoaded() && getModelContents().getModelAnnotation() != null) {
primaryMetamodelUri = getModelContents().getModelAnnotation().getPrimaryMetamodelUri();
} else {
XMIHeader header = doGetXmiHeader();
if (header != null) {
primaryMetamodelUri = header.getPrimaryMetamodelURI();
}
}
if (CoreStringUtil.isEmpty(primaryMetamodelUri)) {
return null;
}
return URI.createURI(primaryMetamodelUri);
}
/**
* @see org.teiid.designer.core.resource.EmfResource#isVisible()
* @since 5.0
*/
@Override
public boolean isVisible() {
boolean isVisible = true;
if (isLoaded() && getModelContents().getModelAnnotation() != null) {
isVisible = getModelContents().getModelAnnotation().isVisible();
} else {
XMIHeader header = doGetXmiHeader();
if (header != null) {
isVisible = header.isVisible();
}
}
return isVisible;
}
/**
* @see org.teiid.designer.core.resource.EmfResource#getUuid()
* @since 4.3
*/
@Override
public ObjectID getUuid() {
ObjectID uuid = null;
if (isLoaded() && getModelContents().getModelAnnotation() != null) {
uuid = getObjectIDFromString(getID(getModelAnnotation()));
} else {
XMIHeader header = doGetXmiHeader();
if (header != null && header.getUUID() != null) {
uuid = getObjectIDFromString(header.getUUID());
}
}
return uuid;
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.core.resource.XResource#getUuid(org.eclipse.emf.ecore.EObject)
*/
@Override
public String getUuid( EObject object ) {
return getID(object);
}
protected XMIHeader doGetXmiHeader() {
XMIHeader header = null;
if (getURI().isFile()) {
File f = new File(getURI().toFileString());
if (f.exists()) {
header = ModelFileUtil.getXmiHeader(f);
}
}
return header;
}
protected class MtkContentsEList extends XMIResourceImpl.ContentsEList {
private static final long serialVersionUID = 1L;
private final MtkXmiResourceImpl owner;
public MtkContentsEList( final MtkXmiResourceImpl owner ) {
this.owner = owner;
}
@Override
protected void didChange() {
MtkXmiResourceImpl.this.setModified(true);
}
/**
* @see java.util.Collection#addAll(java.util.Collection)
*/
@Override
public boolean removeAll( final Collection c ) {
if (c == null || c.isEmpty()) {
return false;
}
final boolean startTxn = getTxn();
try {
final EList vals = new BasicEList(c);
final int[] removedIndexes = getIndexes(vals);
owner.removeMany(c);
if (vals.size() == 1) {
eNotify(createNotification(Notification.REMOVE, vals.get(0), null, removedIndexes[0]));
} else {
eNotify(createNotification(Notification.REMOVE_MANY, vals, removedIndexes, removedIndexes[0]));
}
return true;
} finally {
if (startTxn) {
commitTxn();
}
}
}
private int[] getIndexes( final Collection vals ) {
final BasicEList tmp = new BasicEList(this);
final int[] result = new int[vals.size()];
final Iterator it = tmp.iterator();
int count = 0;
int index = 0;
while (it.hasNext()) {
Object next = it.next();
if (vals.contains(next)) {
result[count++] = index;
}
index++;
}
return result;
}
/**
* @see java.util.Collection#addAll(java.util.Collection)
*/
@Override
public boolean addAll( final Collection c ) {
if (c == null || c.isEmpty()) {
return false;
}
final boolean startTxn = getTxn();
try {
// super.addAll(c);
final int index = size;
owner.addMany(c);
final EList vals = new BasicEList(c);
eNotify(createNotification(Notification.ADD_MANY, null, vals, index, true));
return true;
} finally {
if (startTxn) {
commitTxn();
}
}
}
/**
* @see java.util.List#add(int, java.lang.Object)
*/
@Override
public void add( final int index,
final Object element ) {
final boolean startTxn = getTxn();
try {
super.add(index, element);
attachToResource(element);
} finally {
if (startTxn) {
commitTxn();
}
}
}
/**
* @see java.util.Collection#add(java.lang.Object)
*/
@Override
public boolean add( final Object o ) {
final boolean startTxn = getTxn();
try {
final boolean result = super.add(o);
attachToResource(o);
return result;
} finally {
if (startTxn) {
commitTxn();
}
}
}
/**
* @see java.util.List#addAll(int, java.util.Collection)
*/
@Override
public boolean addAll( final int index,
final Collection c ) {
final boolean startTxn = getTxn();
try {
final boolean result = super.addAll(index, c);
attachToResource(c);
return result;
} finally {
if (startTxn) {
commitTxn();
}
}
}
/**
* @see java.util.Collection#remove(java.lang.Object)
*/
@Override
public boolean remove( final Object o ) {
final boolean startTxn = getTxn();
try {
return super.remove(o);
} finally {
if (startTxn) {
commitTxn();
}
}
}
/**
* @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#inverseAdd(java.lang.Object,
* org.eclipse.emf.common.notify.NotificationChain)
*/
@Override
public NotificationChain inverseAdd( final Object object,
final NotificationChain notifications ) {
if (object == null) {
return notifications;
}
final boolean startTxn = getTxn();
try {
final NotificationChain result = super.inverseAdd(object, notifications);
attachToResource(object);
return result;
} finally {
if (startTxn) {
commitTxn();
}
}
}
/**
* @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#inverseRemove(java.lang.Object,
* org.eclipse.emf.common.notify.NotificationChain)
*/
@Override
public NotificationChain inverseRemove( final Object object,
final NotificationChain notifications ) {
if (object == null) {
return notifications;
}
final boolean startTxn = getTxn();
try {
return super.inverseRemove(object, notifications);
} finally {
if (startTxn) {
commitTxn();
}
}
}
/**
* @see org.eclipse.emf.common.util.BasicEList#addAllUnique(java.util.Collection)
*/
@Override
public boolean addAllUnique( final Collection collection ) {
final boolean startTxn = getTxn();
try {
final boolean result = super.addAllUnique(collection);
attachToResource(collection);
return result;
} finally {
if (startTxn) {
commitTxn();
}
}
}
/**
* @see org.eclipse.emf.common.util.BasicEList#addAllUnique(int, java.util.Collection)
*/
@Override
public boolean addAllUnique( final int index,
final Collection collection ) {
final boolean startTxn = getTxn();
try {
final boolean result = super.addAllUnique(index, collection);
attachToResource(collection);
return result;
} finally {
if (startTxn) {
commitTxn();
}
}
}
/**
* @see org.eclipse.emf.common.util.BasicEList#addUnique(int, java.lang.Object)
*/
@Override
public void addUnique( final int index,
final Object object ) {
final boolean startTxn = getTxn();
try {
super.addUnique(index, object);
attachToResource(object);
} finally {
if (startTxn) {
commitTxn();
}
}
}
/**
* @see org.eclipse.emf.common.util.BasicEList#addUnique(java.lang.Object)
*/
@Override
public void addUnique( final Object object ) {
final boolean startTxn = getTxn();
try {
super.addUnique(object);
attachToResource(object);
} finally {
if (startTxn) {
commitTxn();
}
}
}
/**
* @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#basicAdd(java.lang.Object,
* org.eclipse.emf.common.notify.NotificationChain)
*/
@Override
public NotificationChain basicAdd( final Object object,
final NotificationChain notifications ) {
final boolean startTxn = getTxn();
try {
final NotificationChain result = super.basicAdd(object, notifications);
attachToResource(object);
return result;
} finally {
if (startTxn) {
commitTxn();
}
}
}
/**
* @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#basicRemove(java.lang.Object,
* org.eclipse.emf.common.notify.NotificationChain)
*/
@Override
public NotificationChain basicRemove( final Object object,
final NotificationChain notifications ) {
final boolean startTxn = getTxn();
try {
return super.basicRemove(object, notifications);
} finally {
if (startTxn) {
commitTxn();
}
}
}
void attachToResource( final Object object ) {
if (object instanceof Identifiable) {
// Identifiable instances represent SimpleDatatype instances
// found in the deprecated SDT metamodel
String uuidString = ((Identifiable)object).getUuid();
if (uuidString != null && object instanceof EObject) {
EObject eObject = (EObject)object;
ModelerCore.setObjectId(eObject, uuidString);
ensureUuid(eObject, true);
}
} else if (object instanceof EObject) {
final EObject eObj = (EObject)object;
ensureUuid(eObj, true);
} else {
if (ModelerCore.DEBUG || ModelerCore.DEBUG_METAMODEL) {
ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("MtkXmiResourceImpl.DEBUG.Attaching_non-proxy_object_5", new Object[] {object})); //$NON-NLS-1$
}
}
}
private boolean getTxn() {
return ModelerCore.startTxn(null, this);
}
private void commitTxn() {
ModelerCore.commitTxn();
}
}
/**
* @see org.teiid.designer.core.resource.EmfResource#getModelAnnotation()
*/
@Override
public ModelAnnotation getModelAnnotation() {
if (this.modelContents != null) {
return this.modelContents.getModelAnnotation();
}
return null;
}
/**
* @see org.teiid.designer.core.resource.EmfResource#getModelContents()
*/
@Override
public ModelContents getModelContents() {
if (this.modelContents == null) {
this.modelContents = new ModelContents(this);
}
return this.modelContents;
}
@Override
public List getNamespacePrefixToUris() {
return this.prefixesToURIs;
}
/**
* @see org.eclipse.emf.ecore.resource.Resource#getURIFragment(org.eclipse.emf.ecore.EObject)
*/
@Override
public String getURIFragment( final EObject eObject ) {
String uuid = getID(eObject);
return (uuid == null ? null : uuid.replace(ObjectID.DELIMITER, UUID_PROTOCOL_DELIMITER));
}
/**
* @see org.eclipse.emf.ecore.resource.impl.ResourceImpl#getEObjectForURIFragmentRootSegment(java.lang.String)
*/
@Override
protected EObject getEObjectForURIFragmentRootSegment( final String uriFragmentRootSegment ) {
// See if the string is an ObjectID ...
final ObjectID id = getObjectIDFromString(uriFragmentRootSegment);
if (id != null) {
final EObject result = this.getEObject(id);
if (result != null) {
return result;
}
}
return super.getEObjectForURIFragmentRootSegment(uriFragmentRootSegment);
}
/**
* @see org.eclipse.emf.ecore.resource.Resource#getEObject(java.lang.String)
*/
@Override
public EObject getEObject( final String uriFragment ) {
if (uriFragment != null) {
// See if the string is an ObjectID ...
final ObjectID id = getObjectIDFromString(uriFragment);
if (id != null) {
// Lookup the UUID in the resource
return this.getEObject(id);
}
return super.getEObject(uriFragment);
}
return null;
}
/**
* Override the XMLResourceImpl.getEObjectByID implementation to fix defect 12085 and prevent any
* ConcurrentModificationException that may occur when using the getAllContents() TreeIterator.
*
* @see org.eclipse.emf.ecore.resource.impl.ResourceImpl#getEObjectByID(java.lang.String)
* @since 4.2
*/
@Override
protected EObject getEObjectByID( String id ) {
return idToEObjectMap.get(id);
}
/**
* @see EmfResource#getContainer()
*/
@Override
public Container getContainer() {
if (this.container == null) {
final ResourceSet resourceSet = super.resourceSet;
if (resourceSet == null) {
final String msg = ModelerCore.Util.getString("MtkXmiResourceImpl.The_ResourceSet_reference_may_not_be_null_4"); //$NON-NLS-1$
throw new AssertionError(msg);
}
if (resourceSet instanceof EmfResourceSet) {
this.container = ((EmfResourceSet)resourceSet).getContainer();
if (this.container == null) {
final String msg = ModelerCore.Util.getString("MtkXmiResourceImpl.MtkXmiResourceImpl.The_Container_reference_may_not_be_null_5"); //$NON-NLS-1$
throw new AssertionError(msg);
}
}
if (this.container == null) {
if (ModelerCore.DEBUG || ModelerCore.DEBUG_METAMODEL) {
ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("MtkXmiResourceImpl.DEBUG.The_Container_reference_is_null_for_this_MtkXmiResourceImpl_6")); //$NON-NLS-1$
}
}
}
return this.container;
}
public ObjectManager getObjectManager() {
if (this.objectManager == null) {
final Container ctnr = this.getContainer(); // may load/create/initialize it
if (ctnr instanceof ContainerImpl) {
this.objectManager = ((ContainerImpl)ctnr).getObjectManager();
} else {
this.objectManager = doGetDefaultObjectManager();
}
}
return this.objectManager;
}
protected ObjectManager doGetDefaultObjectManager() {
return new ObjectManager() {
@Override
public String getObjectId( EObject object ) {
return null;
}
@Override
public void setObjectId( EObject object,
String uuid ) {
}
@Override
public EObject findEObject( String id ) {
return null;
}
@Override
public EObject findEObject( String id,
Resource resource ) {
return null;
}
};
}
/**
* @see EmfResource#getEObject(Object)
*/
@Override
public EObject getEObject( final Object object ) {
if (ModelerCore.DEBUG || ModelerCore.DEBUG_METAMODEL) {
ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("MtkXmiResourceImpl.DEBUG.MtkXmiResourceImpl.getEObject()_7", new Object[] {object})); //$NON-NLS-1$
}
if (object instanceof URI) {
return getResourceSet().getEObject(uri, true);
} else if (object instanceof ObjectID) {
return getEObjectByID(((ObjectID)object).toString());
}
throw new ModelerCoreRuntimeException(
ModelerCore.Util.getString("MtkXmiResourceImpl.MtkXmiResourceImpl.getEObject()_invalid_param.__Key_must_be_a_Proxy,_a_URI,_or_an_ObjectID_6", new Object[] {object})); //$NON-NLS-1$
}
/**
* This is strictly a performance method. It should only be used when adding large numbers of new root Objects as the
* EContentsList is modified directly in this method to avoid the overhead of all the EObject callbacks.
*
* @param newRoots to add to this Resource.
*/
public void addMany( final Collection newRoots ) {
final Collection newRootsToAdd = new ArrayList(newRoots);
final Collection allRoots = new ArrayList(this.getContents());
allRoots.addAll(newRootsToAdd);
final Object[] rootArray = allRoots.toArray();
// Reset data object for Contents List.
// NOTE : This is not considered a safe opperation as no callbacks are executed for
// the added objects. See java doc for BasicEList.setData for additional details.
((BasicEList)this.getContents()).setData(rootArray.length, rootArray);
// Attach all these objects to this resource
for (Iterator iter = newRootsToAdd.iterator(); iter.hasNext();) {
Object object = iter.next();
((MtkContentsEList)this.contents).attachToResource(object);
if (object instanceof BasicEObjectImpl) {
((BasicEObjectImpl)object).eSetResource(this, null);
}
}
// Performing the attach to resource and eSetResource is sufficient to ensure
// that the Eobjects get added to the ObjectManager
// //Add all the objects to the object manager
// getObjectManager().processMassAdd(newRoots,this);
}
/**
* This is strictly a performance method. It should only be used when removing large numbers of root Objects as the
* EContentsList is modified directly in this method to avoid the overhead of all the EObject callbacks.
*
* @param roots to remove from this Resource.
*/
public void removeMany( final Collection roots ) {
final Collection allRoots = new ArrayList(this.getContents());
allRoots.removeAll(roots);
final Object[] rootArray = allRoots.toArray();
// Reset data object for Contents List.
// NOTE : This is not considered a safe opperation as no callbacks are executed for
// the added objects. See java doc for BasicEList.setData for additional details.
((BasicEList)this.getContents()).setData(rootArray.length, rootArray);
// Detach from Resource and eSetResource are sufficent to ensure that
// the EObject is removed from the ObjectManager.
// //Remove all the objects from the object manager
// getObjectManager().processMassRemove(roots);
//
// Remove all these objects to this resource
for (Iterator iter = roots.iterator(); iter.hasNext();) {
Object object = iter.next();
if (object instanceof EObjectImpl) {
((EObjectImpl)object).eSetResource(null, null);
}
}
}
/**
* Returns the {@link MetamodelRegistry} instance associated with this resource.
*
* @return MetamodelRegistry; may be null
*/
public MetamodelRegistry getMetamodelRegistry() {
if (this.registry == null) {
Container emfContainer = this.getContainer();
if (container != null) {
this.registry = emfContainer.getMetamodelRegistry();
if (this.registry == null) {
final String msg = ModelerCore.Util.getString("MtkXmiResourceImpl.The_MetamodelRegistry_reference_may_not_be_null_7"); //$NON-NLS-1$
throw new AssertionError(msg);
}
}
if (this.registry == null) {
if (ModelerCore.DEBUG || ModelerCore.DEBUG_METAMODEL) {
ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("MtkXmiResourceImpl.DEBUG.The_MetamodelRegistry_reference_is_null_for_this_MtkXmiResourceImpl_8")); //$NON-NLS-1$
}
}
}
return this.registry;
}
/**
* @see org.teiid.designer.core.resource.MMXmiResource#getAnnotation(org.eclipse.emf.ecore.EObject)
*/
@Override
public Annotation getAnnotation( EObject eobj ) {
return getModelContents().getAnnotation(eobj);
}
/**
* @see org.teiid.designer.core.resource.MMXmiResource#getAnnotationContainer(boolean)
*/
@Override
public AnnotationContainer getAnnotationContainer( boolean createIfNeeded ) {
return getModelContents().getAnnotationContainer(createIfNeeded);
}
protected EObject resolveEObject( final ObjectID id ) {
if (id == null) {
throw new IllegalArgumentException(
ModelerCore.Util.getString("MtkXmiResourceImpl.The_ObjectID_reference_may_not_be_null")); //$NON-NLS-1$
}
return getEObjectByID(id.toString());
}
private ObjectID getObjectIDFromString( final String uuidString ) {
if (uuidString == null || uuidString.length() == 0) {
return null;
}
try {
return IDGenerator.getInstance().stringToObject(uuidString);
} catch (InvalidIDException e) {
// do nothing ...
}
return null;
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.core.resource.XResource#setUuid(org.eclipse.emf.ecore.EObject, java.lang.String)
*/
@Override
public void setUuid( EObject object,
String uuid ) {
super.setID(object, uuid);
}
/**
* Test method to validate the internal uuidToEObjectMap. The method validates if the map contains an entry for every EObject
* instance currently in the modeler
*
* @param description
* @since 4.2
*/
public void testUuidToEObjectMap( final String description ) {
int missingMapEntryCount = 0;
int eObjectCount = 0;
for (final Iterator iter = this.getContents().iterator(); iter.hasNext();) {
final EObject eObject = (EObject)iter.next();
if (eObject != null) {
eObjectCount++;
ObjectID id = ModelerCore.getObjectId(eObject);
if (id == null || getEObjectByID(id.toString()) == null) {
missingMapEntryCount++;
}
for (final Iterator iter2 = eObject.eAllContents(); iter2.hasNext();) {
final EObject eObj = (EObject)iter2.next();
if (eObj != null) {
eObjectCount++;
id = ModelerCore.getObjectId(eObj);
if (id == null || getEObjectByID(id.toString()) == null) {
missingMapEntryCount++;
}
}
}
}
}
ModelerCore.Util.log(IStatus.INFO, description);
ModelerCore.Util.log(IStatus.INFO, "Number of EObject instances in model = " + eObjectCount); //$NON-NLS-1$
ModelerCore.Util.log(IStatus.INFO, "Number of entries in the map = " + this.idToEObjectMap.size()); //$NON-NLS-1$
final int severity = (missingMapEntryCount == 0 ? IStatus.INFO : IStatus.ERROR);
ModelerCore.Util.log(severity, "Number of EObject instances missing in the map = " + missingMapEntryCount); //$NON-NLS-1$
}
}