/**
* Copyright 2010 JBoss Inc
*
* 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.guvnor.server.util;
/*
* Copyright 2005 JBoss Inc
*
* 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.
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.drools.compiler.DrlParser;
import org.drools.compiler.DroolsParserException;
import org.drools.guvnor.client.common.AssetFormats;
import org.drools.lang.descr.RuleDescr;
/**
* This class imports legacy DRL into a structure suitable for storing more
* normalised in the repository.
*
* @author Michael Neale
*/
public class ClassicDRLImporter {
private String source;
private String packageName;
private List<Asset> assets = new ArrayList<Asset>();
private StringBuffer header;
private boolean usesDSL;
private static Pattern functionPattern = Pattern.compile( "function\\s+.*\\s+(.*)\\(.*\\).*" );
public ClassicDRLImporter(InputStream in) throws IOException,
DroolsParserException {
String line = "";
StringBuffer drl = new StringBuffer();
BufferedReader reader = new BufferedReader( new InputStreamReader( in ) );
while ( (line = reader.readLine()) != null ) {
drl.append( "\n" + line );
}
this.source = drl.toString();
parse();
}
private void parse() throws DroolsParserException {
StringTokenizer lines = new StringTokenizer( source,
"\r\n" );
header = new StringBuffer();
while ( lines.hasMoreTokens() ) {
String line = lines.nextToken().trim();
if ( line.startsWith( "package" ) ) {
packageName = getPackage( line );
} else if ( line.startsWith( "rule" ) ) {
String ruleName = getRuleName( line );
StringBuffer currentRule = new StringBuffer();
laConsumeToEnd( lines,
currentRule,
"end",
false );
addRule( ruleName,
currentRule );
} else if ( line.startsWith( "function" ) ) {
String functionName = getFuncName( line );
StringBuffer currentFunc = new StringBuffer();
int counter = 0;
currentFunc.append( line + "\n" );
counter = countBrackets( counter,
line );
if ( counter > 0 ) {
laConsumeBracketsToEnd( counter,
lines,
currentFunc );
}
addFunction( functionName,
currentFunc );
} else if ( line.startsWith( "/*" ) ) {
StringBuffer comment = new StringBuffer();
comment.append( line + "\n");
laConsumeToEnd( lines,
comment,
"*/",
true );
header.append( comment );
} else if ( line.startsWith( "expander" ) ) {
usesDSL = true;
} else {
header.append( line );
header.append( "\n" );
}
}
}
private void addFunction(String functionName,
StringBuffer currentFunc) {
this.assets.add( new Asset( functionName,
currentFunc.toString(),
AssetFormats.FUNCTION ) );
}
private String getFuncName(String line) {
Matcher m = functionPattern.matcher( line );
m.matches();
return m.group( 1 );
}
/**
* Consumes function to the ending curly bracket.
*
* @param lines
* @param currentFunc
*/
private void laConsumeBracketsToEnd(int counter,
StringTokenizer lines,
StringBuffer currentFunc) {
/*
* Check if the first line contains matching amount of brackets.
*/
boolean multilineIsOpen = false;
// Start counting brackets
while ( lines.hasMoreTokens() ) {
String line = lines.nextToken();
currentFunc.append( line );
currentFunc.append( "\n" );
if ( multilineIsOpen ) {
int commentEnd = line.indexOf( "*/" );
if ( commentEnd != -1 ) {
multilineIsOpen = false;
line = line.substring( commentEnd );
}
} else {
multilineIsOpen = checkIfMultilineCommentStarts( line );
line = removeComments( line );
}
if ( !multilineIsOpen ) {
counter = countBrackets( counter,
line );
}
if ( counter == 0 ) {
break;
}
}
}
/**
* @param line
* @return
*/
private boolean checkIfMultilineCommentStarts(String line) {
int commentMultiLineStart = line.indexOf( "/*" );
int commentMultiLineEnd = line.indexOf( "*/" );
// int commentSingleLine = line.indexOf( "//" );
if ( commentMultiLineStart != -1 && commentMultiLineEnd == -1 ) {
return true;
} else {
return false;
}
}
private int countBrackets(int counter,
String line) {
char[] chars = line.toCharArray();
for ( int i = 0; i < chars.length; i++ ) {
if ( chars[i] == '{' ) {
counter++;
} else if ( chars[i] == '}' ) {
counter--;
}
}
return counter;
}
private String removeComments(String line) {
int commentMultiLineStart = line.indexOf( "/*" );
int commentMultiLineEnd = line.indexOf( "*/" );
int commentSingleLine = line.indexOf( "//" );
// Single line comment is first
// Case: some code // /* */
// Another case: some code // No comments
if ( commentSingleLine != -1 && commentMultiLineStart > commentSingleLine ) {
return line.substring( 0,
commentSingleLine );
}
// There is only a start for the multiline comment.
// Case: some code here /* commented out
if ( commentMultiLineStart != -1 && commentMultiLineEnd == -1 ) {
return line.substring( 0,
commentMultiLineStart );
}
// Two ends are on the same line
// some code /* comment */
if ( commentMultiLineStart != -1 && commentMultiLineEnd != -1 ) {
line = line.substring( commentMultiLineEnd );
line = line.substring( 0,
commentMultiLineStart );
return line;
}
return line;
}
private void laConsumeToEnd(StringTokenizer lines,
StringBuffer currentRule,
String end, boolean addLastLine) {
String line;
while ( lines.hasMoreTokens() ) {
line = lines.nextToken();
if ( line.trim().startsWith( end ) ) {
if ( addLastLine ) {
currentRule.append( line + "\n" );
}
break;
}
currentRule.append( line );
currentRule.append( "\n" );
}
}
private void addRule(String ruleName,
StringBuffer currentRule) {
ruleName = ruleName.replace('\'', ' ');
if ( this.isDSLEnabled() ) {
this.assets.add( new Asset( ruleName,
currentRule.toString(),
AssetFormats.DSL_TEMPLATE_RULE ) );
} else {
this.assets.add( new Asset( ruleName,
currentRule.toString(),
AssetFormats.DRL ) );
}
}
/** Get the rule name from a declaration line */
public static String getRuleName(String line) throws DroolsParserException {
DrlParser parser = new DrlParser();
line = line + "\n when\n then \n end";
RuleDescr rule = (RuleDescr) parser.parse( line ).getRules().get( 0 );
return rule.getName();
}
private String getPackage(String line) throws DroolsParserException {
DrlParser parser = new DrlParser();
return parser.parse( line ).getName();
}
public List<Asset> getAssets() {
return this.assets;
}
public String getPackageName() {
return this.packageName;
}
public String getPackageHeader() {
return this.header.toString();
}
public boolean isDSLEnabled() {
return this.usesDSL;
}
/**
* Holds a rule to import. The content does not include the "end".
*
* @author Michael Neale
*/
public static class Asset {
public Asset(String name,
String content,
String format) {
this.name = name;
this.content = content;
this.format = format;
}
public String format;
public String name;
public String content;
}
/**
* This merges the toMerge new schtuff into the existing. Line by line, simple stuff.
*/
public static String mergeLines(String existing,
String toMerge) {
if ( toMerge == null || toMerge.equals( "" ) ) {
return existing;
}
if ( existing == null || existing.equals( "" ) ) {
return toMerge;
}
Set existingLines = new HashSet<String>( Arrays.asList( existing.split( "\n" ) ) );
String[] newLines = toMerge.split( "\n" );
for ( int i = 0; i < newLines.length; i++ ) {
String newLine = newLines[i].trim();
if ( !newLine.equals( "" ) && !existingLines.contains( newLines[i].trim() ) ) {
existing = existing + "\n" + newLines[i];
}
}
return existing;
}
}