package de.unisiegen.tpml.core.expressions ; import java.util.ArrayList ; import java.util.Arrays ; import de.unisiegen.tpml.core.exceptions.LanguageParserMultiException ; import de.unisiegen.tpml.core.exceptions.NotOnlyFreeVariableException ; import de.unisiegen.tpml.core.exceptions.RowSubstitutionException ; import de.unisiegen.tpml.core.interfaces.DefaultExpressions ; import de.unisiegen.tpml.core.languages.MultipleIdentifier ; import de.unisiegen.tpml.core.latex.DefaultLatexCommand ; import de.unisiegen.tpml.core.latex.DefaultLatexStringBuilder ; import de.unisiegen.tpml.core.latex.LatexCommandList ; import de.unisiegen.tpml.core.latex.LatexStringBuilder ; import de.unisiegen.tpml.core.latex.LatexStringBuilderFactory ; import de.unisiegen.tpml.core.prettyprinter.PrettyStringBuilder ; import de.unisiegen.tpml.core.prettyprinter.PrettyStringBuilderFactory ; import de.unisiegen.tpml.core.typechecker.TypeSubstitution ; import de.unisiegen.tpml.core.util.BoundRenaming ; /** * Instances of this class represent row expressions. * * @author Christian Fehler * @version $Rev$ */ public final class Row extends Expression implements DefaultExpressions { /** * String for the case that the expressions are null. */ private static final String EXPRESSIONS_NULL = "expressions is null" ; //$NON-NLS-1$ /** * String for the case that one expression are null. */ private static final String EXPRESSION_NULL = "one expression is null" ; //$NON-NLS-1$ /** * The caption of this {@link Expression}. */ private static final String CAPTION = Expression.getCaption ( Row.class ) ; /** * Returns a set of needed latex commands for this latex printable object. * * @return A set of needed latex commands for this latex printable object. */ public static LatexCommandList getLatexCommandsStatic ( ) { LatexCommandList commands = new LatexCommandList ( ) ; commands.add ( new DefaultLatexCommand ( LATEX_ROW , 1 , "\\color{" //$NON-NLS-1$ + LATEX_COLOR_EXPRESSION + "}#1" , //$NON-NLS-1$ "epsilon | val a = e; r1 | method m : τ = e ; r1" ) ) ; //$NON-NLS-1$ return commands ; } /** * Returns the union of the given first and second {@link Row}. * * @param pR1 The first {@link Row}. * @param pR2 The second {@link Row}. * @return The union of the given first and second {@link Row}. */ public static Row union ( Row pR1 , Row pR2 ) { Expression [ ] newExpressions = new Expression [ pR1.getExpressions ( ).length + pR2.getExpressions ( ).length ] ; for ( int i = 0 ; i < pR1.getExpressions ( ).length ; i ++ ) { newExpressions [ i ] = pR1.getExpressions ( ) [ i ] ; } for ( int i = 0 ; i < pR2.getExpressions ( ).length ; i ++ ) { newExpressions [ pR1.getExpressions ( ).length + i ] = pR2 .getExpressions ( ) [ i ] ; } Row row = new Row ( newExpressions ) ; MultipleIdentifier.check ( row ) ; return row ; } /** * Indeces of the child {@link Expression}s. */ private int [ ] indicesE ; /** * The expressions. */ private Expression [ ] expressions ; /** * Allocates a new {@link Row}. * * @param pExpressions The child {@link Expression}. */ public Row ( Expression [ ] pExpressions ) { if ( pExpressions == null ) { throw new NullPointerException ( EXPRESSIONS_NULL ) ; } for ( Expression e : pExpressions ) { if ( e == null ) { throw new NullPointerException ( EXPRESSION_NULL ) ; } } this.expressions = pExpressions ; this.indicesE = new int [ this.expressions.length ] ; for ( int i = 0 ; i < this.expressions.length ; i ++ ) { this.expressions [ i ].setParent ( this ) ; this.indicesE [ i ] = i + 1 ; } for ( Expression child : this.expressions ) { if ( child instanceof Attribute ) { ( ( Attribute ) child ).getIdentifiersBound ( ) ; } } checkDisjunction ( ) ; } /** * Allocates a new {@link Row}. * * @param pExpressions The child {@link Expression}. * @param pParserStartOffset The start offset of this {@link Expression} in * the source code. * @param pParserEndOffset The end offset of this {@link Expression} in the * source code. */ public Row ( Expression [ ] pExpressions , int pParserStartOffset , int pParserEndOffset ) { this ( pExpressions ) ; this.parserStartOffset = pParserStartOffset ; this.parserEndOffset = pParserEndOffset ; } /** * Checks the disjunction of the {@link Identifier} sets. */ private void checkDisjunction ( ) { ArrayList < Identifier > negativeIdentifiers = new ArrayList < Identifier > ( ) ; ArrayList < Identifier > allIdentifiers = new ArrayList < Identifier > ( ) ; for ( int i = 0 ; i < this.expressions.length ; i ++ ) { if ( this.expressions [ i ] instanceof Attribute ) { Attribute attribute = ( Attribute ) this.expressions [ i ] ; allIdentifiers.clear ( ) ; negativeIdentifiers.clear ( ) ; for ( int j = i + 1 ; j < this.expressions.length ; j ++ ) { allIdentifiers.addAll ( this.expressions [ j ].getIdentifiersAll ( ) ) ; } for ( Identifier allId : allIdentifiers ) { if ( ( attribute.getId ( ).equals ( allId ) ) && ( ! ( Identifier.Set.ATTRIBUTE.equals ( allId.getSet ( ) ) ) ) ) { negativeIdentifiers.add ( allId ) ; } } /* * Throw an exception, if the negative identifier list contains one or * more identifiers. If this happens, all Identifiers are added. */ if ( negativeIdentifiers.size ( ) > 0 ) { negativeIdentifiers.clear ( ) ; for ( Identifier allId : allIdentifiers ) { if ( attribute.getId ( ).equals ( allId ) ) { negativeIdentifiers.add ( allId ) ; } } negativeIdentifiers.add ( attribute.getId ( ) ) ; LanguageParserMultiException .throwExceptionDisjunction ( negativeIdentifiers ) ; } } } } /** * {@inheritDoc} */ @ Override public Row clone ( ) { Expression [ ] newExpressions = new Expression [ this.expressions.length ] ; for ( int i = 0 ; i < this.expressions.length ; i ++ ) { newExpressions [ i ] = this.expressions [ i ].clone ( ) ; } return new Row ( newExpressions ) ; } /** * {@inheritDoc} */ @ Override public boolean equals ( Object pObject ) { if ( pObject instanceof Row ) { Row other = ( Row ) pObject ; return Arrays.equals ( this.expressions , other.expressions ) ; } return false ; } /** * {@inheritDoc} */ @ Override public String getCaption ( ) { return CAPTION ; } /** * Returns the sub {@link Expression}s. * * @return the sub {@link Expression}s. */ public Expression [ ] getExpressions ( ) { return this.expressions ; } /** * Returns the indices of the child {@link Expression}s. * * @return The indices of the child {@link Expression}s. */ public int [ ] getExpressionsIndex ( ) { return this.indicesE ; } /** * Returns a list of to the given {@link Attribute} bound {@link Identifier}s. * * @param pAttribute The input {@link Attribute}. * @return A list of to the given {@link Attribute} bound {@link Identifier}s. */ protected ArrayList < Identifier > getIdentifiersBound ( Attribute pAttribute ) { ArrayList < Identifier > boundId = new ArrayList < Identifier > ( ) ; for ( int i = 0 ; i < this.expressions.length ; i ++ ) { if ( pAttribute == this.expressions [ i ] ) { Attribute attribute = ( Attribute ) this.expressions [ i ] ; for ( int j = i + 1 ; j < this.expressions.length ; j ++ ) { Expression child = this.expressions [ j ] ; ArrayList < Identifier > freeIdentifiers = child .getIdentifiersFree ( ) ; for ( Identifier freeId : freeIdentifiers ) { if ( attribute.getId ( ).equals ( freeId ) ) { freeId.setBoundTo ( attribute , attribute.getId ( ) ) ; boundId.add ( freeId ) ; } } } return boundId ; } } return boundId ; } /** * {@inheritDoc} */ @ Override public ArrayList < Identifier > getIdentifiersFree ( ) { if ( this.identifiersFree == null ) { this.identifiersFree = new ArrayList < Identifier > ( ) ; ArrayList < Identifier > newBound = new ArrayList < Identifier > ( ) ; for ( Expression expr : this.expressions ) { ArrayList < Identifier > freeCurrent = new ArrayList < Identifier > ( ) ; freeCurrent.addAll ( expr.getIdentifiersFree ( ) ) ; while ( freeCurrent.removeAll ( newBound ) ) { // Remove all Identifiers with the same name } this.identifiersFree.addAll ( freeCurrent ) ; if ( expr instanceof Attribute ) { Attribute attribute = ( Attribute ) expr ; newBound.add ( attribute.getId ( ) ) ; } } } return this.identifiersFree ; } /** * Returns a set of needed latex commands for this latex printable object. * * @return A set of needed latex commands for this latex printable object. */ @ Override public LatexCommandList getLatexCommands ( ) { LatexCommandList commands = super.getLatexCommands ( ) ; commands.add ( getLatexCommandsStatic ( ) ) ; return commands ; } /** * Returns the number of child {@link Attribute}s. * * @return The number of child {@link Attribute}s. */ public int getNumberOfAttributes ( ) { int count = 0 ; for ( Expression expression : this.expressions ) { if ( expression instanceof Attribute ) { count ++ ; } } return count ; } /** * Returns the number of child {@link Method}s or {@link CurriedMethod}s. * * @return The number of child {@link Method}s or {@link CurriedMethod}s. */ public int getNumberOfDifferentMethods ( ) { int count = 0 ; ArrayList < Identifier > list = new ArrayList < Identifier > ( ) ; for ( Expression expression : this.expressions ) { if ( expression instanceof Method ) { Method method = ( Method ) expression ; if ( ! list.contains ( method.getId ( ) ) ) { list.add ( method.getId ( ) ) ; count ++ ; } } else if ( expression instanceof CurriedMethod ) { CurriedMethod curriedMethod = ( CurriedMethod ) expression ; if ( ! list.contains ( curriedMethod.getIdentifiers ( ) [ 0 ] ) ) { list.add ( curriedMethod.getIdentifiers ( ) [ 0 ] ) ; count ++ ; } } } return count ; } /** * Returns the number of child {@link Method}s or {@link CurriedMethod}s. * * @return The number of child {@link Method}s or {@link CurriedMethod}s. */ public int getNumberOfMethods ( ) { int count = 0 ; for ( Expression expression : this.expressions ) { if ( ( expression instanceof Method ) || ( expression instanceof CurriedMethod ) ) { count ++ ; } } return count ; } /** * Returns the prefix of this {@link Expression}. * * @return The prefix of this {@link Expression}. * @see #prefix */ @ Override public String getPrefix ( ) { if ( this.prefix == null ) { if ( ( this.parent instanceof Class ) || ( this.parent instanceof Inherit ) ) { this.prefix = PREFIX_BODY ; } else { if ( this.isValue ( ) ) { this.prefix = PREFIX_ROW_VALUE ; } else { this.prefix = PREFIX_ROW ; } } } return this.prefix ; } /** * {@inheritDoc} */ @ Override public int hashCode ( ) { return this.expressions.hashCode ( ) ; } /** * {@inheritDoc} */ @ Override public boolean isValue ( ) { for ( Expression expr : this.expressions ) { if ( expr instanceof Attribute ) { Attribute attribute = ( Attribute ) expr ; /* * If an Attribute is not a value, this Row is not a value. */ if ( ! attribute.isValue ( ) ) { return false ; } } } return true ; } /** * {@inheritDoc} */ @ Override public Row substitute ( Identifier pId , Expression pExpression ) { if ( pExpression.getIdentifierFreeNotOnlyVariable ( ) ) { throw new NotOnlyFreeVariableException ( ) ; } Expression [ ] newExpressions = new Expression [ this.expressions.length ] ; for ( int i = 0 ; i < this.expressions.length ; i ++ ) { newExpressions [ i ] = this.expressions [ i ] ; } if ( this.getIdentifiersFree ( ).contains ( pId ) ) { for ( int i = 0 ; i < newExpressions.length ; i ++ ) { if ( newExpressions [ i ] instanceof Attribute ) { Attribute attribute = ( Attribute ) newExpressions [ i ] ; if ( pId.equals ( attribute.getId ( ) ) ) { newExpressions [ i ] = newExpressions [ i ].substitute ( pId , pExpression ) ; break ; } BoundRenaming < Identifier > boundRenaming = new BoundRenaming < Identifier > ( ) ; for ( int j = i + 1 ; j < newExpressions.length ; j ++ ) { boundRenaming.add ( newExpressions [ j ].getIdentifiersFree ( ) ) ; } boundRenaming.remove ( attribute.getId ( ) ) ; boundRenaming.add ( pExpression.getIdentifiersFree ( ) ) ; boundRenaming.add ( pId ) ; Identifier newId = boundRenaming.newIdentifier ( attribute.getId ( ) ) ; if ( ! attribute.getId ( ).equals ( newId ) ) { for ( int j = i + 1 ; j < newExpressions.length ; j ++ ) { newExpressions [ j ] = newExpressions [ j ].substitute ( attribute.getId ( ) , newId ) ; } } newExpressions [ i ] = new Attribute ( newId , attribute.getE ( ) .substitute ( pId , pExpression ) ) ; } else { newExpressions [ i ] = newExpressions [ i ].substitute ( pId , pExpression ) ; } } } return new Row ( newExpressions ) ; } /** * {@inheritDoc} * * @see Expression#substitute(TypeSubstitution) */ @ Override public Row substitute ( TypeSubstitution pTypeSubstitution ) { Expression [ ] newExpr = new Expression [ this.expressions.length ] ; for ( int i = 0 ; i < this.expressions.length ; i ++ ) { newExpr [ i ] = this.expressions [ i ].substitute ( pTypeSubstitution ) ; } return new Row ( newExpr ) ; } /** * Performs the row substitution. * * @param pId The {@link Identifier}. * @param pExpression The {@link Expression} to substitute. * @return The new {@link Row}. */ public Row substituteRow ( Identifier pId , Expression pExpression ) { if ( pExpression.getIdentifierFreeNotOnlyVariable ( ) ) { throw new NotOnlyFreeVariableException ( ) ; } Expression [ ] newExpressions = new Expression [ this.expressions.length ] ; for ( int i = 0 ; i < this.expressions.length ; i ++ ) { newExpressions [ i ] = this.expressions [ i ].clone ( ) ; } boolean found = false ; for ( int i = 0 ; i < newExpressions.length ; i ++ ) { if ( newExpressions [ i ] instanceof Attribute ) { Attribute attribute = ( Attribute ) newExpressions [ i ] ; if ( pId.equals ( attribute.getId ( ) ) ) { newExpressions [ i ] = new Attribute ( attribute.getId ( ) , pExpression ) ; found = true ; break ; } } } if ( ! found ) { throw new RowSubstitutionException ( ) ; } return new Row ( newExpressions ) ; } /** * Returns the tail <code>Row</code>, the current <code>Row</code> * without the first {@link Expression}. * * @return The tail <code>Row</code>. */ public Row tailRow ( ) { Expression [ ] newRowExpressions = new Expression [ this.expressions.length - 1 ] ; for ( int i = 0 ; i < newRowExpressions.length ; i ++ ) { newRowExpressions [ i ] = this.expressions [ i + 1 ] ; } return new Row ( newRowExpressions ) ; } /** * {@inheritDoc} * * @see Expression#toLatexStringBuilder(LatexStringBuilderFactory,int) */ @ Override public LatexStringBuilder toLatexStringBuilder ( LatexStringBuilderFactory pLatexStringBuilderFactory , int pIndent ) { StringBuilder body = new StringBuilder ( ) ; for ( int i = 0 ; i < this.expressions.length ; i ++ ) { body.append ( this.expressions [ i ].toPrettyString ( ).toString ( ) ) ; if ( i != this.expressions.length - 1 ) { body.append ( PRETTY_SPACE ) ; } } if ( this.expressions.length == 0 ) { body.append ( PRETTY_EPSILON ) ; } String descriptions[] = new String [ 2 + this.expressions.length ] ; descriptions [ 0 ] = this.toPrettyString ( ).toString ( ) ; descriptions [ 1 ] = body.toString ( ) ; for ( int i = 0 ; i < this.expressions.length ; i ++ ) { descriptions [ 2 + i ] = this.expressions [ i ].toPrettyString ( ) .toString ( ) ; } LatexStringBuilder builder = pLatexStringBuilderFactory.newBuilder ( PRIO_ROW , LATEX_ROW , pIndent , descriptions ) ; builder.addBuilderBegin ( ) ; for ( int i = 0 ; i < this.expressions.length ; i ++ ) { builder.addBuilder ( this.expressions [ i ].toLatexStringBuilder ( pLatexStringBuilderFactory , pIndent + LATEX_INDENT * 2 ) , PRIO_ROW_E ) ; if ( i != this.expressions.length - 1 ) { builder.addText ( LATEX_LINE_BREAK_SOURCE_CODE ) ; builder.addText ( DefaultLatexStringBuilder.getIndent ( pIndent + LATEX_INDENT ) + LATEX_SPACE ) ; builder.addBreak ( ) ; } } if ( this.expressions.length == 0 ) { builder.addText ( LATEX_LINE_BREAK_SOURCE_CODE ) ; builder.addText ( DefaultLatexStringBuilder.getIndent ( pIndent + LATEX_INDENT ) ) ; builder.addText ( LATEX_EPSILON ) ; } builder.addBuilderEnd ( ) ; return builder ; } /** * {@inheritDoc} * * @see Expression#toPrettyStringBuilder(PrettyStringBuilderFactory) */ @ Override public PrettyStringBuilder toPrettyStringBuilder ( PrettyStringBuilderFactory pPrettyStringBuilderFactory ) { if ( this.prettyStringBuilder == null ) { this.prettyStringBuilder = pPrettyStringBuilderFactory.newBuilder ( this , PRIO_ROW ) ; for ( int i = 0 ; i < this.expressions.length ; i ++ ) { this.prettyStringBuilder .addBuilder ( this.expressions [ i ] .toPrettyStringBuilder ( pPrettyStringBuilderFactory ) , PRIO_ROW_E ) ; if ( i != this.expressions.length - 1 ) { this.prettyStringBuilder.addText ( PRETTY_SPACE ) ; this.prettyStringBuilder.addBreak ( ) ; } } if ( this.expressions.length == 0 ) { this.prettyStringBuilder.addText ( PRETTY_EPSILON ) ; } } return this.prettyStringBuilder ; } }