package de.unisiegen.tpml.core.expressions ;
import java.util.ArrayList ;
import java.util.Arrays ;
import de.unisiegen.tpml.core.exceptions.NotOnlyFreeVariableException ;
import de.unisiegen.tpml.core.interfaces.DefaultExpressions ;
import de.unisiegen.tpml.core.interfaces.DefaultIdentifiers ;
import de.unisiegen.tpml.core.interfaces.ExpressionOrType ;
import de.unisiegen.tpml.core.interfaces.SortedChildren ;
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 duplication expressions,
*
* @author Christian Fehler
* @author Christian Fehler
* @version $Rev$
*/
public final class Duplication extends Expression implements
DefaultIdentifiers , DefaultExpressions , SortedChildren
{
/**
* String for the case that the identifiers are null.
*/
private static final String IDENTIFIERS_NULL = "identifiers is null" ; //$NON-NLS-1$
/**
* String for the case that one identifier are null.
*/
private static final String IDENTIFIER_NULL = "one identifier is null" ; //$NON-NLS-1$
/**
* 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 identifier has the wrong set.
*/
private static final String WRONG_SET = "the set of the identifier has to be 'attribute'" ; //$NON-NLS-1$
/**
* String for the case that the arity of identifiers and expressions doesn´t
* match.
*/
private static final String ARITY = "the arity of identifiers and expressions must match" ; //$NON-NLS-1$
/**
* The caption of this {@link Expression}.
*/
private static final String CAPTION = Expression
.getCaption ( Duplication.class ) ;
/**
* The string of an self identifier.
*/
private static final String SELF = "self" ; //$NON-NLS-1$
/**
* 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_DUPLICATION , 1 , "\\color{" //$NON-NLS-1$
+ LATEX_COLOR_EXPRESSION + "}\\{<#1>\\}" , "a1 = e1 ; ... ; an = en" ) ) ; //$NON-NLS-1$//$NON-NLS-2$
return commands ;
}
/**
* Indeces of the child {@link Expression}s.
*/
private int [ ] indicesE ;
/**
* Indeces of the child {@link Identifier}s.
*/
private int [ ] indicesId ;
/**
* The expressions.
*
* @see #getExpressions()
*/
private Expression [ ] expressions ;
/**
* The identifiers.
*
* @see #getIdentifiers()
*/
private Identifier [ ] identifiers ;
/**
* Allocates a new {@link Duplication}.
*
* @param pIdentifiers The {@link Identifier}.
* @param pExpressions The child {@link Expression}.
*/
public Duplication ( Identifier [ ] pIdentifiers , Expression [ ] pExpressions )
{
if ( pIdentifiers == null )
{
throw new NullPointerException ( IDENTIFIERS_NULL ) ;
}
for ( Identifier id : pIdentifiers )
{
if ( id == null )
{
throw new NullPointerException ( IDENTIFIER_NULL ) ;
}
if ( ! Identifier.Set.ATTRIBUTE.equals ( id.getSet ( ) ) )
{
throw new IllegalArgumentException ( WRONG_SET ) ;
}
}
if ( pExpressions == null )
{
throw new NullPointerException ( EXPRESSIONS_NULL ) ;
}
for ( Expression e : pExpressions )
{
if ( e == null )
{
throw new NullPointerException ( EXPRESSION_NULL ) ;
}
}
if ( pIdentifiers.length != pExpressions.length )
{
throw new IllegalArgumentException ( ARITY ) ;
}
// Identifier
this.identifiers = pIdentifiers ;
this.indicesId = new int [ this.identifiers.length ] ;
// Expression
this.expressions = pExpressions ;
this.indicesE = new int [ this.expressions.length ] ;
for ( int i = 0 ; i < this.expressions.length ; i ++ )
{
// Identifier
this.identifiers [ i ].setParent ( this ) ;
this.indicesId [ i ] = i + 1 ;
// Expression
this.expressions [ i ].setParent ( this ) ;
this.indicesE [ i ] = i + 1 ;
}
}
/**
* Allocates a new {@link Duplication}.
*
* @param pIdentifiers The {@link Identifier}.
* @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 Duplication ( Identifier [ ] pIdentifiers ,
Expression [ ] pExpressions , int pParserStartOffset ,
int pParserEndOffset )
{
this ( pIdentifiers , pExpressions ) ;
this.parserStartOffset = pParserStartOffset ;
this.parserEndOffset = pParserEndOffset ;
}
/**
* {@inheritDoc}
*/
@ Override
public Duplication clone ( )
{
Expression [ ] newExpressions = new Expression [ this.expressions.length ] ;
for ( int i = 0 ; i < this.expressions.length ; i ++ )
{
newExpressions [ i ] = this.expressions [ i ].clone ( ) ;
}
Identifier [ ] newIdentifiers = new Identifier [ this.identifiers.length ] ;
for ( int i = 0 ; i < newIdentifiers.length ; i ++ )
{
newIdentifiers [ i ] = this.identifiers [ i ].clone ( ) ;
}
return new Duplication ( newIdentifiers , newExpressions ) ;
}
/**
* {@inheritDoc}
*/
@ Override
public boolean equals ( Object pObject )
{
if ( pObject instanceof Duplication )
{
Duplication other = ( Duplication ) pObject ;
return ( ( Arrays.equals ( this.expressions , other.expressions ) ) && ( Arrays
.equals ( this.identifiers , other.identifiers ) ) ) ;
}
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 the {@link Identifier}s of this {@link Expression}.
*
* @return The {@link Identifier}s of this {@link Expression}.
*/
public Identifier [ ] getIdentifiers ( )
{
return this.identifiers ;
}
/**
* {@inheritDoc}
*/
@ Override
public ArrayList < Identifier > getIdentifiersFree ( )
{
if ( this.identifiersFree == null )
{
this.identifiersFree = new ArrayList < Identifier > ( ) ;
this.identifiersFree.add ( new Identifier ( SELF , Identifier.Set.SELF ) ) ;
for ( int i = 0 ; i < this.expressions.length ; i ++ )
{
this.identifiersFree.addAll ( this.expressions [ i ]
.getIdentifiersFree ( ) ) ;
this.identifiersFree.addAll ( this.identifiers [ i ]
.getIdentifiersFree ( ) ) ;
}
}
return this.identifiersFree ;
}
/**
* Returns the indices of the child {@link Identifier}s.
*
* @return The indices of the child {@link Identifier}s.
*/
public int [ ] getIdentifiersIndex ( )
{
return this.indicesId ;
}
/**
* 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 {@link Identifier}s and {@link Expression}s in the right
* sorting.
*
* @return The {@link Identifier}s and {@link Expression}s in the right
* sorting.
* @see SortedChildren#getSortedChildren()
*/
public ExpressionOrType [ ] getSortedChildren ( )
{
ExpressionOrType [ ] result = new ExpressionOrType [ this.identifiers.length
+ this.expressions.length ] ;
for ( int i = 0 ; i < this.identifiers.length + this.expressions.length ; i ++ )
{
if ( i % 2 == 0 )
{
result [ i ] = this.identifiers [ i / 2 ] ;
}
else
{
result [ i ] = this.expressions [ i / 2 ] ;
}
}
return result ;
}
/**
* {@inheritDoc}
*/
@ Override
public int hashCode ( )
{
return this.identifiers.hashCode ( ) + this.expressions.hashCode ( ) ;
}
/**
* {@inheritDoc}
*/
@ Override
public Expression substitute ( Identifier pId , Expression pExpression )
{
if ( pExpression.getIdentifierFreeNotOnlyVariable ( ) )
{
throw new NotOnlyFreeVariableException ( ) ;
}
if ( ( Identifier.Set.SELF.equals ( pId.getSet ( ) ) )
&& ( pExpression instanceof ObjectExpr ) )
{
ObjectExpr objectExpr = ( ObjectExpr ) pExpression ;
Row row = objectExpr.getRow ( ) ;
Identifier [ ] newIdentifiers = new Identifier [ this.identifiers.length ] ;
BoundRenaming < Identifier > boundRenaming = new BoundRenaming < Identifier > ( ) ;
boundRenaming.add ( row.getIdentifiersFree ( ) ) ;
for ( Expression e : this.expressions )
{
boundRenaming.add ( e.getIdentifiersFree ( ) ) ;
}
for ( Identifier id : this.identifiers )
{
boundRenaming.add ( id ) ;
}
for ( int i = 0 ; i < this.expressions.length ; i ++ )
{
Identifier newId = boundRenaming
.newIdentifier ( this.identifiers [ i ] ) ;
boundRenaming.add ( newId ) ;
newIdentifiers [ i ] = newId ;
newIdentifiers [ i ].setSet ( Identifier.Set.VARIABLE ) ;
}
for ( int i = 0 ; i < this.expressions.length ; i ++ )
{
row = row
.substituteRow ( this.identifiers [ i ] , newIdentifiers [ i ] ) ;
}
Expression result = new ObjectExpr ( objectExpr.getId ( ) , objectExpr
.getTau ( ) , row ) ;
for ( int i = this.expressions.length - 1 ; i >= 0 ; i -- )
{
result = new Let ( newIdentifiers [ i ].clone ( ) , null ,
this.expressions [ i ].substitute ( pId , pExpression ) , result ) ;
}
return result ;
}
Expression [ ] newExpressions = new Expression [ this.expressions.length ] ;
for ( int i = 0 ; i < this.expressions.length ; i ++ )
{
newExpressions [ i ] = this.expressions [ i ].substitute ( pId ,
pExpression ) ;
}
return new Duplication ( this.identifiers , newExpressions ) ;
}
/**
* {@inheritDoc}
*
* @see Expression#substitute(TypeSubstitution)
*/
@ Override
public Duplication substitute ( TypeSubstitution pTypeSubstitution )
{
Expression [ ] newExpressions = new Expression [ this.expressions.length ] ;
for ( int i = 0 ; i < this.expressions.length ; i ++ )
{
newExpressions [ i ] = this.expressions [ i ]
.substitute ( pTypeSubstitution ) ;
}
return new Duplication ( this.identifiers , newExpressions ) ;
}
/**
* {@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.identifiers [ i ].toPrettyString ( ).toString ( ) ) ;
body.append ( PRETTY_SPACE ) ;
body.append ( PRETTY_EQUAL ) ;
body.append ( PRETTY_SPACE ) ;
body.append ( this.expressions [ i ].toPrettyString ( ).toString ( ) ) ;
if ( i != this.expressions.length - 1 )
{
body.append ( PRETTY_SEMI ) ;
body.append ( PRETTY_SPACE ) ;
}
}
String descriptions[] = new String [ 2 + this.identifiers.length
+ this.expressions.length ] ;
descriptions [ 0 ] = this.toPrettyString ( ).toString ( ) ;
descriptions [ 1 ] = body.toString ( ) ;
for ( int i = 0 ; i < this.identifiers.length ; i ++ )
{
descriptions [ 2 + i * 2 ] = this.identifiers [ i ].toPrettyString ( )
.toString ( ) ;
descriptions [ 3 + i * 2 ] = this.expressions [ i ].toPrettyString ( )
.toString ( ) ;
}
LatexStringBuilder builder = pLatexStringBuilderFactory.newBuilder (
PRIO_DUPLICATION , LATEX_DUPLICATION , pIndent , descriptions ) ;
builder.addBuilderBegin ( ) ;
builder.addText ( LATEX_LINE_BREAK_SOURCE_CODE ) ;
builder.addText ( DefaultLatexStringBuilder.getIndent ( pIndent
+ LATEX_INDENT )
+ LATEX_SPACE ) ;
for ( int i = 0 ; i < this.expressions.length ; i ++ )
{
builder.addBuilder ( this.identifiers [ i ].toLatexStringBuilder (
pLatexStringBuilderFactory , pIndent + LATEX_INDENT * 2 ) , PRIO_ID ) ;
builder.addText ( LATEX_LINE_BREAK_SOURCE_CODE ) ;
builder.addText ( DefaultLatexStringBuilder.getIndent ( pIndent
+ LATEX_INDENT )
+ LATEX_SPACE ) ;
builder.addText ( LATEX_EQUAL ) ;
builder.addText ( LATEX_SPACE ) ;
builder.addBuilder ( this.expressions [ i ].toLatexStringBuilder (
pLatexStringBuilderFactory , pIndent + LATEX_INDENT * 2 ) ,
PRIO_DUPLICATION_E ) ;
if ( i != this.expressions.length - 1 )
{
builder.addText ( LATEX_LINE_BREAK_SOURCE_CODE ) ;
builder.addText ( DefaultLatexStringBuilder.getIndent ( pIndent
+ LATEX_INDENT )
+ LATEX_SEMI ) ;
builder.addText ( LATEX_SPACE ) ;
builder.addBreak ( ) ;
}
}
// Only one space for '{< >}'
if ( this.expressions.length > 0 )
{
builder.addText ( LATEX_LINE_BREAK_SOURCE_CODE ) ;
builder.addText ( DefaultLatexStringBuilder.getIndent ( pIndent
+ LATEX_INDENT )
+ LATEX_SPACE ) ;
}
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_DUPLICATION ) ;
this.prettyStringBuilder.addText ( PRETTY_DUPLBEGIN ) ;
this.prettyStringBuilder.addText ( PRETTY_SPACE ) ;
for ( int i = 0 ; i < this.expressions.length ; i ++ )
{
this.prettyStringBuilder.addBuilder ( this.identifiers [ i ]
.toPrettyStringBuilder ( pPrettyStringBuilderFactory ) , PRIO_ID ) ;
this.prettyStringBuilder.addText ( PRETTY_SPACE ) ;
this.prettyStringBuilder.addText ( PRETTY_EQUAL ) ;
this.prettyStringBuilder.addText ( PRETTY_SPACE ) ;
this.prettyStringBuilder.addBuilder ( this.expressions [ i ]
.toPrettyStringBuilder ( pPrettyStringBuilderFactory ) ,
PRIO_DUPLICATION_E ) ;
if ( i != this.expressions.length - 1 )
{
this.prettyStringBuilder.addText ( PRETTY_SEMI ) ;
this.prettyStringBuilder.addText ( PRETTY_SPACE ) ;
this.prettyStringBuilder.addBreak ( ) ;
}
}
// Only one space for '{< >}'
if ( this.expressions.length > 0 )
{
this.prettyStringBuilder.addText ( PRETTY_SPACE ) ;
}
this.prettyStringBuilder.addText ( PRETTY_DUPLEND ) ;
}
return this.prettyStringBuilder ;
}
}