/*
* 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.builder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.teiid.core.designer.ModelerCoreException;
import org.teiid.core.designer.id.ObjectID;
import org.teiid.core.designer.plugin.PluginUtilities;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.util.Stopwatch;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.container.Container;
import org.teiid.designer.core.container.DuplicateResourceException;
import org.teiid.designer.core.index.ModelIndexer;
import org.teiid.designer.core.index.ModelSearchIndexer;
import org.teiid.designer.core.index.ResourceIndexer;
import org.teiid.designer.core.refactor.OrganizeImportCommand;
import org.teiid.designer.core.refactor.OrganizeImportHandler;
import org.teiid.designer.core.resource.EmfResource;
import org.teiid.designer.core.validation.ValidationContext;
import org.teiid.designer.core.workspace.ModelResource;
import org.teiid.designer.core.workspace.ModelUtil;
import org.teiid.designer.core.workspace.ModelWorkspace;
import org.teiid.designer.core.workspace.ModelWorkspaceException;
/**
* This class was derived from ModelBuildUtil class in order to streamline the validation of models that are specifically part of of a Vdb.
*
* In particular, a VDB doesn't care about Search indexes, so the ModelSearchIndexer isn't needed and is not added to INDEXERS list.
*
* Also, a synchronizing flag is accessible via start() and stop() methods so a VDB can notify the builder that indexed or validated resources should
* be cached so any attempt to re-index or re-validate will be ignored. This can be a performance hog if not enabled in the VDB's synchronize ALL process.
*
* @since 8.0
*/
@SuppressWarnings("javadoc")
public class VdbModelBuilder {
public static final int MONITOR_TASK_NAME_MAX_LENGTH = 200;
public static final String TASK_NAME_TRUNCTATION_SUFFIX = ModelerCore.Util.getString("ModelBuildUtil.taskNameTruncationSuffix"); //$NON-NLS-1$
public static final String MONITOR_RESOURCE_VALIDATION_MSG = ModelerCore.Util.getString("ModelBuildUtil.Validating_Resource__1"); //$NON-NLS-1$
public static final String MONITOR_OBJECT_VALIDATION_MSG = ModelerCore.Util.getString("ModelBuildUtil.Validating__2"); //$NON-NLS-1$
private static Collection VALIDATORS;
private static Collection INDEXERS;
private Collection<IResource> indexedResources;
private Collection<IResource> builtResources;
private boolean synchronizing = false;
static {
// initialize the validators collection
initValidators();
// initialize the indexers collection
initIndexers();
}
public VdbModelBuilder() {
super();
this.indexedResources = new ArrayList<IResource>();
this.builtResources = new ArrayList<IResource>();
}
public void start() {
this.synchronizing = true;
this.indexedResources.clear();
this.builtResources.clear();
}
public void stop() {
this.synchronizing = false;
this.indexedResources.clear();
this.builtResources.clear();
}
/**
* Index and validate the collection of IResources being passed. Validation of the resources can be within the context of the
* resources being passed in. The validation rules that use the context may use this information.
*
* @param monitor The progressMonitor, may be null
* @param resources Collection of IResouurces
* @param validateInContext If true the validationContext is updated with the collection of resources.
* @since 4.2
*/
public void buildResources( final IProgressMonitor monitor,
final Collection iResources,
final Container container,
final boolean validateInContext ) {
// Get the list of "modified" EMF resources within the model container
// prior to performing the build. Since indexing and validation are
// considered read-only operations we must make sure that any resources
// that are loaded as a result of these operations have a status of
// not modified
final List modifiedResources = getModifiedResources();
// index all the resources
indexResources(monitor, iResources);
// Reset the modified state of all EMF resources to what it was
// prior to indexing. This needs to be done since indexing may
// mark an XSD resource as modified causing the XSDResourceValidator
// to not validate it.
setModifiedResources(modifiedResources);
// validate all the resources
validateResources(monitor, iResources, container, validateInContext);
// validateResources(monitor, dependentModels, container, validateInContext);
// Reset the modified state of all EMF resources to what it was prior to validating
setModifiedResources(modifiedResources);
}
// ############################################################################################################################
// # Validation Methods #
// ############################################################################################################################
/**
* Deletes all markers for the supplied list of IResources
*
* @param iResources
* @since 5.0.2
*/
public void clearResourceMarkers( final Collection iResources ) {
try {
// clear all markers on this resource
final Iterator itr = iResources.iterator();
while (itr.hasNext()) {
final IResource iResource = (IResource)itr.next();
if (iResource.exists()) {
iResource.deleteMarkers(IMarker.PROBLEM, false, IResource.DEPTH_INFINITE);
} // endif
}
} catch (final CoreException e) {
ModelerCore.Util.log(e);
}
}
/**
* Deletes all markers for the supplied IResource
*
* @param iResource
* @since 5.0.2
*/
public void clearResourceMarkers( final IResource iResource ) {
try {
// clear all markers on this resource, as we are
// going to create fresh markets
// defect 16537 - make sure resource exists before deleting markers
if (iResource.exists()) {
iResource.deleteMarkers(IMarker.PROBLEM, false, IResource.DEPTH_INFINITE);
} // endif
} catch (final CoreException e) {
ModelerCore.Util.log(e);
}
}
public void createModelIndexes( final IProgressMonitor monitor,
final Collection resources ) {
final ModelIndexer modelIndexer = new ModelIndexer();
indexResources(monitor, resources, modelIndexer);
}
/**
* Create a new instance of ValidationContext to use during validation
*/
public ValidationContext createValidationContext() {
ValidationContext context = null;
final Plugin corePlugin = ModelerCore.getPlugin();
if (corePlugin != null && !ModelerCore.ignoreValidationPreferencesOnBuild()) {
context = new ValidationContext(ModelerCore.PLUGIN_ID);
} else {
// non plugin environment
context = new ValidationContext();
}
return context;
}
public List getModifiedResources() {
try {
return getModifiedResources(ModelerCore.getModelContainer().getResources());
} catch (final CoreException e) {
// do nothing - the method will return an empty list
}
return Collections.EMPTY_LIST;
}
public List getModifiedResources( final List eResources ) {
final List result = new ArrayList();
if (eResources != null) {
for (final Iterator iter = eResources.iterator(); iter.hasNext();) {
final Resource resource = (Resource)iter.next();
if (resource.isModified()) {
result.add(resource);
}
}
}
return result;
}
/**
* @param workspace
* @return
* @throws CoreException
* @since 4.3
*/
private Resource[] getWorkspaceResourcesInScope( final ModelWorkspace workspace ) throws CoreException {
final Resource[] wsEmfResources = workspace.getEmfResources();
final Collection resourcesInScope = new ArrayList(Arrays.asList(wsEmfResources));
// MyCode : 18565
updateResource(resourcesInScope);
return (Resource[])resourcesInScope.toArray(new Resource[resourcesInScope.size()]);
}
public void indexResource( IProgressMonitor monitor,
final IResource iResource,
final ResourceIndexer indexer ) {
if( this.synchronizing && this.indexedResources.contains(iResource) ) {
return;
}
//System.out.println(" >>> VdbModelBuilder.indexResource() = " + iResource.getName());
// create a monitor if needed
monitor = monitor != null ? monitor : new NullProgressMonitor();
if (monitor.isCanceled()) {
return;
}
final String sTask = ModelerCore.Util.getString("ModelBuildUtil.Creating_{0}_for_{1}_1", indexer.getIndexType(), iResource.getFullPath()); //$NON-NLS-1$
//System.out.println("[ModelBuildUtil.indexResource TaskName is: " + sTask ); //$NON-NLS-1$
monitor.setTaskName(sTask);
try {
this.indexedResources.add(iResource);
final Stopwatch totalWatch = new Stopwatch();
totalWatch.start();
indexer.indexResource(iResource, false, true);
totalWatch.stop();
} catch (final Throwable e) {
ModelerCore.Util.log(IStatus.ERROR,
e,
ModelerCore.Util.getString("ModelBuilder.Error_indexing_model_resource_3", iResource.getFullPath())); //$NON-NLS-1$
}
}
public void indexResources( IProgressMonitor monitor,
final Collection resources ) {
// create a monitor if needed
monitor = monitor != null ? monitor : new NullProgressMonitor();
// get all indexers and index
for (final Iterator indexerIter = INDEXERS.iterator(); indexerIter.hasNext();) {
final ResourceIndexer indexer = (ResourceIndexer)indexerIter.next();
indexResources(monitor, resources, indexer);
}
}
public void indexResources( final IProgressMonitor monitor,
final Collection resources,
final ResourceIndexer indexer ) {
for (final Iterator rsourceIter = resources.iterator(); rsourceIter.hasNext();) {
final IResource resource = (IResource)rsourceIter.next();
indexResource(monitor, resource, indexer);
if (monitor != null) {
monitor.worked(1);
}
}
}
private static void initIndexers() {
// Find all extensions of the notifiers extension point
final String id = ModelerCore.EXTENSION_POINT.RESOURCE_INDEXER.UNIQUE_ID;
final IExtension[] extensions = PluginUtilities.getExtensions(id);
// initialize the indexers array
INDEXERS = new ArrayList(extensions.length);
for (int i = 0; i < extensions.length; ++i) {
final IExtension extension = extensions[i];
final String element = ModelerCore.EXTENSION_POINT.RESOURCE_INDEXER.ELEMENTS.CLASS;
final String attribute = ModelerCore.EXTENSION_POINT.RESOURCE_INDEXER.ATTRIBUTES.NAME;
try {
final Object instance = PluginUtilities.createExecutableExtension(extension, element, attribute);
if (instance instanceof ResourceIndexer ) {
if( !(instance instanceof ModelSearchIndexer) ) {
INDEXERS.add(instance);
}
} else {
final String message = ModelerCore.Util.getString("ModelBuildUtil.Extension_class_not_instance_of_IIndexer_1"); //$NON-NLS-1$
ModelerCore.Util.log(message);
}
} catch (final CoreException e) {
ModelerCore.Util.log(e);
}
}
}
// ############################################################################################################################
// # Indexing Methods #
// ############################################################################################################################
private static void initValidators() {
// Find all extensions of the notifiers extension point
final String id = ModelerCore.EXTENSION_POINT.RESOURCE_VALIDATOR.UNIQUE_ID;
final IExtension[] extensions = PluginUtilities.getExtensions(id);
// initialize the validators array
VALIDATORS = new ArrayList(extensions.length);
for (int i = 0; i < extensions.length; ++i) {
final IExtension extension = extensions[i];
final String element = ModelerCore.EXTENSION_POINT.RESOURCE_VALIDATOR.ELEMENTS.CLASS;
final String attribute = ModelerCore.EXTENSION_POINT.RESOURCE_VALIDATOR.ATTRIBUTES.NAME;
try {
final Object instance = PluginUtilities.createExecutableExtension(extension, element, attribute);
if (instance instanceof ResourceValidator) {
VALIDATORS.add(instance);
} else {
final String message = ModelerCore.Util.getString("ModelBuildUtil.Extension_class_not_instance_of_ResourceValidator_1"); //$NON-NLS-1$
ModelerCore.Util.log(message);
}
} catch (final CoreException e) {
ModelerCore.Util.log(e);
}
}
}
private void internalValidateResource( final IProgressMonitor monitor,
final IResource iResource,
final ResourceValidator validator,
final ValidationContext context,
final boolean clearMarkers ) {
if( this.synchronizing && this.builtResources.contains(iResource) ) {
return;
}
//System.out.println(" >>> VdbModelBuilder.internalValidateResource() = " + iResource.getName());
// create a monitor if needed
final IProgressMonitor progresssMonitor = (monitor != null ? monitor : new NullProgressMonitor());
if (progresssMonitor.isCanceled() || !validator.isValidatorForObject(iResource)) {
return;
}
// path to the resource in the workspace
progresssMonitor.setTaskName(MONITOR_RESOURCE_VALIDATION_MSG + iResource.getFullPath());
// See if the model was marked as a duplicate ...
Object duplicateOfModel = null;
try {
duplicateOfModel = iResource.getSessionProperty(ModelerCore.DUPLICATE_MODEL_OF_IPATH_KEY);
} catch (final CoreException err) {
// Do nothing; treat as tho not a duplicate ...
}
if (clearMarkers) {
clearResourceMarkers(iResource);
}
// Try to validate; if this is the first time this resource is opened, the duplicate model
// session property might not be assigned, and a ModelWorkspaceException may be thrown when
// opening the model
if (duplicateOfModel == null) {
this.builtResources.add(iResource);
try {
// Validate the model ...
final ModelWorkspace workspace = ModelerCore.getModelWorkspace();
final ModelResource mResource = workspace.findModelResource(iResource);
// Find the Resource for the given IResource (unless IResource is VDB Resource)
// VDB IResources do not have a corresponding Emf Resource.
Resource resource = null;
if (!ModelUtil.isVdbArchiveFile(iResource)) {
try {
if (mResource != null && mResource.getEmfResource() != null) {
resource = mResource.getEmfResource();
} else {
// Force a load if it not already loaded.
IPath location = ModelUtil.getLocation(iResource);
if (location != null) {
final URI uri = URI.createFileURI(location.toString());
resource = ModelerCore.getModelContainer().getResource(uri, true);
if (resource != null) {
resource.setModified(false);
}
}
}
} catch (final Exception e) {
// Do nothing. IResources that do not wrap an emf Resource will throw an exception here...
// Let the validator decide what to do if no emf Resource can be found.
}
}
final Object objToValidate = (resource != null ? (Object)resource : (Object)iResource);
final Stopwatch totalWatch = new Stopwatch();
totalWatch.start();
validator.validate(progresssMonitor, objToValidate, context);
totalWatch.stop();
validator.addMarkers(context, iResource);
context.clearResults();
} catch (final ModelerCoreException e) {
final Throwable underlyingException = e.getException();
if (underlyingException instanceof DuplicateResourceException) {
// Look again for the duplicate of model path ...
try {
duplicateOfModel = iResource.getSessionProperty(ModelerCore.DUPLICATE_MODEL_OF_IPATH_KEY);
} catch (final CoreException err) {
ModelerCore.Util.log(err);
}
} else {
ModelerCore.Util.log(e);
}
}
}
// Now handle the case when this is a duplicate model ...
if (duplicateOfModel != null) {
try {
final Object[] params = new Object[] {duplicateOfModel};
final String msg = ModelerCore.Util.getString("ModelBuildUtil.ModelDuplicateOf_0", params); //$NON-NLS-1$
// The Model is a duplicate, so don't validate ...
final IMarker marker = iResource.createMarker(IMarker.PROBLEM);
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
marker.setAttribute(IMarker.LOCATION, duplicateOfModel);
marker.setAttribute(ModelerCore.MARKER_URI_PROPERTY, null);
marker.setAttribute(ModelerCore.TARGET_MARKER_URI_PROPERTY, null);
marker.setAttribute(IMarker.MESSAGE, msg);
} catch (final CoreException e) {
ModelerCore.Util.log(e);
}
}
}
/**
* Basic Update Imports utility method.
*
* @param modelResource
* @param includeDiagramReferences
* @return
* @throws ModelWorkspaceException
* @since 5.0.2
*/
public boolean rebuildImports( final Resource resource,
final boolean includeDiagramReferences ) {
if (resource != null) {
final ModelResource mr = ModelerCore.getModelEditor().findModelResource(resource);
if (mr != null && !mr.isReadOnly()) {
final OrganizeImportHandler handler = new OrganizeImportHandler() {
@Override
public Object choose( final List options ) {
return null;
}
};
final OrganizeImportCommand importCommand = new OrganizeImportCommand();
importCommand.setResource(resource);
importCommand.setHandler(handler);
importCommand.setIncludeDiagramReferences(includeDiagramReferences);
final IProgressMonitor monitor = null; // for now
final IStatus status = importCommand.canExecute();
if (status.isOK()) {
final IStatus runStatus = importCommand.execute(monitor);
if (!runStatus.isOK()) {
ModelerCore.Util.log(runStatus);
return false;
}
return true;
}
ModelerCore.Util.log(status);
}
}
return false;
}
public void setModifiedResources( final List modifiedResources ) {
try {
final Container container = ModelerCore.getModelContainer();
for (final Object element : container.getResources()) {
final Resource resource = (Resource)element;
if (modifiedResources != null && modifiedResources.contains(resource)) {
resource.setModified(true);
} else {
resource.setModified(false);
}
}
} catch (final CoreException theException) {
ModelerCore.Util.log(IStatus.ERROR, theException, theException.getMessage());
}
}
/**
* updates the collection if the systemVdbResources are not already in the collection
*
* @param resourcesInScope
* @since 4.3
*/
private void updateResource( final Collection resourcesInScope ) {
final Resource[] systemVdbResources = ModelerCore.getSystemVdbResources();
for (final Resource systemVdbResource : systemVdbResources) {
if (systemVdbResource instanceof EmfResource) {
final ObjectID objectID = ((EmfResource)systemVdbResource).getUuid();
try {
final Resource resrc = ModelerCore.getModelContainer().getResourceFinder().findByUUID(objectID, false);
if (resrc != null && !resourcesInScope.contains(resrc)) {
resourcesInScope.add(systemVdbResource);
}
} catch (final CoreException err) {
ModelerCore.Util.log(err);
}
} else {
resourcesInScope.add(systemVdbResource);
}
}
}
public void validateResource( final IProgressMonitor monitor,
final IResource iResource,
final ResourceValidator validator,
final ValidationContext context ) {
internalValidateResource(monitor, iResource, validator, context, true);
}
/**
* Validate the {@link org.eclipse.emf.ecore.resource.Resource} using the specified
*
* @param monitor
* @param eResource
* @param validator
* @param context
* @since 4.2
*/
private void validateResource( final IProgressMonitor monitor,
final Resource eResource,
final ResourceValidator validator,
final ValidationContext context ) {
CoreArgCheck.isNotNull(eResource);
CoreArgCheck.isNotNull(validator);
CoreArgCheck.isNotNull(context);
// create a monitor if needed
final IProgressMonitor progresssMonitor = (monitor != null ? monitor : new NullProgressMonitor());
if (monitor.isCanceled() || !validator.isValidatorForObject(eResource)) {
return;
}
monitor.setTaskName(MONITOR_RESOURCE_VALIDATION_MSG + eResource.getURI().lastSegment());
final boolean isModified = eResource.isModified();
try {
final Stopwatch totalWatch = new Stopwatch();
totalWatch.start();
validator.validate(progresssMonitor, eResource, context);
totalWatch.stop();
} catch (final ModelerCoreException e) {
ModelerCore.Util.log(e);
} finally {
// Restore the "modified" state to what is was prior to validation
eResource.setModified(isModified);
}
}
/**
* Validate the {@link org.eclipse.emf.ecore.resource.Resource} returning the result in the supplied ValidationContext instance.
* After validation is complete the user should clear the ValidationContext to free up memory.
*
* @param monitor The progressMonitor, may be null
* @param eResource the resource to validate
* @param context the validation context to use
* @since 4.2
*/
public void validateResource( final IProgressMonitor monitor,
final Resource eResource,
final ValidationContext context ) {
CoreArgCheck.isNotNull(eResource);
CoreArgCheck.isNotNull(context);
// get all validators and validate
for (final Iterator validateIter = VALIDATORS.iterator(); validateIter.hasNext();) {
final ResourceValidator validator = (ResourceValidator)validateIter.next();
validateResource(monitor, eResource, validator, context);
}
}
/**
* Validate the collection of IResources being passed. Validation of the resources can be within the context of the resources
* being passed in. The validation rules that use the context may use this information.
*
* @param monitor The progressMonitor, may be null
* @param resources Collection of IResouurces
* @param validateInContext If true the validationContext is updated with the collection of resources.
* @since 4.2
*/
public void validateResources( final IProgressMonitor monitor,
final Collection iResources,
final Container container,
final boolean validateInContext ) {
// create a monitor if needed
final IProgressMonitor progresssMonitor = (monitor != null ? monitor : new NullProgressMonitor());
// Create the validation context to use
final ValidationContext context = createValidationContext();
// set the resource scope (all model resources in open model projects)
final ModelWorkspace workspace = ModelerCore.getModelWorkspace();
try {
final Resource[] resourcesInScope = getWorkspaceResourcesInScope(workspace);
context.setResourcesInScope(resourcesInScope);
} catch (final CoreException theException) {
ModelerCore.Util.log(theException);
}
// set the resources on the context since validation
// is in context of the resources.
if (validateInContext && (iResources != null)) {
final List temp = new ArrayList();
final Iterator itr = iResources.iterator();
while (itr.hasNext()) {
final IResource iResource = (IResource)itr.next();
if (ModelUtil.isModelFile(iResource)) {
try {
final ModelResource modelResource = ModelerCore.getModelEditor().findModelResource((IFile)iResource);
if (modelResource != null) {
temp.add(modelResource.getEmfResource());
}
} catch (final ModelWorkspaceException theException) {
ModelerCore.Util.log(theException);
}
}
}
context.setResourcesToValidate((Resource[])temp.toArray(new Resource[temp.size()]));
}
context.setResourceContainer(container); // may be null
clearResourceMarkers(iResources);
// get all validators and validate
for (final Iterator validateIter = VALIDATORS.iterator(); validateIter.hasNext();) {
final ResourceValidator validator = (ResourceValidator)validateIter.next();
validator.validationStarted(iResources, context);
try {
for (final Iterator rsourceIter = iResources.iterator(); rsourceIter.hasNext();) {
final IResource resource = (IResource)rsourceIter.next();
internalValidateResource(progresssMonitor, resource, validator, context, false);
}
} finally {
validator.validationEnded(context);
}
}
// clear the context after validation to free up memory
context.clearState();
}
/**
* Validate the collection of {@link org.eclipse.emf.ecore.resource.Resource} instances returning the result in the supplied
* ValidationContext instance. After validation is complete the user should clear the ValidationContext to free up memory.
*
* @param monitor The progressMonitor, may be null
* @param eResources the collection of resources to validate
* @param context the validation context to use
* @since 4.2
*/
public void validateResources( final IProgressMonitor monitor,
final Resource[] eResources,
final ValidationContext context ) {
CoreArgCheck.isNotNull(eResources);
CoreArgCheck.isNotNull(context);
// create a monitor if needed
final IProgressMonitor progresssMonitor = (monitor != null ? monitor : new NullProgressMonitor());
// set the resources on the context since validation is in context of the resources.
context.setResourcesToValidate(eResources);
// get all validators and validate
for (int i = 0; i < eResources.length; ++i) {
validateResource(progresssMonitor, eResources[i], context);
}
}
// #############################################################################
// # Protected methods
// #############################################################################
}