/*
* Copyright (C) 2008 Universidade Federal de Campina Grande
*
* This file is part of OurGrid.
*
* OurGrid is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.ourgrid.common.specification.grammar.io;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;
import org.ourgrid.common.specification.grammar.Grammar;
import org.ourgrid.common.specification.grammar.Rule;
import org.ourgrid.common.specification.grammar.Symbol;
import org.ourgrid.common.specification.grammar.exception.InvalidRuleException;
/**
* This is a GrammarReader entity that knows how to read a file generated by the
* Gals software (version 2003.10.03)
*
* @see Gals - http://sourceforge.net/projects/gals/
*/
public class GalsGrammarReader implements GrammarReader {
/**
* This symbol is the one that is recognized as the empty rule at the
* grammar description language. In this case is the GALS grammar
* description language.
*/
private final String NULL_SYMBOL = "" + '@';
// OBS.: This symbols was modified because we have got problems using the
// original '?' symbol.
// Theses problems are related with the codifications at the remote machines
// that do not support
// this symbol... then, to use our grammars into GALS, change this symbol.
private BufferedReader reader;
private Grammar grammar;
private int ruleCounter;
private int symbolCounter;
/**
* @see org.ourgrid.common.specification.grammar.io.GrammarReader#read(java.io.File,
* org.ourgrid.common.specification.grammar.Grammar)
*/
public Grammar read( File grammarFile, Grammar toFillGrammar ) throws MalformedGrammarException,
FileNotFoundException, IOException {
this.reader = new BufferedReader( new FileReader( grammarFile ) );
buildGrammar( toFillGrammar );
return this.grammar;
}
/**
* @see org.ourgrid.common.specification.grammar.io.GrammarReader#read(java.io.InputStream,
* org.ourgrid.common.specification.grammar.Grammar)
*/
public Grammar read( InputStream stream, Grammar toFillGrammar ) throws MalformedGrammarException,
FileNotFoundException, IOException {
this.reader = new BufferedReader( new InputStreamReader( stream ) );
buildGrammar( toFillGrammar );
return this.grammar;
}
/**
* @param toFillGrammar The grammar object to be filled with the read
* informations
* @throws IOException
* @throws MalformedGrammarException
*/
private void buildGrammar( Grammar toFillGrammar ) throws IOException, MalformedGrammarException {
this.grammar = toFillGrammar;
this.ruleCounter = 0;
this.symbolCounter = 0;
this.getGrammar();
}
/**
* Changes the grammar passed as paramether for the read() method. It will
* fill up the grammar with the information read from the file.
*
* @throws MalformedGrammarException
*/
private void getGrammar() throws IOException, MalformedGrammarException {
String actualLine = null;
StringBuffer buffer = null;
actualLine = this.readNonBlankLine();
if ( actualLine != null ) {
buffer = new StringBuffer( actualLine );
String blockName = (buffer.substring( 1 )).trim();
// First Block of needed informatin from the Gals grammar
// description file.
while ( !blockName.equals( "Tokens" ) && !blockName.equals( "" ) ) {
blockName = this.getNextBlockName();
}
blockName = this.getTerminalInformations();
// Second Block of needed informatin from the Gals grammar
// description file.
blockName = this.getNonTerminalInformations();
// Third Block of needed informatin from the Gals grammar
// description file.
this.getRulesInformations();
} else {
throw new MalformedGrammarException( "The grammar file is empty." );
}
}
/**
* Will read the informations from the grammar block and insert the
* necessary ones at the grammar.
*
* @throws MalformedGrammarException
*/
private void getRulesInformations() throws IOException, MalformedGrammarException {
String line = this.readNonBlankLine();
while ( line != null ) {
StringTokenizer headAndBodies = new StringTokenizer( line, " " );
String headStr = headAndBodies.nextToken().trim();
Symbol head = this.createSymbol( ++this.symbolCounter, headStr, Symbol.NON_TERMINAL );
if ( headAndBodies.nextToken().equals( "::=" ) ) {
Vector<Symbol> bodyVector = new Vector<Symbol>();
String symbolStr = "";
while ( head != null ) {
if ( headAndBodies.hasMoreTokens() ) {
symbolStr = headAndBodies.nextToken().trim();
} else { // the line ended and was not found a ";"
// symbol. It is necessary to get next line
headAndBodies = this.getNextNonEmptyTokenizer();
symbolStr = headAndBodies.nextToken().trim();
}
if ( symbolStr.equals( "|" ) ) { // Time to close a rule
// with the actual head
// and bodyVector
createAndDeployRule( ++ruleCounter, head, bodyVector );
bodyVector = new Vector<Symbol>();
} else {
if ( symbolStr.equals( ";" ) ) { // Time to eliminate
// the actual head
createAndDeployRule( ++ruleCounter, head, bodyVector );
bodyVector = new Vector<Symbol>();
head = null;
} else { // Read another symbol from token and add it
// at bodyVector
Symbol symbol;
if ( symbolStr.charAt( 0 ) == '<' ) {
symbol = this.createSymbol( ++this.symbolCounter, symbolStr, Symbol.NON_TERMINAL );
} else if ( symbolStr.charAt( 0 ) == '#' ) {
String action = symbolStr.trim();
symbol = new Symbol( Integer.MAX_VALUE, action, Symbol.SEMANTIC_ACTION );
} else {
symbol = this.createSymbol( ++this.symbolCounter, symbolStr, Symbol.TERMINAL );
}
bodyVector.add( symbol );
}
}
}
} else { // Error situation
throw new MalformedGrammarException( "There is a rule without a head or the symbol \"::=\" was forgot." );
}
line = this.readNonBlankLine();
}
}
private String readNonBlankLine() throws IOException {
String line = "";
do {
line = reader.readLine();
if ( line == null )
break;
} while ( line.equals( "" ) );
return line;
}
/*
* Search for the next line non empty and returns a StringTokenizer of this
* line.
*/
private StringTokenizer getNextNonEmptyTokenizer() throws IOException {
String line = this.readNonBlankLine();
return new StringTokenizer( line, " " );
}
private Symbol createSymbol( int code, String value, int type ) {
// If the value is in format "xxx" will eliminate the " symbols
int last = value.length() - 1;
if ( value.charAt( 0 ) == '\"' && value.charAt( last ) == '\"' ) {
StringBuffer buffer = new StringBuffer( value );
value = buffer.substring( 1, last );
}
Symbol toTest = grammar.getSymbol( value );
if ( toTest == null ) {
if ( value.equals( this.NULL_SYMBOL ) ) {
toTest = Symbol.EMPTY;
} else {
toTest = new Symbol( code, value, type );
}
} else {
--symbolCounter;
}
return toTest;
}
/*
* Creates a rule and add it at the grammar.
*/
private void createAndDeployRule( int codeCounter, Symbol head, Vector<Symbol> bodyVector )
throws MalformedGrammarException {
try {
Rule rule = new Rule( codeCounter, head, getArrayOfSymbols( bodyVector ) );
this.grammar.addRule( rule );
} catch ( InvalidRuleException irex ) {
Iterator<Symbol> it = bodyVector.iterator();
String rule = head.getValue() + " ::= ";
while ( it.hasNext() )
rule = rule + (it.next()).getValue();
throw new MalformedGrammarException( "Found a MalFormed rule : " + rule, irex );
}
}
/*
* Transform a vector os Symbol objects in a array cointaining them.
*/
private Symbol[ ] getArrayOfSymbols( Vector<Symbol> vec ) {
Symbol[ ] toReturn = new Symbol[ vec.size() ];
Iterator<Symbol> it = vec.iterator();
int counter = 0;
while ( it.hasNext() ) {
toReturn[counter++] = it.next();
}
return toReturn;
}
/**
* Will read the informations from the non terminals block and insert the
* necessaries ones at the grammar.
*
* @return The last line read from the file. The empty string if occurres
* any problem.
*/
private String getNonTerminalInformations() throws IOException {
String newSymbol = this.readNonBlankLine();
while ( newSymbol.charAt( 0 ) != '#' ) {
Symbol symbol = this.createSymbol( ++this.symbolCounter, newSymbol.trim(), Symbol.NON_TERMINAL );
this.grammar.addSymbol( symbol );
newSymbol = this.readNonBlankLine();
}
StringBuffer buffer = new StringBuffer( newSymbol );
return buffer.substring( 1 );
}
/**
* Will read the informations from the tokens block and insert the
* necessaries ones at the grammar.
*
* @return The last line read from the file. The empty string if occurres
* any problem.
*/
private String getTerminalInformations() throws IOException {
String newSymbol = this.readNonBlankLine();
while ( newSymbol.charAt( 0 ) != '#' ) {
Symbol symbol = this.createSymbol( ++this.symbolCounter, newSymbol.trim(), Symbol.TERMINAL );
this.grammar.addSymbol( symbol );
newSymbol = this.readNonBlankLine();
}
StringBuffer buffer = new StringBuffer( newSymbol );
return buffer.substring( 1 );
}
/**
* Will try to find another line that begins with the '#' symbol.
*
* @return The name of the next block if it was found, and the empty string
* if not.
* @throws MalformedGrammarException if the EOF was found
*/
private String getNextBlockName() throws IOException, MalformedGrammarException {
String line = "";
do {
line = reader.readLine();
if ( line != null ) {
// Test if this line is empty because of the index above.
if ( !line.equals( "" ) ) {
if ( line.charAt( 0 ) == '#' ) {
StringBuffer buffer = new StringBuffer( line );
return buffer.substring( 1 ).trim();
}
}
}
} while ( line != null );
throw new MalformedGrammarException( "The grammar have not all the informations block necessaries." );
}
}