/* * 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.validation; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.core.designer.util.CoreStringUtil; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.ValidationDescriptor; import org.teiid.designer.core.container.Container; import org.teiid.designer.core.index.IndexUtil; import org.teiid.designer.core.types.DatatypeManager; /** * ValidationContext contains state information stored durring validation process. * * @since 8.0 */ public class ValidationContext { private final static Resource[] EMPTY_RESOURCE_ARRAY = new Resource[0]; /** * A namespace qualifier for the preferences (typically a plugin ID). */ private String preferenceQualifier; // Map of preference key to IStatus severity private Map<String, Integer> preferenceStatusMap; // validation results to be passed as part of context as private List validationResults; // map between container uuid/uri and collection of rule names private Map rulesRun; // map between a target and transformation to which it is a target private Map targetTransformMap; // map between a target and validation results for the target private Map targetValidationResults; // collection of resources being validated private Resource[] resourcesToValidate; /** * Collection of {@link org.eclipse.emf.ecore.resource.Resource}s defining all the valid resources in the validation scope. * All resources not is this collection should not validate. When value is empty all resources are to be considered valid. * Value is never <code>null</code>. */ private Resource[] resourcesInScope = EMPTY_RESOURCE_ARRAY; // Reference to the container in which the resources exist. This is not required, // but specifying it is strongly suggested. No assumptions are made if no container is specified. private Container resourceContainer; // flag to indicate if VdbMetadata/ServerRuntimeMetadata should be used for query validation/resolution private boolean useServerIndexes = false; // flag to indicate if index files should be used for query validation/resolution private boolean useIndexesToResolve = true; // cache validation results for sqlTransformation mapping roots // defalut true for workspace validation private boolean cacheMappingRootResults = true; // directory containing the index files // defaults to the state directory of the modeler private String indexDirectory = IndexUtil.INDEX_PATH; // collection of uuids in the context of validation private Collection uuidsInContext = null; // collection of eobjects that should not be validated private Collection objectsToIgnore = null; // A place to store any other data private Map dataMap; /** * Construct an instance of ValidationContext. */ public ValidationContext() { this(null); } /** * @param preferenceQualifier the namespace qualifier for the preferences (typically a plugin ID) */ public ValidationContext( String preferenceQualifier ) { this.preferenceQualifier = preferenceQualifier; this.preferenceStatusMap = new HashMap<String, Integer>(); } /** * Add the uuid to the context, these uuid strings are used to check uniqueness. */ public void addUuidToContext( final String uuidString ) { if (this.uuidsInContext == null) { this.uuidsInContext = new HashSet(); } this.uuidsInContext.add(uuidString); } /** * Check if the context already contains this uuid string. */ public boolean containsUuid( final String uuidString ) { if (this.uuidsInContext != null) { return this.uuidsInContext.contains(uuidString); } return false; } /** * Obtains the collection of resources being validated. * <p> * If the {@link #getResourceContainer() resource container} is used, all of the {@link #getResourcesToValidate() resources in * the context} should exist in the same container. * </p> * * @return the resourcesToValidate (never <code>null</code>) * @since 4.2 */ public Resource[] getResourcesToValidate() { if (this.resourcesToValidate == null) { this.resourcesToValidate = EMPTY_RESOURCE_ARRAY; } return this.resourcesToValidate; } /** * Sets the collection of resources being validated. * <p> * If the {@link #getResourceContainer() resource container} is used, all of the {@link #getResourcesToValidate() resources in * the context} should exist in the same container. * </p> * * @param eResourcesToValidate the resources to validate * @since 4.2 */ public void setResourcesToValidate( final Resource[] eResourcesToValidate ) { this.resourcesToValidate = eResourcesToValidate; } /** * Sets the {@link org.eclipse.emf.ecore.resource.Resource}s that define the valid resources considered to be within the * validation scope. Resources that are not defined in this scope should be considered invalid and should result in a * validation error. * * @param theResources the resources in scope or <code>null</code> or empty if all resources are in scope * @since 4.2 */ public void setResourcesInScope( Resource[] theResources ) { if (theResources == null) { this.resourcesInScope = EMPTY_RESOURCE_ARRAY; } else { this.resourcesInScope = theResources; } } /** * Obtain all the valid {@link org.eclipse.emf.ecore.resource.Resource}s that define the validation scope. If an empty * collection is returned then all resources are considered to be within the validation scope. * * @return the valid resources (never <code>null</code>) * @since 4.2 */ public Resource[] getResourcesInScope() { return this.resourcesInScope; } /** * Check if the given object is ignored during validation. * * @return boolen true if object should not be validated * @since 4.2 */ public boolean shouldIgnore( final Object object ) { if (this.objectsToIgnore != null) { return this.objectsToIgnore.contains(object); } return false; } /** * Add an object that should be ignored during validation. * * @param object * @param recursive if true add children recursively * @since 4.2 */ public void addObjectToIgnore( final EObject eObject, final boolean recursive ) { if (eObject != null) { if (this.objectsToIgnore == null) { this.objectsToIgnore = new HashSet(); } this.objectsToIgnore.add(eObject); if (recursive) { for (final Iterator iter = eObject.eContents().iterator(); iter.hasNext();) { addObjectToIgnore((EObject)iter.next(), recursive); } } } } /** * Get the container in which the {@link #getResourcesToValidate() resources} exist * * @return the container, or null if no container is available * <p> * If the {@link #getResourceContainer() resource container} is used, all of the {@link #getResourcesToValidate() * resources in the context} should exist in the same container. * </p> * @since 4.2 */ public Container getResourceContainer() { return this.resourceContainer; } /** * Set the container in which the {@link #getResourcesToValidate() resources} exist * * @param container the container, or null if there is no container * <p> * If the {@link #getResourceContainer() resource container} is used, all of the {@link #getResourcesToValidate() * resources in the context} should exist in the same container. * </p> * @since 4.2 */ public void setResourceContainer( final Container container ) { this.resourceContainer = container; } public DatatypeManager getDatatypeManager() { DatatypeManager dtMgr = null; if (this.getResourceContainer() != null) { dtMgr = this.getResourceContainer().getDatatypeManager(); } CoreArgCheck.isNotNull(dtMgr); return dtMgr; } /** * Should cache validation results for SqlTransformationMappingRoots * * @return Returns the cacheMappingRootResults. * @since 4.2 */ public boolean cacheMappingRootResults() { return this.cacheMappingRootResults; } /** * Set flag to determine if validation results for SqlTransformationMappingRoots should be cached. * * @return Returns the cacheMappingRootResults. * @since 4.2 */ public void setCacheMappingRootResults( boolean cacheMappingRootResults ) { this.cacheMappingRootResults = cacheMappingRootResults; } /** * Set the absolute path to the directory containing the index files. * * @param location The absolute path to the directory containing index files * @since 4.2 */ public void setIndexLocation( final String location ) { this.indexDirectory = location; } /** * Get the directory location for the index files * * @return The directory location for index files * @since 4.2 */ public String getIndexLocation() { return this.indexDirectory; } /** * @return Returns the useIndexesToResolve. * @since 4.2 */ public boolean useIndexesToResolve() { return this.useIndexesToResolve; } /** * @param useIndexesToResolve The useIndexesToResolve to set. * @since 4.2 */ public void setUseIndexesToResolve( boolean useIndexesToResolve ) { this.useIndexesToResolve = useIndexesToResolve; } /** * @return Returns the useServerIndexes. * @since 4.2 */ public boolean useServerIndexes() { return this.useServerIndexes; } /** * @param useServerIndexes The useServerIndexes to set. * @since 4.2 */ public void setUseServerIndexes( boolean useServerIndexes ) { this.useServerIndexes = useServerIndexes; } /** * Check if the context has any preferences on it. * * @return true if has preferences else false. * @since 4.2 */ public boolean hasPreferences() { return (this.preferenceQualifier != null); } /** * Get the value for a given preference key. * * @param key the preference key (man not be <code>null</code>) * @return the preference value (can be <code>null</code>) */ public String getPreferenceValue( final String key ) { CoreArgCheck.isNotNull(key, "key"); //$NON-NLS-1$ if (hasPreferences()) { String value = ModelerCore.getPreferences(this.preferenceQualifier).get(key, null); // if no value set get default value if (CoreStringUtil.isEmpty(value)) { value = ModelerCore.getDefaultPreferences(this.preferenceQualifier).get(key, null); } return value; } // no preferences return null; } /** * Obtains an {@link IStatus} severity for the given preference. * * @param prefKey the preference key whose status is being requested * @param defaultStatus the severity to return if the status can't be determined * @return the <code>IStatus</code> code * @since 4.2 */ public int getPreferenceStatus( final String prefKey, final int defaultStatus ) { if (hasPreferences()) { if (this.preferenceStatusMap.containsKey(prefKey)) { return this.preferenceStatusMap.get(prefKey); } String value = getPreferenceValue(prefKey); if (value != null) { if (value.equals(ValidationDescriptor.IGNORE)) { this.preferenceStatusMap.put(prefKey, IStatus.OK); return IStatus.OK; } if (value.equals(ValidationDescriptor.INFO)) { this.preferenceStatusMap.put(prefKey, IStatus.INFO); return IStatus.INFO; } if (value.equals(ValidationDescriptor.WARNING)) { this.preferenceStatusMap.put(prefKey, IStatus.WARNING); return IStatus.WARNING; } if (value.equals(ValidationDescriptor.ERROR)) { this.preferenceStatusMap.put(prefKey, IStatus.ERROR); return IStatus.ERROR; } } } // no preference value so use default status this.preferenceStatusMap.put(prefKey, new Integer(defaultStatus)); return defaultStatus; } protected String getValidationDescriptorValue( final int statusValue ) { switch (statusValue) { case IStatus.OK: { return ValidationDescriptor.IGNORE; } case IStatus.INFO: { return ValidationDescriptor.INFO; } case IStatus.WARNING: { return ValidationDescriptor.WARNING; } case IStatus.ERROR: { return ValidationDescriptor.ERROR; } default: { break; } } return CoreStringUtil.Constants.EMPTY_STRING; } /** * Clear all the results on this context */ public void clearResults() { if (validationResults != null) { validationResults.clear(); } if (targetValidationResults != null) { targetValidationResults.clear(); } } /** * Clear all the results on this context */ public void clearState() { clearResults(); if (this.rulesRun != null) { this.rulesRun.clear(); } if (this.targetTransformMap != null) { this.targetTransformMap.clear(); } this.resourcesToValidate = null; this.preferenceQualifier = null; this.preferenceStatusMap.clear(); this.resourceContainer = null; this.useServerIndexes = false; this.useIndexesToResolve = true; this.indexDirectory = IndexUtil.INDEX_PATH; this.uuidsInContext = null; this.dataMap = null; } public boolean hasResults() { if (validationResults != null && !validationResults.isEmpty()) { return true; } return false; } /** * Collect the validation results that may be used by validation rules that use this context * * @param result ValidationResult */ public void addResult( final ValidationResult result ) { CoreArgCheck.isNotNull(result); // add the result only if it has problems if (result.hasProblems()) { if (validationResults == null) { validationResults = new ArrayList(); } validationResults.add(result); // update the map of target to result updateTargetResults(result.getTarget(), result); } } public ValidationResult getLastResult() { if (validationResults == null || validationResults.isEmpty()) { return null; } return (ValidationResult)validationResults.get(validationResults.size() - 1); } /** * @return */ public List getValidationResults() { return validationResults; } /** * Check if the given rule has been run given the information about the container of eobject on which the rule is run. * * @param eObject The EObject for whose container we are checking if the rule is run * @param ruleName The name of the rule * @return true if this rule has already been run, else false. * @since 4.2 */ public boolean hasRunRule( final EObject eObject, final String ruleName ) { CoreArgCheck.isNotNull(eObject); if (rulesRun != null) { String containerInfo = getContainerInfo(eObject); return hasRunRule(containerInfo, ruleName); } return false; } /** * Check if the given rule has been run given the information about the container of eobject on which the rule is run. * * @param containerInfo Can be eObject uuid, resource uri or any other name that uniquely identifies a container. * @param ruleName The name of the rule * @return true if this rule has already been run, else false. * @since 4.2 */ public boolean hasRunRule( final String containerInfo, final String ruleName ) { CoreArgCheck.isNotNull(containerInfo); if (rulesRun != null) { Collection ruleNames = (Collection)rulesRun.get(containerInfo); if (ruleNames != null && ruleNames.contains(ruleName)) { return true; } } return false; } private String getContainerInfo( EObject eObject ) { CoreArgCheck.isNotNull(eObject); // container of the eObject EObject container = eObject.eContainer(); if (container != null) { return ModelerCore.getObjectIdString(container); } return eObject.eResource().getURI().toString(); } /** * Record rule name for a partucular container of a eObject, indicating this rule need not be run in future for this eObject. * * @param eObject The eObject for whose container this rule needs to be recorded * @param ruleName The name of the rule * @return true if this rule has already been run, else false. */ public void recordRuleRun( final EObject eObject, final String ruleName ) { CoreArgCheck.isNotNull(eObject); String containerInfo = getContainerInfo(eObject); recordRuleRun(containerInfo, ruleName); } /** * Record rule name for a partucular container of a eObject, indicating this rule need not be run in future for this eObject. * * @param containerInfo Can be eObject uuid, resource uri or any other name that uniquely identifies a container. * @param ruleName The name of the rule * @return true if this rule has already been run, else false. */ public void recordRuleRun( final String containerInfo, final String ruleName ) { CoreArgCheck.isNotNull(containerInfo); if (rulesRun == null) { rulesRun = new HashMap(); } Collection ruleNames = (Collection)rulesRun.get(containerInfo); if (ruleNames == null) { // not may rules run per container (initialize to 1) ruleNames = new HashSet(1); } ruleNames.add(ruleName); rulesRun.put(containerInfo, ruleNames); } public Map getTargetTransformMap() { return targetTransformMap; } public void addTargetTransform( final EObject target, final EObject transform ) { if (targetTransformMap == null) { targetTransformMap = new HashMap(); } if (target != null) { targetTransformMap.put(target, transform); } } /** * @return Returns the targetValidationResults. * @since 4.2 */ public Collection getTargetResults( final EObject target ) { if (this.targetValidationResults != null) { return (Collection)this.targetValidationResults.get(target); } return Collections.EMPTY_LIST; } public IStatus getTargetStatus( final EObject target ) { // construct a multi status to which we add all the validation results final MultiStatus status = new MultiStatus(ModelerCore.PLUGIN_ID, 0, "", null); //$NON-NLS-1$ final Collection results = getTargetResults(target); if (results != null && !results.isEmpty()) { for (Iterator iter = results.iterator(); iter.hasNext();) { final ValidationResult result = (ValidationResult)iter.next(); final ValidationProblem[] problems = result.getProblems(); for (int i = 0; i < problems.length; i++) { status.add(problems[i].getStatus()); } } } return status; } /** * @return Returns the targetValidationResults. * @since 4.2 */ protected void updateTargetResults( final Object target, final ValidationResult validationResult ) { if (this.targetValidationResults == null) { this.targetValidationResults = new HashMap(); } // get the current results Collection results = (Collection)this.targetValidationResults.get(target); if (results == null) { // usually one result/obj results = new HashSet(1); } results.add(validationResult); // update the map this.targetValidationResults.put(target, results); } /** * Obtains the data associated with the specified key. * * @param theKey the key whose data is being requested * @return the data or <code>null</code> if none found * @since 4.2 */ public Object getData( String theKey ) { return (this.dataMap == null) ? null : this.dataMap.get(theKey); } /** * Sets the data associated with the specified key. If the key already exists it's data is overwritten. * * @param theKey the key * @param theValue the data * @since 4.2 */ public void setData( String theKey, Object theValue ) { CoreArgCheck.isNotNull(theKey); if (this.dataMap == null) { this.dataMap = new HashMap(); } this.dataMap.put(theKey, theValue); } }