| /******************************************************************************* |
| * Copyright (c) 2001, 2008 Oracle Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Oracle Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jst.jsf.validation.internal.strategy; |
| |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.jst.jsf.common.dom.DOMAdapter; |
| import org.eclipse.jst.jsf.common.dom.TagIdentifier; |
| import org.eclipse.jst.jsf.common.internal.JSPUtil; |
| import org.eclipse.jst.jsf.common.metadata.Entity; |
| import org.eclipse.jst.jsf.common.metadata.Trait; |
| import org.eclipse.jst.jsf.common.metadata.internal.IMetaDataDomainContext; |
| import org.eclipse.jst.jsf.common.metadata.query.internal.MetaDataQueryContextFactory; |
| import org.eclipse.jst.jsf.common.metadata.query.internal.MetaDataQueryFactory; |
| import org.eclipse.jst.jsf.common.metadata.query.internal.taglib.ITaglibDomainMetaDataQuery; |
| import org.eclipse.jst.jsf.common.sets.AxiomaticSet; |
| import org.eclipse.jst.jsf.common.sets.ConcreteAxiomaticSet; |
| import org.eclipse.jst.jsf.context.resolver.structureddocument.IDOMContextResolver; |
| import org.eclipse.jst.jsf.context.resolver.structureddocument.IStructuredDocumentContextResolverFactory; |
| import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContext; |
| import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; |
| import org.eclipse.jst.jsf.core.internal.region.Region2ElementAdapter; |
| import org.eclipse.jst.jsf.core.internal.tld.IJSFConstants; |
| import org.eclipse.jst.jsf.core.internal.tld.TagIdentifierFactory; |
| import org.eclipse.jst.jsf.core.set.constraint.MemberConstraint; |
| import org.eclipse.jst.jsf.core.set.mapping.ElementToTagIdentifierMapping; |
| import org.eclipse.jst.jsf.core.tagmatcher.EvaluationException; |
| import org.eclipse.jst.jsf.core.tagmatcher.InvalidExpressionException; |
| import org.eclipse.jst.jsf.core.tagmatcher.XPathMatchingAlgorithm; |
| import org.eclipse.jst.jsf.validation.internal.AbstractXMLViewValidationStrategy; |
| import org.eclipse.jst.jsf.validation.internal.JSFValidationContext; |
| import org.eclipse.jst.jsf.validation.internal.constraints.ContainsTagConstraint; |
| import org.eclipse.jst.jsf.validation.internal.constraints.TagId; |
| import org.eclipse.jst.jsf.validation.internal.constraints.TagSet; |
| import org.w3c.dom.Node; |
| |
| /** |
| * @author cbateman |
| * |
| */ |
| public class ContainmentValidatingStrategy extends |
| AbstractXMLViewValidationStrategy |
| { |
| /** |
| * identifier |
| */ |
| public final static String ID = |
| "org.eclipse.jst.jsf.validation.strategy.ElementValidatingStrategy"; //$NON-NLS-1$ |
| private final static String DISPLAY_NAME = |
| Messages.ContainmentValidatingStrategy_DisplayName; |
| private final static ElementToTagIdentifierMapping elem2TagIdMapper = |
| new ElementToTagIdentifierMapping(); |
| |
| private static final String ENABLE_CONTAINMENT_VALIDATION_KEY = "jsfCoreEnableContainmentValidation"; //$NON-NLS-1$ |
| |
| private int _containmentValidationCount; // = 0; |
| private final JSFValidationContext _jsfValidationContext; |
| private boolean _enabled; |
| |
| /** |
| * @param jsfValidationContext |
| */ |
| public ContainmentValidatingStrategy( |
| final JSFValidationContext jsfValidationContext) |
| { |
| super(ID, DISPLAY_NAME); |
| _jsfValidationContext = jsfValidationContext; |
| _enabled = isEnabled(); |
| } |
| |
| @Override |
| public boolean isInteresting(DOMAdapter domAdapter) |
| { |
| return domAdapter instanceof Region2ElementAdapter; |
| } |
| |
| @Override |
| public void validate(DOMAdapter domAdapter) |
| { |
| if (_enabled |
| && domAdapter instanceof Region2ElementAdapter) |
| { |
| final Region2ElementAdapter elementAdapter = |
| (Region2ElementAdapter) domAdapter; |
| validateContainment(elementAdapter, _jsfValidationContext); |
| } |
| } |
| |
| /** |
| * @return true if the containment validation strategy is enabled |
| */ |
| public static boolean isEnabled() { |
| String res = System.getProperty(ENABLE_CONTAINMENT_VALIDATION_KEY); |
| if (res == null) { |
| //check env var also |
| res = System.getenv(ENABLE_CONTAINMENT_VALIDATION_KEY); |
| } |
| return res != null; |
| } |
| |
| private void validateContainment( |
| final Region2ElementAdapter elementAdapter, |
| final JSFValidationContext jsfValidationContext) |
| { |
| // don't validate JSP fragments since the necessary containment may |
| // existing |
| // in the JSP files that include them |
| // also only validate the first instance of containment violation in a |
| // file |
| if (JSPUtil.isJSPFragment(jsfValidationContext.getFile()) |
| || _containmentValidationCount > 0) |
| { |
| return; |
| } |
| |
| final IStructuredDocumentContext context = elementAdapter |
| .getDocumentContext(); |
| final IDOMContextResolver resolver = IStructuredDocumentContextResolverFactory.INSTANCE |
| .getDOMContextResolver(context); |
| final Node node = resolver.getNode(); |
| |
| final String uri = elementAdapter.getNamespace(); |
| final String tagName = elementAdapter.getLocalName(); |
| // final Element node = elementAdapter. |
| |
| final IMetaDataDomainContext mdcontext = MetaDataQueryContextFactory.getInstance().createTaglibDomainModelContext(jsfValidationContext.getFile()); |
| final ITaglibDomainMetaDataQuery query = MetaDataQueryFactory.getInstance().createQuery(mdcontext); |
| // final ITaglibDomainMetaDataModelContext modelContext = TaglibDomainMetaDataQueryHelper |
| // .createMetaDataModelContext(jsfValidationContext.getFile() |
| // .getProject(), uri); |
| final Entity entity = query.getQueryHelper().getEntity( |
| uri, tagName); |
| if (entity != null) |
| { |
| final Trait trait = query.findTrait( |
| entity, "containment-constraint"); //$NON-NLS-1$ |
| |
| if (trait != null) |
| { |
| final ContainsTagConstraint tagConstraint = (ContainsTagConstraint) trait |
| .getValue(); |
| |
| final String algorithm = tagConstraint.getSetGenerator() |
| .getAlgorithm(); |
| |
| // TODO: need generalized factory mechanism for registering and |
| // constructing algorithms. |
| if (!"xpath".equals(algorithm)) //$NON-NLS-1$ |
| { |
| return; |
| } |
| |
| final String expr = tagConstraint.getSetGenerator() |
| .getExpression(); |
| |
| // TODO: optimize on the expression and cache for reuse |
| final XPathMatchingAlgorithm xpathAlg = new XPathMatchingAlgorithm( |
| expr); |
| |
| AxiomaticSet set = null; |
| |
| try |
| { |
| set = xpathAlg.evaluate(node); |
| // map dom nodes to tag identifiers |
| set = elem2TagIdMapper.map(set); |
| } |
| catch (final InvalidExpressionException e) |
| { |
| JSFCorePlugin.log(e, "Problem with expression: " + expr //$NON-NLS-1$ |
| + " on node " + node); //$NON-NLS-1$ |
| return; |
| } |
| catch (final EvaluationException e) |
| { |
| JSFCorePlugin.log(e, "Problem evaluating expression: " //$NON-NLS-1$ |
| + expr + " on node " + node); //$NON-NLS-1$ |
| return; |
| } |
| |
| final TagSet constraintData = tagConstraint.getSatisfiesSet(); |
| final AxiomaticSet constraintSet = new ConcreteAxiomaticSet(); |
| for (final Iterator it = constraintData.getTags().iterator(); it |
| .hasNext();) |
| { |
| final TagId tagId = (TagId) it.next(); |
| constraintSet.add(TagIdentifierFactory.createJSPTagWrapper( |
| tagId.getUri(), tagId.getName())); |
| } |
| final MemberConstraint memberConstraint = new MemberConstraint( |
| constraintSet); |
| final Diagnostic diag = memberConstraint.isSatisfied(set); |
| |
| if (diag.getSeverity() != Diagnostic.OK) |
| { |
| _containmentValidationCount++; // found a violation |
| |
| final List data = diag.getData(); |
| |
| for (final Iterator it = data.iterator(); it.hasNext();) |
| { |
| final TagIdentifier missingParent = (TagIdentifier) it |
| .next(); |
| |
| reportContainmentProblem(context, node, missingParent); |
| } |
| } |
| } |
| } |
| } |
| |
| private void reportContainmentProblem( |
| final IStructuredDocumentContext context, |
| final Node node, |
| final TagIdentifier missingParent) |
| { |
| |
| Diagnostic diagnostic = null; |
| if (missingParent.equals(IJSFConstants.TAG_IDENTIFIER_VIEW)) { |
| diagnostic = DiagnosticFactory.create_CONTAINMENT_ERROR_MISSING_VIEW(node.getNodeName()); |
| } |
| else if (missingParent.equals(IJSFConstants.TAG_IDENTIFIER_FORM)) { |
| diagnostic = DiagnosticFactory.create_CONTAINMENT_ERROR_MISSING_FORM(node.getNodeName()); |
| } |
| else { |
| diagnostic = DiagnosticFactory.create_CONTAINMENT_ERROR_MISSING_ANCESTOR(node.getNodeName(), missingParent); |
| } |
| |
| // add one so that the start offset is at the node name, rather |
| // than the opening brace. |
| final int start = context.getDocumentPosition()+1; |
| final int length = node.getNodeName().length(); |
| |
| _jsfValidationContext.getReporter().report(diagnostic, start, length); |
| } |
| |
| } |