use of com.helger.schematron.pure.model.PSRule in project ph-schematron by phax.
the class PSPreprocessor method _resolveRuleContent.
/**
* Resolve all <extends> elements. This method calls itself recursively
* until all extends elements are resolved.
*
* @param aRuleContent
* A list consisting of {@link PSAssertReport} and {@link PSExtends}
* objects. Never <code>null</code>.
* @param aLookup
* The rule lookup object
* @throws SchematronPreprocessException
* If the base rule of an extends object could not be resolved.
*/
private void _resolveRuleContent(@Nonnull final ICommonsList<IPSElement> aRuleContent, @Nonnull final PreprocessorLookup aLookup, @Nonnull final PreprocessorIDPool aIDPool, @Nullable final ICommonsMap<String, String> aParamValueMap, @Nonnull final PSRule aTargetRule) throws SchematronPreprocessException {
for (final IPSElement aElement : aRuleContent) {
if (aElement instanceof PSAssertReport) {
final PSAssertReport aAssertReport = (PSAssertReport) aElement;
aTargetRule.addAssertReport(_getPreprocessedAssert(aAssertReport, aIDPool, aParamValueMap));
} else {
final PSExtends aExtends = (PSExtends) aElement;
final String sRuleID = aExtends.getRule();
final PSRule aBaseRule = aLookup.getAbstractRuleOfID(sRuleID);
if (aBaseRule == null)
throw new SchematronPreprocessException("Failed to resolve rule ID '" + sRuleID + "' in extends statement. Available rules are: " + aLookup.getAllAbstractRuleIDs());
// Recursively resolve the extends of the base rule
_resolveRuleContent(aBaseRule.getAllContentElements(), aLookup, aIDPool, aParamValueMap, aTargetRule);
// Copy all lets
for (final PSLet aBaseLet : aBaseRule.getAllLets()) aTargetRule.addLet(aBaseLet.getClone());
}
}
}
use of com.helger.schematron.pure.model.PSRule in project ph-schematron by phax.
the class PSPreprocessor method _getPreprocessedPattern.
@Nullable
private PSPattern _getPreprocessedPattern(@Nonnull final PSPattern aPattern, @Nonnull final PreprocessorLookup aLookup, @Nonnull final PreprocessorIDPool aIDPool) throws SchematronPreprocessException {
if (aPattern.isAbstract()) {
// Will be inlined
return null;
}
final PSPattern ret = new PSPattern();
// abstract always false
// is-a must be resolved
ret.setID(aIDPool.getUniqueID(aPattern.getID()));
ret.setRich(aPattern.getRichClone());
if (aPattern.hasAnyInclude())
throw new SchematronPreprocessException("Cannot preprocess <pattern> with an <include>");
if (m_bKeepTitles && aPattern.hasTitle())
ret.setTitle(aPattern.getTitle().getClone());
final String sIsA = aPattern.getIsA();
if (sIsA != null) {
final PSPattern aBasePattern = aLookup.getAbstractPatternOfID(sIsA);
if (aBasePattern == null)
throw new SchematronPreprocessException("Failed to resolve the pattern denoted by is-a='" + sIsA + "'");
if (!ret.hasID())
ret.setID(aIDPool.getUniqueID(aBasePattern.getID()));
if (!ret.hasRich())
ret.setRich(aBasePattern.getRichClone());
// get the string replacements
final ICommonsNavigableMap<String, String> aParamValueMap = m_aQueryBinding.getStringReplacementMap(aPattern.getAllParams());
for (final IPSElement aElement : aBasePattern.getAllContentElements()) {
if (aElement instanceof PSLet)
ret.addLet(((PSLet) aElement).getClone());
else if (aElement instanceof PSRule) {
final PSRule aMinifiedRule = _getPreprocessedRule((PSRule) aElement, aLookup, aIDPool, aParamValueMap);
if (aMinifiedRule != null)
ret.addRule(aMinifiedRule);
}
// params must have be resolved
// ps are ignored
}
} else {
for (final IPSElement aElement : aPattern.getAllContentElements()) {
if (aElement instanceof PSLet)
ret.addLet(((PSLet) aElement).getClone());
else if (aElement instanceof PSRule) {
final PSRule aMinifiedRule = _getPreprocessedRule((PSRule) aElement, aLookup, aIDPool, null);
if (aMinifiedRule != null)
ret.addRule(aMinifiedRule);
}
// params must be resolved
// ps are ignored
}
}
ret.addForeignElements(aPattern.getAllForeignElements());
ret.addForeignAttributes(aPattern.getAllForeignAttributes());
return ret;
}
use of com.helger.schematron.pure.model.PSRule in project ph-schematron by phax.
the class PSXPathBoundSchema method _createBoundPatterns.
/**
* Pre-compile all patterns incl. their content
*
* @param aXPathContext
* @param aXPathContext
* Global XPath object to use. May not be <code>null</code>.
* @param aBoundDiagnostics
* A map from DiagnosticID to its mapped counterpart. May not be
* <code>null</code>.
* @param aGlobalVariables
* The global Schematron-let variables. May not be <code>null</code>.
* @return <code>null</code> if an XPath error is contained
*/
@Nullable
private ICommonsList<PSXPathBoundPattern> _createBoundPatterns(@Nonnull final XPath aXPathContext, @Nonnull final ICommonsMap<String, PSXPathBoundDiagnostic> aBoundDiagnostics, @Nonnull final IPSXPathVariables aGlobalVariables) {
final ICommonsList<PSXPathBoundPattern> ret = new CommonsArrayList<>();
boolean bHasAnyError = false;
// For all relevant patterns
for (final PSPattern aPattern : getAllRelevantPatterns()) {
// Handle pattern specific variables
final PSXPathVariables aPatternVariables = aGlobalVariables.getClone();
if (aPattern.hasAnyLet()) {
// map
for (final Map.Entry<String, String> aEntry : aPattern.getAllLetsAsMap().entrySet()) if (aPatternVariables.add(aEntry).isUnchanged())
error(aPattern, "Duplicate <let> with name '" + aEntry.getKey() + "' in <pattern>");
}
// For all rules of the current pattern
final ICommonsList<PSXPathBoundRule> aBoundRules = new CommonsArrayList<>();
for (final PSRule aRule : aPattern.getAllRules()) {
// Handle rule specific variables
final PSXPathVariables aRuleVariables = aPatternVariables.getClone();
if (aRule.hasAnyLet()) {
// variable map
for (final Map.Entry<String, String> aEntry : aRule.getAllLetsAsMap().entrySet()) if (aRuleVariables.add(aEntry).isUnchanged())
error(aRule, "Duplicate <let> with name '" + aEntry.getKey() + "' in <rule>");
}
// For all contained assert and reports within the current rule
final ICommonsList<PSXPathBoundAssertReport> aBoundAssertReports = new CommonsArrayList<>();
for (final PSAssertReport aAssertReport : aRule.getAllAssertReports()) {
final String sTest = aRuleVariables.getAppliedReplacement(aAssertReport.getTest());
try {
final XPathExpression aTestExpr = _compileXPath(aXPathContext, sTest);
final ICommonsList<PSXPathBoundElement> aBoundElements = _createBoundElements(aAssertReport, aXPathContext, aRuleVariables);
if (aBoundElements == null) {
// Error already emitted
bHasAnyError = true;
} else {
final PSXPathBoundAssertReport aBoundAssertReport = new PSXPathBoundAssertReport(aAssertReport, sTest, aTestExpr, aBoundElements, aBoundDiagnostics);
aBoundAssertReports.add(aBoundAssertReport);
}
} catch (final Throwable t) {
error(aAssertReport, "Failed to compile XPath expression in <" + (aAssertReport.isAssert() ? "assert" : "report") + ">: '" + sTest + "' with the following variables: " + aRuleVariables.getAll(), t);
bHasAnyError = true;
}
}
// Evaluate base node set for this rule
final String sRuleContext = aGlobalVariables.getAppliedReplacement(getValidationContext(aRule.getContext()));
PSXPathBoundRule aBoundRule = null;
try {
final XPathExpression aRuleContext = _compileXPath(aXPathContext, sRuleContext);
aBoundRule = new PSXPathBoundRule(aRule, sRuleContext, aRuleContext, aBoundAssertReports);
aBoundRules.add(aBoundRule);
} catch (final XPathExpressionException ex) {
error(aRule, "Failed to compile XPath expression in <rule>: '" + sRuleContext + "'", ex.getCause() != null ? ex.getCause() : ex);
bHasAnyError = true;
}
}
// Create the bound pattern
final PSXPathBoundPattern aBoundPattern = new PSXPathBoundPattern(aPattern, aBoundRules);
ret.add(aBoundPattern);
}
if (bHasAnyError)
return null;
return ret;
}
use of com.helger.schematron.pure.model.PSRule in project ph-schematron by phax.
the class PSPreprocessor method _getPreprocessedRule.
@Nullable
private PSRule _getPreprocessedRule(@Nonnull final PSRule aRule, @Nonnull final PreprocessorLookup aLookup, @Nonnull final PreprocessorIDPool aIDPool, @Nullable final ICommonsMap<String, String> aParamValueMap) throws SchematronPreprocessException {
if (aRule.isAbstract()) {
// Will be inlined
return null;
}
final PSRule ret = new PSRule();
ret.setFlag(aRule.getFlag());
ret.setRich(aRule.getRichClone());
ret.setLinkable(aRule.getLinkableClone());
// abstract is always false
ret.setContext(m_aQueryBinding.getWithParamTextsReplaced(aRule.getContext(), aParamValueMap));
ret.setID(aIDPool.getUniqueID(aRule.getID()));
if (aRule.hasAnyInclude())
throw new SchematronPreprocessException("Cannot preprocess <rule> with an <include>");
for (final PSLet aLet : aRule.getAllLets()) ret.addLet(aLet.getClone());
_resolveRuleContent(aRule.getAllContentElements(), aLookup, aIDPool, aParamValueMap, ret);
ret.addForeignElements(aRule.getAllForeignElements());
ret.addForeignAttributes(aRule.getAllForeignAttributes());
return ret;
}
use of com.helger.schematron.pure.model.PSRule in project ph-schematron by phax.
the class PSXPathBoundSchema method validate.
public void validate(@Nonnull final Node aNode, @Nullable final String sBaseURI, @Nonnull final IPSValidationHandler aValidationHandler) throws SchematronValidationException {
ValueEnforcer.notNull(aNode, "Node");
ValueEnforcer.notNull(aValidationHandler, "ValidationHandler");
if (m_aBoundPatterns == null)
throw new IllegalStateException("bind was never called!");
final PSSchema aSchema = getOriginalSchema();
final PSPhase aPhase = getPhase();
// Call the "start" callback method
aValidationHandler.onStart(aSchema, aPhase, sBaseURI);
// For all bound patterns
for (final PSXPathBoundPattern aBoundPattern : m_aBoundPatterns) {
final PSPattern aPattern = aBoundPattern.getPattern();
aValidationHandler.onPattern(aPattern);
// For all bound rules
rules: for (final PSXPathBoundRule aBoundRule : aBoundPattern.getAllBoundRules()) {
final PSRule aRule = aBoundRule.getRule();
// Find all nodes matching the rules
NodeList aRuleMatchingNodes = null;
try {
aRuleMatchingNodes = XPathEvaluationHelper.evaluate(aBoundRule.getBoundRuleExpression(), aNode, XPathConstants.NODESET, sBaseURI);
} catch (final XPathExpressionException ex) {
// Handle the cause, because it is usually a wrapper only
error(aRule, "Failed to evaluate XPath expression to a nodeset: '" + aBoundRule.getRuleExpression() + "'", ex.getCause() != null ? ex.getCause() : ex);
continue rules;
}
final int nRuleMatchingNodes = aRuleMatchingNodes.getLength();
if (nRuleMatchingNodes > 0) {
// For all contained assert and report elements
for (final PSXPathBoundAssertReport aBoundAssertReport : aBoundRule.getAllBoundAssertReports()) {
// XSLT does "fired-rule" for each node
aValidationHandler.onRule(aRule, aBoundRule.getRuleExpression());
final PSAssertReport aAssertReport = aBoundAssertReport.getAssertReport();
final boolean bIsAssert = aAssertReport.isAssert();
final XPathExpression aTestExpression = aBoundAssertReport.getBoundTestExpression();
// Check each node, if it matches the assert/report
for (int i = 0; i < nRuleMatchingNodes; ++i) {
final Node aRuleMatchingNode = aRuleMatchingNodes.item(i);
try {
final boolean bTestResult = ((Boolean) XPathEvaluationHelper.evaluate(aTestExpression, aRuleMatchingNode, XPathConstants.BOOLEAN, sBaseURI)).booleanValue();
if (bIsAssert) {
// It's an assert
if (!bTestResult) {
// Assert failed
if (aValidationHandler.onFailedAssert(aAssertReport, aBoundAssertReport.getTestExpression(), aRuleMatchingNode, i, aBoundAssertReport).isBreak()) {
return;
}
}
} else {
// It's a report
if (bTestResult) {
// Successful report
if (aValidationHandler.onSuccessfulReport(aAssertReport, aBoundAssertReport.getTestExpression(), aRuleMatchingNode, i, aBoundAssertReport).isBreak()) {
return;
}
}
}
} catch (final XPathExpressionException ex) {
error(aRule, "Failed to evaluate XPath expression to a boolean: '" + aBoundAssertReport.getTestExpression() + "'", ex.getCause() != null ? ex.getCause() : ex);
}
}
}
if (false) {
// the next pattern
break rules;
}
}
}
}
// Call the "end" callback method
aValidationHandler.onEnd(aSchema, aPhase);
}
Aggregations