/*
* 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.vdb.ui.util;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.teiid.core.designer.TeiidDesignerException;
import org.teiid.core.designer.util.CoreStringUtil;
import org.teiid.core.designer.util.StringConstants;
import org.teiid.designer.common.xsd.XsdHeader;
import org.teiid.designer.common.xsd.XsdHeaderReader;
import org.teiid.designer.core.ModelEditorImpl;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.container.ContainerImpl;
import org.teiid.designer.core.workspace.ModelUtil;
import org.teiid.designer.core.workspace.WorkspaceResourceFinderUtil;
import org.teiid.designer.core.xmi.ModelImportInfo;
import org.teiid.designer.core.xmi.XMIHeader;
import org.teiid.designer.core.xmi.XMIHeaderReader;
import org.teiid.designer.metamodels.core.ModelAnnotation;
import org.teiid.designer.ui.util.ErrorHandler;
import org.teiid.designer.vdb.Vdb;
import org.teiid.designer.vdb.XmiVdb;
/**
* This class provides utilities to find resources given in input VDB file
*
* @since 8.0
*/
public class VdbResourceFinder {
@SuppressWarnings("javadoc")
protected static final Resource[] EMPTY_RESOURCE_ARRAY = new Resource[0];
private ContainerImpl container;
private Vdb vdb;
private IFile vdbFile;
private Collection<Resource> vdbResourceFiles;
/**
* @param vdbFile the vdbFile
* @throws CoreException
*/
public VdbResourceFinder(IFile vdbFile) throws CoreException {
this.vdbFile = vdbFile;
initialize();
}
private void initialize() throws CoreException {
this.container = (ContainerImpl)ModelerCore.createContainer("tempVdbModelContainer"); //$NON-NLS-1$
ModelEditorImpl.setContainer(this.container);
try {
this.vdb = new XmiVdb(vdbFile, false);
} catch (Exception ex) {
throw ErrorHandler.toCoreException(ex);
}
Collection<Resource> resourceFiles = new ArrayList<Resource>();
Collection<File> modelFiles = getVdb().getModelFiles();
for (File modelFile : modelFiles) {
Resource res = this.container.getResource(URI.createFileURI(modelFile.getPath()), true);
resourceFiles.add(res);
}
Collection<File> schemaFiles = getVdb().getSchemaFiles();
for (File modelFile : schemaFiles) {
Resource res = this.container.getResource(URI.createFileURI(modelFile.getPath()), true);
resourceFiles.add(res);
}
this.vdbResourceFiles = resourceFiles;
}
/**
* This method needs to be called in order to clean up/restore the ModelEditor's container
*/
public void dispose() {
this.vdb = null;
this.container = null;
ModelEditorImpl.setContainer(null);
}
/**
* @return the temporary Vdb Model container
*/
public ContainerImpl getContainer() {
return this.container;
}
/**
* @return the Vdb
*/
public Vdb getVdb() {
return this.vdb;
}
/**
* @return the vdbFile
*/
public IFile getVdbFile() {
return this.vdbFile;
}
/**
* @return collection of Resource's
* @throws CoreException
*/
public Collection<Resource> getWebServiceResources() throws CoreException {
Collection<Resource> webServiceModels = new ArrayList<Resource>();
Collection<File> modelFiles = getVdb().getModelFiles();
for (File modelFile : modelFiles) {
boolean isVisible = true;
Resource res = this.container.getResource(URI.createFileURI(modelFile.getPath()), true);
if (isVisible && ModelUtil.isModelFile(res) && !ModelUtil.isXsdFile(res)) {
EObject firstEObj = res.getContents().get(0);
ModelAnnotation ma = ModelerCore.getModelEditor().getModelAnnotation(firstEObj);
String mmURI = ma.getPrimaryMetamodelUri();
if (ModelUtil.URI_WEB_SERVICES_VIEW_MODEL.equalsIgnoreCase(mmURI)) {
webServiceModels.add(res);
}
}
}
return webServiceModels;
}
/**
* @param resource
* @return Resource array
*/
public Resource[] getDependentResources( final Resource resource ) {
if (resource == null || getWorkspace() == null) return EMPTY_RESOURCE_ARRAY;
File theFile = new File(resource.getURI().toFileString());
final Collection<Resource> result = new ArrayList<Resource>();
try {
// Get the header information from the XSD file
if (ModelUtil.isXsdFile(resource)) {
final XsdHeader header = XsdHeaderReader.readHeader(theFile);
if (header != null) {
// Add all the imported schema locations
String[] locations = header.getImportSchemaLocations();
for (int i = 0; i != locations.length; ++i) {
final String location = locations[i];
// TODO: Need to convert the following call to work with "Resource" instead of "IResource"
Resource dependentResource = findResource(location);
if (dependentResource == null) {
final String absolutePath = WorkspaceResourceFinderUtil.getAbsoluteLocation(theFile, location);
dependentResource = findResource(absolutePath);
}
if (dependentResource != null && !result.contains(dependentResource)) result.add(dependentResource);
}
// Add all the included schema locations
locations = header.getIncludeSchemaLocations();
for (int i = 0; i != locations.length; ++i) {
final String location = locations[i];
// TODO: Need to convert the following call to work with "Resource" instead of "IResource"
Resource dependentResource = findResource(location);
if (dependentResource == null) {
final String absolutePath = WorkspaceResourceFinderUtil.getAbsoluteLocation(theFile, location);
dependentResource = findResource(absolutePath);
}
if (dependentResource != null && !result.contains(dependentResource)) result.add(dependentResource);
}
}
// Get the header information from the XMI file
} else if (ModelUtil.isModelFile(resource)) {
final XMIHeader header = XMIHeaderReader.readHeader(theFile);
if (header != null) {
String fullFilePath = resource.getURI().path();
IPath fileLocationPath = new Path(fullFilePath).removeLastSegments(1);
String fileLocation = fileLocationPath.toOSString();
final ModelImportInfo[] infos = header.getModelImportInfos();
for (final ModelImportInfo info : infos) {
Resource dependentResource = null;
final String location = info.getLocation();
if( !location.startsWith("http:") ) { //$NON-NLS-1$
final String path = new Path(fileLocation).append(location).toOSString() ;
// TODO: Need to convert the following call to work with "Resource" instead of "IResource"
if (!CoreStringUtil.isEmpty(path)) {
dependentResource = findResource(path);
} else if (!CoreStringUtil.isEmpty(location)) {
final String depPath = fileLocation;
if (!WorkspaceResourceFinderUtil.isGlobalResource(depPath)) {
dependentResource = findResource(depPath);
if (dependentResource == null) {
final String absolutePath = WorkspaceResourceFinderUtil.getAbsoluteLocation(theFile, location);
dependentResource = findResource(absolutePath);
}
}
}
if (dependentResource != null && !result.contains(dependentResource)) {
result.add(dependentResource);
}
}
}
}
}
} catch (final Exception err) {
final Object[] params = new Object[] {resource.getURI()};
final String msg = ModelerCore.Util.getString("WorkspaceResourceFinderUtil.Error_getting_model_imports_from_resource", params); //$NON-NLS-1$
ModelerCore.Util.log(IStatus.ERROR, err, msg);
}
return result.toArray(new Resource[result.size()]);
}
/**
* Returns the workbench associated with this object.
* @return the workspace
*/
private IWorkspace getWorkspace() {
return ModelerCore.getWorkspace();
}
/**
* Return the Resource instance corresponding to the specified URI string. The URI represents a relative path within the
* workspace to particular file resource. If the URI is one of the well-known Teiid Designer/EMF identifiers to a global
* resource such as
* <p>
* "http://www.metamatrix.com/metamodels/SimpleDatatypes-instance" "http://www.w3.org/2001/XMLSchema"
* "http://www.w3.org/2001/MagicXMLSchema" "http://www.w3.org/2001/XMLSchema-instance"
* </p>
* then null is returned since there is no Resource in the workspace that represents any one of those models.
*
* @param workspaceUri the URI string
* @return the IResource identified by the URI if it exists; may return null
*/
public Resource findResource( final String workspaceUri ) {
if (!WorkspaceResourceFinderUtil.isValidWorkspaceUri(workspaceUri)) return null;
final String normalizedUriString = WorkspaceResourceFinderUtil.normalizeUriString(workspaceUri);
// Check existing vdb models
File modelFile = new File(normalizedUriString);
if( modelFile.exists() ) {
return getExistingVdbResource(modelFile);
}
// MyDefect : 16368 Refactored methods.
Resource fileResource = null;
final Collection<Resource> fileResources = this.vdbResourceFiles;
// If the workspace URI starts with "http" then check it against the target
// namespaces of any XML schema in the workspace ...
fileResource = getResourceStartsWithHttp(fileResources, normalizedUriString);
if (fileResource != null) return fileResource;
// Try to convert the workspace URI to a relative path and then match
// this path in workspace to one of the IResource paths instances
fileResource = getResourceStartsWithPathSeparator(fileResources, normalizedUriString);
if (fileResource != null) return fileResource;
// Try to match the workspace URI to a IResource location ...
fileResource = getResourceByLocation(fileResources, normalizedUriString);
if (fileResource != null) return fileResource;
return null;
}
private Resource getExistingVdbResource(File file) {
for( Resource modelFile : this.vdbResourceFiles) {
String vdbFileStr = WorkspaceResourceFinderUtil.normalizeUriString(modelFile.getURI().toFileString());
//This will remove the drive from the file string on Windows
String prefix = vdbFileStr.substring(0, vdbFileStr.indexOf("\\")>-1?vdbFileStr.indexOf("\\"):0); //$NON-NLS-1$ //$NON-NLS-2$
if (prefix.length() > 0 && prefix.contains(":")){ //$NON-NLS-1$
vdbFileStr = vdbFileStr.replace(prefix, ""); //$NON-NLS-1$
}
String fileStr = file.getPath();
if( vdbFileStr.equals(fileStr) ) {
return modelFile;
}
}
return null;
}
/**
* @param resources
* @param dependentSchemas
*/
public void getAllDependentSchemas( Resource[] resources, ArrayList<Resource> dependentSchemas) {
// Add discovered dependent schemas
for (Resource resource : resources) {
if (ModelUtil.isXsdFile(resource) && shouldAddSchema(resource, dependentSchemas)) {
dependentSchemas.add(resource);
}
}
// Now iterate through the dependent schemas and find their dependent
// resources, if any
for (Resource resource : resources) {
Resource[] moreResources = getDependentResources(resource);
if (moreResources.length > 0) {
getAllDependentSchemas(moreResources, dependentSchemas);
}
}
}
private boolean shouldAddSchema(Resource resource, ArrayList<Resource> dependentSchemas) {
for( Resource schemaResource : dependentSchemas ) {
if( resource.getURI().toFileString().equalsIgnoreCase(schemaResource.getURI().toFileString()) ) {
return false;
}
}
return true;
}
private Resource getResourceStartsWithHttp(final Collection<Resource> fileResources,
final String workspaceUri) {
File file = null;
String targetNamespace;
if (workspaceUri.startsWith("http")) { //$NON-NLS-1$
for (final Resource fileResource2 : fileResources) {
File modelFile = new File(fileResource2.getURI().toFileString());
file = modelFile;
targetNamespace = getXsdTargetNamespace(file);
if (workspaceUri.equals(targetNamespace))
return fileResource2;
}
}
return null;
}
private String getXsdTargetNamespace( final File file ) {
if (ModelUtil.isXsdFile(file)) {
if (file.exists()) try {
final XsdHeader header = XsdHeaderReader.readHeader(file);
if (header != null) return header.getTargetNamespaceURI();
} catch (final TeiidDesignerException e) {
ModelerCore.Util.log(IStatus.ERROR, e, e.getMessage());
}
}
return null;
}
private static Resource getResourceStartsWithPathSeparator( final Collection<Resource> fileResources, final String workspaceUri) {
Resource fileResource = null;
IPath pathInWorkspace;
if (workspaceUri.charAt(0) == IPath.SEPARATOR) {
pathInWorkspace = new Path(workspaceUri);
URI pathInWorkspaceURI = URI.createURI(pathInWorkspace.toString());
for (final Resource fileResource2 : fileResources) {
fileResource = fileResource2;
if (fileResource != null && fileResource.getURI().equals(pathInWorkspaceURI))
return fileResource;
}
}
return null;
}
private static Resource getResourceByLocation(final Collection<Resource> fileResources,
final String workspaceUri) {
Resource fileResource = null;
String resourceLocation;
// Try to match the workspace URI to a Resource URI ...
for (final Resource fileResource2 : fileResources) {
fileResource = fileResource2;
resourceLocation = fileResource.getURI().toString();
if (workspaceUri.endsWith(resourceLocation))
return fileResource;
}
// Case 5683 - look for a match of the supplied workspaceUri (usually
// file.ext) to the
// last segment of the fileResource path.
for (final Resource fileResource2 : fileResources) {
fileResource = fileResource2;
final String fileNameSegment = fileResource.getURI().lastSegment();
if (fileNameSegment != null && fileNameSegment.equalsIgnoreCase(workspaceUri))
return fileResource;
}
// MyDefect : 16368 Added to fix the defect
for (final Resource fileResource2 : fileResources) {
fileResource = fileResource2;
final IPath resrcLocation = new Path(fileResource.getURI().toFileString());
resourceLocation = resrcLocation.toOSString();
if (resourceLocation.endsWith(workspaceUri))
return fileResource;
resourceLocation = resrcLocation.toString();
//Check for backslash vs slash
resourceLocation = resourceLocation.replaceAll("/", "\\\\"); //$NON-NLS-1$ //$NON-NLS-2$
///Windows fix for spaces in workspace path
resourceLocation = resourceLocation.replaceAll(StringConstants.SPACE, "%20"); //$NON-NLS-1$
if (resourceLocation.endsWith(workspaceUri))
return fileResource;
}
return null;
}
}