/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.drools.workbench.screens.dtablexls.backend.server.conversion.builders;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.drools.compiler.compiler.DrlExprParser;
import org.drools.compiler.compiler.DrlParser;
import org.drools.compiler.lang.descr.AtomicExprDescr;
import org.drools.compiler.lang.descr.BaseDescr;
import org.drools.compiler.lang.descr.ConstraintConnectiveDescr;
import org.drools.compiler.lang.descr.RelationalExprDescr;
import org.drools.workbench.models.commons.backend.rule.RuleModelDRLPersistenceImpl;
/**
* Utility class to convert XLS Decision Table parameters from their "$..."
* format to the "@{...}" format required by the Templating mechanism used with
* the BRL editors and related DRL generators. Template Keys must be unique
* across the entire model.
*/
public class ParameterUtilities {
private static final Pattern patternSingleParameter = Pattern.compile( "\\$param" );
private static final Pattern patternIndexedParameter = Pattern.compile( "\\$\\d+" );
private static final Pattern patternTemplateKey = Pattern.compile( "@\\{.+?\\}" );
private static final Pattern patternUnwrapExistingQuotes = Pattern.compile( "\"(@\\{.+?\\})\"" );
private static final String PARAMETER_PREFIX = "param";
private int parameterCounter = 1;
public String convertIndexedParametersToTemplateKeys( final String xlsTemplate,
final ParameterizedValueBuilder.Part part ) {
//Replace indexed parameter place-holders
final StringBuffer result = new StringBuffer();
final Matcher matcherIndexedParameter = patternIndexedParameter.matcher( xlsTemplate );
while ( matcherIndexedParameter.find() ) {
matcherIndexedParameter.appendReplacement( result,
"@{" + PARAMETER_PREFIX + ( parameterCounter++ ) + "}" );
}
matcherIndexedParameter.appendTail( result );
return assertTemplateKeyConversion( result,
part );
}
public String convertSingleParameterToTemplateKey( final String xlsTemplate,
final ParameterizedValueBuilder.Part part ) {
//Replace the single parameter place-holder
final StringBuffer result = new StringBuffer();
final Matcher matcherSingleParameter = patternSingleParameter.matcher( xlsTemplate );
while ( matcherSingleParameter.find() ) {
matcherSingleParameter.appendReplacement( result,
"@{" + PARAMETER_PREFIX + parameterCounter + "}" );
}
parameterCounter++;
matcherSingleParameter.appendTail( result );
return assertTemplateKeyConversion( result,
part );
}
private String assertTemplateKeyConversion( final StringBuffer result,
final ParameterizedValueBuilder.Part part ) {
if ( part == ParameterizedValueBuilder.Part.LHS ) {
final String expression = addQuotesToTemplateKeys( result ).toString();
final ViabilityVisitor visitor = new ViabilityVisitor( expression );
if ( visitor.isViable() ) {
return expression;
}
}
return result.toString();
}
public Set<String> extractTemplateKeys( final String template ) {
//Extract Template Keys
final Set<String> result = new LinkedHashSet<String>();
final Matcher matcherTemplateKey = patternTemplateKey.matcher( template );
while ( matcherTemplateKey.find() ) {
String fullKey = matcherTemplateKey.group();
result.add( fullKey.substring( 2,
fullKey.length() - 1 ) );
}
return result;
}
// Ensure LHS Template Keys are enclosed in quotes otherwise DrlParser in GuidedDecisionTablePopulater
// reports compilation errors and the XLS Column definition becomes FreeFormLine meaning all
// fields need to have a value for DRL to be generated.
private StringBuffer addQuotesToTemplateKeys( final StringBuffer source ) {
final StringBuffer unwrapped = new StringBuffer();
final Matcher unwrappedMatcher = patternUnwrapExistingQuotes.matcher( source );
while ( unwrappedMatcher.find() ) {
unwrappedMatcher.appendReplacement( unwrapped,
unwrappedMatcher.group( 1 ) );
}
unwrappedMatcher.appendTail( unwrapped );
final StringBuffer wrapped = new StringBuffer();
final Matcher wrappedMatcher = patternTemplateKey.matcher( unwrapped );
while ( wrappedMatcher.find() ) {
wrappedMatcher.appendReplacement( wrapped,
"\"" + wrappedMatcher.group( 0 ) + "\"" );
}
wrappedMatcher.appendTail( wrapped );
return wrapped;
}
// There's a legacy test that uses Template Keys as Field Names (see "GuidedDecisionTableGeneratorListenerTest.testMultipleSingleParameters")
// This is impossible to represent as anything other than a FreeFormLine. The most accurate way to identify whether a XLS Column definition
// uses Template Keys on the LHS of an expression is to have Drools parse the expression and check RelationalExprDescr's LHS.
private class ViabilityVisitor {
private final String expression;
ViabilityVisitor( final String expression ) {
this.expression = expression;
}
private boolean isViable() {
final DrlExprParser parser = new DrlExprParser( DrlParser.DEFAULT_LANGUAGE_LEVEL );
final ConstraintConnectiveDescr result = parser.parse( expression );
if ( parser.hasErrors() || result == null ) {
return false;
}
try {
for ( BaseDescr descr : result.getDescrs() ) {
visit( descr );
}
} catch ( RuleModelDRLPersistenceImpl.RuleModelUnmarshallingException e ) {
return false;
}
return true;
}
private void visit( final BaseDescr descr ) {
if ( descr instanceof RelationalExprDescr ) {
visit( ( (RelationalExprDescr) descr ).getLeft() );
} else if ( descr instanceof AtomicExprDescr ) {
visit( (AtomicExprDescr) descr );
}
}
private void visit( final AtomicExprDescr descr ) {
if ( descr.getExpression().contains( "@{" ) ) {
throw new RuleModelDRLPersistenceImpl.RuleModelUnmarshallingException();
}
}
}
}