blob: d303b26259795f968853fdb1a79e337c8baec5d6 [file] [log] [blame]
/*******************************************************************************
* 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);
}
}