/* * 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.peer.business.controller.matcher; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.StringTokenizer; import org.ourgrid.common.exception.InvalidExpressionException; import org.ourgrid.common.exception.TooManyExpressionsException; import org.ourgrid.common.util.CommonUtils; import org.ourgrid.common.util.StringUtil; /** * This class translates the logical expression sintax used at requirements * field at JDF to Minimize library form and vice-versa. */ public class ExpressionTranslator { /** * Initial expression key. */ public final int INITIAL_KEY = 'a'; /** * Map where the variables will be held. */ private Map<String,String> variablesMap; /** * The key index. */ private char keyIndex; public ExpressionTranslator() { } /** * Translates an expression in Broker JDF sintax compliance to Minimize * library sintax. * * @param brokerLogicalExpression An logical expression in Broker JDF sintax * @return An equivalent expression in Minimize library sintax */ // TOO BIG .. REFACTOR public String translateToMinimizeLibraryForm( String brokerLogicalExpression ) throws InvalidExpressionException { // Initiating here forces the user to first call this method this.variablesMap = CommonUtils.createSerializableMap(); this.keyIndex = INITIAL_KEY; List<Integer> parenthesisList = new LinkedList<Integer>(); StringBuffer returnedExpression = new StringBuffer(); StringTokenizer tokenizer = new StringTokenizer( brokerLogicalExpression, " " ); while ( tokenizer.hasMoreTokens() ) { String token = tokenizer.nextToken(); if ( isANDOperator( token ) ) { returnedExpression.append( "*" ); } else if ( isOROperator( token ) ) { returnedExpression.append( "+" ); } else if ( isNOTOperator( token ) ) { parenthesisList.add( Integer.valueOf( 0 ) ); continue; } else if ( token.equals( "(" ) ) { returnedExpression.append( token ); parenthesisList = incrementedParenthesisList( parenthesisList ); } else if ( token.equals( ")" ) ) { returnedExpression.append( token ); parenthesisList = decrementParenthesisList( parenthesisList ); } else { // Is an expression try { token += tokenizer.nextToken() + tokenizer.nextToken(); } catch ( NoSuchElementException e ) { throw new InvalidExpressionException( "Invalid expression." ); } String key = getKeyByValue( variablesMap, token ); if ( key == null ) { key = generateAKey() + ""; variablesMap.put( key, token ); } returnedExpression.append( key ); } if ( itIsTimeToPutTheNotOperator( parenthesisList ) ) { returnedExpression.append( "'" ); } } return returnedExpression.toString(); } /** * Returns the variable key by its value. * * @param map The Map where the keys and variables are held. * @param value The variable value. * @return The key with the variable exists, <code>null</code> otherwise. */ private String getKeyByValue( Map<String,String> map, String value ) { Iterator<Map.Entry<String,String>> iterator = map.entrySet().iterator(); while ( iterator.hasNext() ) { Map.Entry<String,String> entry = iterator.next(); if ( entry.getValue().equals( value ) ) { return entry.getKey(); } } return null; } /** * Translates a logical expression written in Minimize syntax to the * Broker's syntax * * @param minimizeLogicaExpression The expression in Minimize's syntax. * @return A logical expression in Broker's syntax. * @throws InvalidExpressionException Case the translation in impossible. */ // TOO BIG .. REFACTOR public String translateToBrokerExpressionForm( String minimizeLogicaExpression ) throws InvalidExpressionException { // When the user tryies to converts to MG form and not convert to // minimize first. if ( variablesMap == null ) { throw new InvalidExpressionException( "It is necessary translate to Minimize form first" ); } StringBuffer returnedExpression = new StringBuffer(); List<Integer> notControlList = new LinkedList<Integer>(); // iterates from end to begin to easy the treatment with not (') // operator. for ( int index = minimizeLogicaExpression.length() - 1; index >= 0; index-- ) { String character = minimizeLogicaExpression.charAt( index ) + ""; if ( character.equals( " " ) ) { continue; } else if ( character.equals( "'" ) ) { notControlList.add( Integer.valueOf( 0 ) ); continue; } else if ( character.equals( "(" ) ) { notControlList = decrementParenthesisList( notControlList ); returnedExpression.insert( 0, " " + character ); } else if ( character.equals( ")" ) ) { notControlList = incrementedParenthesisList( notControlList ); returnedExpression.insert( 0, " " + character ); } else if ( character.equals( "*" ) ) { returnedExpression.insert( 0, " AND" ); } else if ( character.equals( "+" ) ) { returnedExpression.insert( 0, " OR" ); } else { String expression = this.variablesMap.get( character ); if ( expression == null ) { throw new InvalidExpressionException( "Invalid variables in minimized expression" ); } returnedExpression.insert( 0, " " + StringUtil.insertSpaces( expression ) ); } if ( itIsTimeToPutTheNotOperator( notControlList ) ) { // Only to remove the space before NOT operator if it comes at // first. returnedExpression.insert( 0, " NOT" ); } // The strange AND case. if ( itIsTimeToPutAndOperator( index, minimizeLogicaExpression ) ) { returnedExpression.insert( 0, " AND" ); } } return returnedExpression.delete( 0, 1 ).toString(); } /** * Checks if it is necessary put the AND operator. * * @param index Where the AND should be put. * @param minimizeLogicaExpression The expression where the AND operator * should be put. * @return <code>true</code> case AND operator is necessary, * <code>false</code> otherwise. */ private boolean itIsTimeToPutAndOperator( int index, String minimizeLogicaExpression ) { char charNow = minimizeLogicaExpression.charAt( index ); if ( ((charNow >= 'a' && charNow <= 'z') || (charNow >= 'A' && charNow <= 'Z') || charNow == '(') && index > 0 ) { int indexBefore = index - 1; char charBefore = minimizeLogicaExpression.charAt( indexBefore ); return ((charBefore != '(') && charBefore != '*' && charBefore != '+' && charBefore != ' '); } return false; } /** * Generates a new key for a variable. * * @return A new key - a char. * @throws TooManyExpressionsException As the Minimize Library works only * with alphabetical characters, and we use this library to get DNF, * we are restricted to use only 52 characters and, eventually, 52 * different expressions at JDF's requirements. */ private char generateAKey() { if ( keyIndex > 'z' ) { keyIndex = 'A'; } else if ( keyIndex == 'Z' + 1 ) { throw new TooManyExpressionsException(); } return (keyIndex++); } /** * Checks if <code>operator</code> is a NOT operator. * * @param operator An operator. * @return <code>true</code> case <code>operator</code> is equals to '!' * or 'NOT', <code>false</code> otherwise. */ private boolean isNOTOperator( String operator ) { return operator.equals( "!" ) || operator.equals( "NOT" ); } /** * Checks if <code>operator</code> is an AND operator. * * @param operator An operator. * @return <code>true</code> case <code>operator</code> is equals to * AND!' or '&&', <code>false</code> otherwise. */ private boolean isANDOperator( String operator ) { return operator.equals( "AND" ) || operator.equals( "&&" ); } /** * Checks if <code>operator</code> is a OR operator. * * @param operator An operator. * @return <code>true</code> case <code>operator</code> is equals to * 'OR' or '||', <code>false</code> otherwise. */ private boolean isOROperator( String operator ) { return operator.equals( "OR" ) || operator.equals( "||" ); } /** * Increases all members of parenthesis list by one. * * @param parenthesisList A List of Integers. * @return The List with all members increased by one. */ private List<Integer> incrementedParenthesisList( List<Integer> parenthesisList ) { List<Integer> newParentList = new LinkedList<Integer>(); Iterator<Integer> it = parenthesisList.listIterator(); while ( it.hasNext() ) { Integer integer = it.next(); newParentList.add( new Integer( integer.intValue() + 1 ) ); } return newParentList; } /** * Decreases all members of parenthesis list by one. * * @param parenthesisList A List of Integers. * @return The List with all members decreased by one. */ private List<Integer> decrementParenthesisList( List<Integer> parenthesisList ) { List<Integer> newParentList = new LinkedList<Integer>(); Iterator<Integer> it = parenthesisList.listIterator(); while ( it.hasNext() ) { Integer integer = it.next(); newParentList.add( Integer.valueOf(integer.intValue() - 1 ) ); } return newParentList; } /** * Checks if it is necessary to put a NOT operator. * * @param parenthesisList A List of Integers that has the notification if a * NOT operator is necessary or not. * @return <code>true</code> case there's any Integer on the List equals * to zero, <code>false</code> otherwise. */ private boolean itIsTimeToPutTheNotOperator( List<Integer> parenthesisList ) { Iterator<Integer> it = parenthesisList.listIterator(); while ( it.hasNext() ) { Integer integer = it.next(); if ( integer.intValue() == 0 ) { it.remove(); return true; } } return false; } }