/* * 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.metamodels.webservice.aspects.validation.rules; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.xerces.util.XMLChar; import org.eclipse.core.runtime.IStatus; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xsd.util.XSDResourceImpl; 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.resource.EmfResource; import org.teiid.designer.core.util.ModelVisitorProcessor; import org.teiid.designer.core.validation.ValidationContext; import org.teiid.designer.core.validation.ValidationProblem; import org.teiid.designer.core.validation.ValidationProblemImpl; import org.teiid.designer.core.validation.ValidationResult; import org.teiid.designer.core.validation.ValidationResultImpl; import org.teiid.designer.core.validation.rules.StringNameRule; import org.teiid.designer.core.validation.rules.StringNameValidator; import org.teiid.designer.core.workspace.ModelFileUtil; import org.teiid.designer.metamodels.core.ModelAnnotation; import org.teiid.designer.metamodels.webservice.Input; import org.teiid.designer.metamodels.webservice.Interface; import org.teiid.designer.metamodels.webservice.Message; import org.teiid.designer.metamodels.webservice.Operation; import org.teiid.designer.metamodels.webservice.Output; import org.teiid.designer.metamodels.webservice.WebServiceMetamodelPlugin; import org.teiid.designer.metamodels.webservice.WebServicePackage; import org.teiid.designer.metamodels.webservice.util.WebServiceUtil; /** * @since 8.0 */ public class WebServiceComponentNameRule extends StringNameRule { /** * @param featureID * @since 4.2 */ public WebServiceComponentNameRule( final int featureID ) { super(featureID); } /** * @see org.teiid.designer.core.validation.StructuralFeatureValidationRule#validate(org.eclipse.emf.ecore.EStructuralFeature, * org.eclipse.emf.ecore.EObject, java.lang.Object, org.teiid.designer.core.validation.ValidationContext) * @since 4.2 */ @Override public void validate( final EStructuralFeature eStructuralFeature, final EObject eObject, final Object value, final ValidationContext context ) { // check if the feature matches the given feature if (super.getFeatureID() != eStructuralFeature.getFeatureID()) { return; } // apply the default name validation checks super.validate(eStructuralFeature, eObject, value, context); // get results for the eObject final Collection results = context.getTargetResults(eObject); if (results != null && !results.isEmpty()) { return; } // validate the name final String name = (String)value; // check if this a valid xml name InvalidCharacter invalidCharInName = null; if (name != null) { // check if this a valid xml name invalidCharInName = isValidNCName(name); } final ValidationResult result = new ValidationResultImpl(eObject); if (invalidCharInName != null) { // create validation problem and addit to the results String msg = null; if (invalidCharInName.index == 0) { msg = WebServiceMetamodelPlugin.Util.getString("WebServiceComponentNameRule.NameHasInvalidFirstCharacter", invalidCharInName.character); //$NON-NLS-1$ } else if (invalidCharInName.index == 1) { msg = WebServiceMetamodelPlugin.Util.getString("WebServiceComponentNameRule.NameHasInvalidSecondCharacter", invalidCharInName.character); //$NON-NLS-1$ } else if (invalidCharInName.index == 2) { msg = WebServiceMetamodelPlugin.Util.getString("WebServiceComponentNameRule.NameHasInvalidThirdCharacter", invalidCharInName.character); //$NON-NLS-1$ } else { final Object[] params = new Object[] {new Integer(invalidCharInName.index + 1), new Character(invalidCharInName.character)}; msg = WebServiceMetamodelPlugin.Util.getString("WebServiceComponentNameRule.NameHasInvalidNthCharacter", params); //$NON-NLS-1$ } final ValidationProblem problem = new ValidationProblemImpl(0, IStatus.ERROR, msg); result.addProblem(problem); return; } // collect all the siblings whose names need to be checked aginst the name of the // current final List siblings = getSiblingsForUniquenessCheck(eObject, context); if (siblings.isEmpty()) { return; } // validate the uniqueness of the name among the siblings validateUniqueness(context, siblings, eStructuralFeature.getFeatureID()); return; } class InvalidCharacter { public final char character; public final int index; public InvalidCharacter( final char character, final int index ) { this.character = character; this.index = index; } } /** * Check to see if a string is a valid NCName according to [4] from the XML Namespaces 1.0 Recommendation * * @param name string to check * @return the first invalid character that was found, or null if all the characters were valid * @see XMLChar#isValidNCName(java.lang.String) */ protected InvalidCharacter isValidNCName( final String ncName ) { if (ncName.length() == 0) { return null; } char ch = ncName.charAt(0); if (XMLChar.isNCNameStart(ch) == false) { return new InvalidCharacter(ch, 0); } for (int i = 1; i < ncName.length(); i++) { ch = ncName.charAt(i); if (XMLChar.isNCName(ch) == false) { return new InvalidCharacter(ch, i); } } return null; } /** * Uniqueness check is handled in this rule rather than use the existing framework. * * @see org.teiid.designer.core.validation.rules.StringNameRule#validateUniqueness() * @since 4.2 */ @Override protected boolean validateUniqueness() { return false; } /** * @see org.teiid.designer.core.validation.rules.StringNameRule#getSiblingsForUniquenessCheck(org.eclipse.emf.ecore.EObject) * @since 4.2 */ protected List getSiblingsForUniquenessCheck( final EObject eObject, final ValidationContext context ) { final String objType = eObject.eClass().getName(); final String ruleName = getRuleName() + objType; if (eObject instanceof Interface) { // Find all Interface instances in all of the models in the validation context, // but only run this rule once for Interfaces ... Container container = context.getResourceContainer(); boolean modelContainer = ModelerCore.isModelContainer(container); String containerInfo = (modelContainer) ? eObject.eResource().getURI().toString() : Interface.class.getName(); if (!context.hasRunRule(containerInfo, ruleName)) { List resources = new ArrayList(); if (modelContainer) { resources.add(eObject.eResource()); } else { List containerResources = container.getResources(); for (int size = containerResources.size(), i = 0; i < size; ++i) { Resource resource = (Resource)containerResources.get(i); if (isWebServiceModel(resource)) { resources.add(resource); } } } context.recordRuleRun(containerInfo, ruleName); // Find all of the interfaces in all the models in the context final List results = WebServiceUtil.findInterfaces(resources, ModelVisitorProcessor.DEPTH_ONE); return results; } } else if (eObject instanceof Operation) { // type of object this rule is being run on. // this rule is being run once per object type per parent if (!context.hasRunRule(eObject, ruleName)) { context.recordRuleRun(eObject, ruleName); // Operation names must be unique within an Interface (per WS-I). final Interface parent = ((Operation)eObject).getInterface(); final List results = WebServiceUtil.findOperations(parent, ModelVisitorProcessor.DEPTH_INFINITE); return results; } } else if (eObject instanceof Input) { // type of object this rule is being run on. // this rule is being run once per object type per parent if (!context.hasRunRule(eObject, ruleName)) { context.recordRuleRun(eObject, ruleName); // output name should be unique among the messages // of the same operation Object parent = eObject.eContainer(); return WebServiceUtil.findMessages(parent); } } else if (eObject instanceof Output) { // type of object this rule is being run on. // this rule is being run once per object type per parent if (!context.hasRunRule(eObject, ruleName)) { context.recordRuleRun(eObject, ruleName); // operation name should be unique among the operations // of the same operation Object parent = eObject.eContainer(); return WebServiceUtil.findMessages(parent); } } return Collections.EMPTY_LIST; } /** * Indicates if the specified <code>Resource</code> is a web service model. * * @param theResource the resource being checked * @return <code>true</code>if a web service model; <code>false</code> otherwise. * @since 4.2 */ private boolean isWebServiceModel( Resource theResource ) { boolean result = false; String metamodelUri = null; // get the metamodel URI from the loaded resource if (theResource instanceof EmfResource) { ModelAnnotation annotation = ((EmfResource)theResource).getModelAnnotation(); if (annotation != null) { metamodelUri = annotation.getPrimaryMetamodelUri(); } } else if (theResource instanceof XSDResourceImpl) { return false; } // if necessary get metamodel URI by reading the header if (metamodelUri == null) { URI uri = theResource.getURI(); if (uri != null) { File file = new File(uri.toFileString()); if (file.exists()) { final XMIHeader header = ModelFileUtil.getXmiHeader(file); if (header != null) { metamodelUri = header.getPrimaryMetamodelURI(); } } } } if (metamodelUri != null && WebServicePackage.eNS_URI.equals(metamodelUri)) result = true; return result; } /** * Recursively check the names of the siblings against each other names and create validation errors if we find siblings with * same case insensitive name. * * @param context The validation context to which we add results * @param siblings The siblings EObjects * @param nameFeatureID the ID of the feature that represents the name feature */ public void validateUniqueness( final ValidationContext context, final List siblings, final int nameFeatureID ) { final StringNameValidator validator = new StringNameValidator(); Map objectCountMap = validator.getDuplicateNamesMap(siblings, nameFeatureID); // if there is at least one match, create a problem if (objectCountMap.size() > 0) { Iterator keyIter = objectCountMap.keySet().iterator(); while (keyIter.hasNext()) { EObject eObject = (EObject)keyIter.next(); final EStructuralFeature eFeature = eObject.eClass().getEStructuralFeature(nameFeatureID); final String name = (String)eObject.eGet(eFeature); final Integer count = (Integer)objectCountMap.get(eObject); final ValidationResult result = new ValidationResultImpl(eObject); final String message = getUniquenessMessage(context, eObject, name, count); if (message != null) { final ValidationProblem problem = new ValidationProblemImpl(0, IStatus.ERROR, message); result.addProblem(problem); context.addResult(result); } } } } protected String getUniquenessMessage( final ValidationContext context, final EObject eObject, final String name, final Integer count ) { // Determine the number of models being validated in this context ... final int numModels = context.getResourcesToValidate().length; // create validation problem and addit to the results String msg = null; if (eObject instanceof Interface) { // interface name should be unique among the interfaces // of in the context of a given set of the same webservice metamodels if (numModels > 1) { msg = WebServiceMetamodelPlugin.Util.getString("WebServiceComponentNameRule.InterfaceNameMustBeUniqueInVdb", name, count); //$NON-NLS-1$ } else { msg = WebServiceMetamodelPlugin.Util.getString("WebServiceComponentNameRule.InterfaceNameMustBeUniqueInModel", name, count); //$NON-NLS-1$ } } else if (eObject instanceof Operation) { // operation name should be unique among the operations // of in the context of a given set of the same webservice metamodels if (numModels > 1) { msg = WebServiceMetamodelPlugin.Util.getString("WebServiceComponentNameRule.OperationNameMustBeUniqueInVdb", name, count); //$NON-NLS-1$ } else { msg = WebServiceMetamodelPlugin.Util.getString("WebServiceComponentNameRule.OperationNameMustBeUniqueInModel", name, count); //$NON-NLS-1$ } } else if (eObject instanceof Message) { // output name should be unique among the messages // of the same operation msg = WebServiceMetamodelPlugin.Util.getString("WebServiceComponentNameRule.InputAndOutputNamesMustBeUnique", name, count); //$NON-NLS-1$ } return msg; } }