/*
* 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;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.edit.provider.ChangeNotifier;
import org.eclipse.emf.edit.provider.IChangeNotifier;
import org.eclipse.emf.edit.provider.INotifyChangedListener;
import org.teiid.core.designer.TeiidDesignerException;
import org.teiid.core.designer.TeiidDesignerRuntimeException;
import org.teiid.core.designer.id.IDGenerator;
import org.teiid.core.designer.id.InvalidIDException;
import org.teiid.core.designer.id.ObjectID;
import org.teiid.designer.common.xmi.XMIHeader;
import org.teiid.designer.common.xmi.XMIHeaderReader;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.container.DuplicateResourceException;
/**
* @since 8.0
*/
public class EResourceSetImpl extends ResourceSetImpl implements EResourceSet {
private static final char SEGMENT_SEPARATOR = '/';
// private static final boolean DEBUG = false;
// private EObjectFinder eObjectFinder;
// private EResourceFinder eResourceFinder;
// private Map physicalToLogicalUri;
private EObjectHrefConverter eObjectHrefConverter;
private final List externalResourceSets;
private final IChangeNotifier changeNotifier;
// ==================================================================================
// C O N S T R U C T O R S
// ==================================================================================
/**
* Constructor for EResourceSetImpl.
*/
public EResourceSetImpl() {
super();
this.externalResourceSets = new ArrayList(7);
// this.physicalToLogicalUri = new HashMap();
this.changeNotifier = new ChangeNotifier();
// Add an EContentAdapter to all resources in this resource set
this.eAdapters().add(new EContentAdapter() {
@Override
public void notifyChanged( Notification notification ) {
super.notifyChanged(notification);
EResourceSetImpl.this.getChangeNotifier().fireNotifyChanged(notification);
}
});
}
// ==================================================================================
// O V E R R I D D E N M E T H O D S
// ==================================================================================
/**
* @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#getLoadOptions()
* @since 4.3
*/
@Override
public Map getLoadOptions() {
final Map options = new HashMap(super.getLoadOptions());
// Disable notifications upon load
if (options.get(XMLResource.OPTION_DISABLE_NOTIFY) == null) {
options.put(XMLResource.OPTION_DISABLE_NOTIFY, Boolean.TRUE);
}
return options;
}
/**
* @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#createResource(org.eclipse.emf.common.util.URI)
* @since 4.2
*/
@Override
public Resource createResource( final URI uri ) {
canCreateResource(uri);
return super.createResource(uri);
}
/**
* @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#delegatedGetResource(org.eclipse.emf.common.util.URI, boolean)
* @since 4.3
*/
@Override
protected Resource delegatedGetResource( final URI uri,
final boolean loadOnDemand ) {
// Check the EPackage registry for this URI
Resource eResource = super.delegatedGetResource(uri, loadOnDemand);
// Check the external resources sets for this URI
if (eResource == null) {
ResourceSet[] eResourceSets = getExternalResourceSets();
for (int i = 0; i < eResourceSets.length; i++) {
eResource = eResourceSets[i].getResource(uri, false);
if (eResource != null) {
break;
}
}
}
return eResource;
}
/**
* @see org.eclipse.emf.ecore.resource.ResourceSet#getResource(org.eclipse.emf.common.util.URI, boolean)
*/
@Override
public Resource getResource( final URI uri,
final boolean loadOnDemand ) {
Resource eResource = super.getResource(uri, loadOnDemand);
// If the resource URI starts with a '/' then it may be of the form
// "/Project/.../Resource". Try to match the path against existing
// file URIs in the resource set.
if (eResource == null && uri.toString().charAt(0) == SEGMENT_SEPARATOR) {
String relativePath = uri.toFileString();
for (Iterator iter = getResources().iterator(); iter.hasNext();) {
Resource rsrc = (Resource)iter.next();
String uriString = rsrc.getURI().toFileString();
if (uriString.endsWith(relativePath)) {
return rsrc;
}
}
}
return eResource;
}
/**
* @see org.eclipse.emf.ecore.resource.ResourceSet#getEObject(org.eclipse.emf.common.util.URI, boolean)
*/
@Override
public EObject getEObject( final URI uri,
final boolean loadOnDemand ) {
// If the resource URI is a file URI but there is no file on the file system
// corresponding to this location then return null because there is no resource
// that can be loaded to resolve this proxy and we do not want to create an
// empty resource in our container by calling getResource(URI,true)
URI eResourceURI = uri.trimFragment();
if (eResourceURI.isFile()) {
File f = new File(eResourceURI.toFileString());
if (!f.exists()) {
return null;
}
}
// If the resource is the built-in datatypes resource then we potentially need to convert
// from a logical URI to a physical URI. For example, if the logicalURI was
// "http://www.w3.org/2001/XMLSchema#string" we need to remap this to the physical URI of
// "file:/E:/.../cache/www.w3.org/2001/XMLSchema.xsd#//string;XSDSimpleTypeDefinition=7".
URI lookupUri = uri;
if (getEObjectHrefConverter() != null) {
URI physicalUri = getEObjectHrefConverter().getPhysicalURI(uri);
if (physicalUri != null) {
lookupUri = physicalUri;
}
}
EObject eObject = super.getEObject(lookupUri, loadOnDemand);
if (eObject == null) {
final String msg = ModelerCore.Util.getString("EResourceSetImpl.Unresolved_proxy", lookupUri); //$NON-NLS-1$
ModelerCore.Util.log(IStatus.WARNING, msg);
}
return eObject;
}
// ==================================================================================
// I N T E R F A C E M E T H O D S
// ==================================================================================
// /**
// * @see org.teiid.designer.core.resource.EResourceSet#getEObjectFinder()
// * @since 4.3
// */
// public EObjectFinder getEObjectFinder() {
// if (this.eObjectFinder == null) {
// this.eObjectFinder = new DefaultEObjectFinder(this);
// }
// return this.eObjectFinder;
// }
// /**
// * @see org.teiid.designer.core.resource.EResourceSet#getEResourceFinder()
// * @since 4.3
// */
// public EResourceFinder getEResourceFinder() {
// if (this.eResourceFinder == null) {
// this.eResourceFinder = new DefaultEResourceFinder(this);
// }
// return this.eResourceFinder;
// }
// /**
// * @see
// org.teiid.designer.core.resource.EResourceSet#setEObjectFinder(org.teiid.designer.core.resource.EObjectFinder)
// * @since 4.3
// */
// public void setEObjectFinder(final EObjectFinder theFinder) {
// this.eObjectFinder = theFinder;
// }
// /**
// * @see
// org.teiid.designer.core.resource.EResourceSet#setEResourceFinder(org.teiid.designer.core.resource.EResourceFinder)
// * @since 4.3
// */
// public void setEResourceFinder(final EResourceFinder theFinder) {
// this.eResourceFinder = theFinder;
// }
/**
* @see EResourceSet#getEObjectHrefConverter()
* @since 4.3
*/
@Override
public EObjectHrefConverter getEObjectHrefConverter() {
if (this.eObjectHrefConverter == null) {
this.eObjectHrefConverter = new BuiltInTypesHrefConverter(this);
}
return this.eObjectHrefConverter;
}
/**
* @see EResourceSet#setEObjectHrefConverter(EObjectHrefConverter)
* @since 4.3
*/
@Override
public void setEObjectHrefConverter( final EObjectHrefConverter theConverter ) {
this.eObjectHrefConverter = theConverter;
}
/**
* @see EResourceSet#addListener(org.eclipse.emf.edit.provider.INotifyChangedListener)
* @since 4.3
*/
@Override
public void addListener( final INotifyChangedListener notifyChangedListener ) {
changeNotifier.addListener(notifyChangedListener);
}
/**
* @see EResourceSet#removeListener(org.eclipse.emf.edit.provider.INotifyChangedListener)
* @since 4.3
*/
@Override
public void removeListener( final INotifyChangedListener notifyChangedListener ) {
changeNotifier.removeListener(notifyChangedListener);
}
/**
* Add a ResourceSet to be used for resolution of a resource URI. The specified ResourceSet will be treated as read-only and
* will never be used to load a resource for the URI being checked.
*
* @see EResourceSet#addExternalResourceSet(org.eclipse.emf.ecore.resource.ResourceSet)
*/
public void addExternalResourceSet( final ResourceSet resourceSet,
final Map physicalToLogicalUri ) {
if (resourceSet != null && !this.externalResourceSets.contains(resourceSet)) {
this.externalResourceSets.add(resourceSet);
// If URI mappings are specified then add them to the URIConverter
if (physicalToLogicalUri != null) {
for (Iterator i = physicalToLogicalUri.entrySet().iterator(); i.hasNext();) {
final Map.Entry entry = (Map.Entry)i.next();
final URI physicalURI = (URI)entry.getKey();
final URI logicalURI = (URI)entry.getValue();
getURIConverter().getURIMap().put(logicalURI, physicalURI);
}
}
}
}
@Override
public void addExternalResourceSet( final ResourceSet resourceSet ) {
if (resourceSet != null && !this.externalResourceSets.contains(resourceSet)) {
this.externalResourceSets.add(resourceSet);
}
}
/**
* Return the array of external resource sets registered with this resource set.
*
* @see EResourceSet#getExternalResourceSets()
* @return
* @since 4.3
*/
@Override
public ResourceSet[] getExternalResourceSets() {
return (ResourceSet[])this.externalResourceSets.toArray(new ResourceSet[this.externalResourceSets.size()]);
}
// ==================================================================================
// P U B L I C M E T H O D S
// ==================================================================================
/**
* Returns the IChangeNotifier associated with the EResourceSet.
*
* @return IChangeNotifier
* @since 3.1
*/
public IChangeNotifier getChangeNotifier() {
return this.changeNotifier;
}
// ==================================================================================
// P R O T E C T E D M E T H O D S
// ==================================================================================
/**
* Determine whether the resource given by the URI can be loaded. This method should throw a {@link WrappedException} or a
* runtime exception if the resource cannot be loaded.
*
* @param uri
* @return
* @since 4.2
*/
protected void canCreateResource( final URI uri ) {
checkForInvalidXmiVersion(uri);
checkForDuplicateUuid(uri);
}
/**
* Check if the UUID corresponding to the specified resource URI already exists in this resource set. If one does exist a
* DuplicateResourceException is thrown.
*
* @param uri
* @since 4.3
*/
protected void checkForDuplicateUuid( final URI uri ) {
final XMIHeader header = doGetXMIHeader(uri);
if (header != null && header.getUUID() != null) {
// There is already a file at this URI, so check the UUID ...
final String uuidString = header.getUUID();
try {
final ObjectID uuid = IDGenerator.getInstance().stringToObject(uuidString);
for (Iterator iter = getResources().iterator(); iter.hasNext();) {
Resource rsrc = (Resource)iter.next();
if (rsrc instanceof EResource && uuid.equals(((EResource)rsrc).getUuid())) {
final Object[] params = new Object[] {URI.decode(uri.toString()), URI.decode(rsrc.getURI().toString())};
final String msg = ModelerCore.Util.getString("EResourceSetImpl.Duplicate_resource_UUID_encountered", params); //$NON-NLS-1$
throw new DuplicateResourceException(rsrc, null, msg);
}
}
} catch (InvalidIDException e) {
ModelerCore.Util.log(e);
}
}
}
/**
* Check if the XMI version corresponding to the specified resource URI represents an older 1.x version of the XMI
* specification. If it does a TeiidDesignerRuntimeException is thrown indicating that the resource must be converted to an
* xmi:version="2.0" file
*
* @param uri
* @since 4.3
*/
protected void checkForInvalidXmiVersion( final URI uri ) {
final XMIHeader header = doGetXMIHeader(uri);
if (header != null && header.getXmiVersion() != null && header.getXmiVersion().startsWith("1.")) { //$NON-NLS-1$
Object[] params = new Object[] {uri};
String msg = ModelerCore.Util.getString("EResourceSetImpl.Old_model_format_encountered", params); //$NON-NLS-1$
throw new TeiidDesignerRuntimeException(msg);
}
}
/**
* Read the XMIHeader from the underlying file specified by the resource URI. If the URI is not a file URI or the underlying
* file does not exists then null is returned.
*
* @param uri
* @return
* @since 4.3
*/
protected XMIHeader doGetXMIHeader( final URI uri ) {
// First, normalize the URI to the 'physical' location ...
URIConverter theURIConverter = getURIConverter();
URI normalizedURI = theURIConverter.normalize(uri);
// If the file has an absolute path ...
// [note: test isFile first, because URI.toFileString returns NULL if not a file.]
if (normalizedURI.isFile()) {
// Find the corresponding file for this location ...
File f = new File(normalizedURI.toFileString());
// Return the header only if the file exists (if it doesn't exist, there's nothing to read) ...
if (f.isFile() && f.exists()) {
try {
return XMIHeaderReader.readHeader(f);
} catch (TeiidDesignerException e) {
ModelerCore.Util.log(e);
}
}
}
return null;
}
}