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.interfaces.BoundIdentifiers ; import de.unisiegen.tpml.core.interfaces.DefaultExpressions ; 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 ; /** * Instances of this class represent inherit expressions. * * @author Christian Fehler * @version $Rev$ */ public final class Inherit extends Expression implements BoundIdentifiers , DefaultExpressions { /** * The string of an self identifier. */ private static final String SELF = "self" ; //$NON-NLS-1$ /** * Indeces of the child {@link Expression}s. */ private static final int [ ] INDICES_E = new int [ ] { - 1 , - 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 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 expression is null. */ private static final String EXPRESSION_NULL = "expression is null" ; //$NON-NLS-1$ /** * String for the case that the body is null. */ private static final String BODY_NULL = "body is null" ; //$NON-NLS-1$ /** * String for the case that the sub body is not a {@link Inherit} and not a * {@link Row}. */ private static final String BODY_INCORRECT = "the sub body is not a body and not a row" ; //$NON-NLS-1$ /** * The caption of this {@link Expression}. */ private static final String CAPTION = Expression.getCaption ( Inherit.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_KEY_INHERIT , 0 , "\\textbf{\\color{" + LATEX_COLOR_KEYWORD + "}{inherit}}" ) ) ; //$NON-NLS-1$ //$NON-NLS-2$ commands.add ( new DefaultLatexCommand ( LATEX_KEY_FROM , 0 , "\\textbf{\\color{" + LATEX_COLOR_KEYWORD + "}{from}}" ) ) ; //$NON-NLS-1$ //$NON-NLS-2$ commands.add ( new DefaultLatexCommand ( LATEX_INHERIT , 3 , "\\color{" //$NON-NLS-1$ + LATEX_COLOR_EXPRESSION + "}\\" + LATEX_KEY_INHERIT + "\\ #1\\ \\" //$NON-NLS-1$//$NON-NLS-2$ + LATEX_KEY_FROM + "\\ #2\\ ;\\ #3" , "a1, ... , ak" , "e" , "b" ) ) ; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ return commands ; } /** * Indeces of the child {@link Identifier}s. */ private int [ ] indicesId ; /** * The expression. */ private Expression [ ] expressions ; /** * The list of identifiers. * * @see #getIdentifiers() */ protected Identifier [ ] identifiers ; /** * Allocates a new {@link Inherit}. * * @param pIdentifiers The attribute {@link Identifier}s. * @param pExpression The child {@link Expression}. * @param pBody The child body. */ public Inherit ( Identifier [ ] pIdentifiers , Expression pExpression , Expression pBody ) { 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 ( pExpression == null ) { throw new NullPointerException ( EXPRESSION_NULL ) ; } if ( pBody == null ) { throw new NullPointerException ( BODY_NULL ) ; } if ( ( ! ( pBody instanceof Inherit ) ) && ( ! ( pBody instanceof Row ) ) ) { throw new IllegalArgumentException ( BODY_INCORRECT ) ; } // Identifier this.identifiers = pIdentifiers ; this.indicesId = new int [ this.identifiers.length ] ; for ( int i = 0 ; i < this.identifiers.length ; i ++ ) { this.identifiers [ i ].setParent ( this ) ; this.indicesId [ i ] = i + 1 ; } // Expression this.expressions = new Expression [ ] { pExpression , pBody } ; this.expressions [ 0 ].setParent ( this ) ; this.expressions [ 1 ].setParent ( this ) ; // Check the disjunction getIdentifiersBound ( ) ; checkDisjunction ( ) ; } /** * Allocates a new {@link Inherit}. * * @param pIdentifiers The attribute {@link Identifier}s. * @param pExpression The child {@link Expression}. * @param pBody The child body. * @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 Inherit ( Identifier [ ] pIdentifiers , Expression pExpression , Expression pBody , int pParserStartOffset , int pParserEndOffset ) { this ( pIdentifiers , pExpression , pBody ) ; this.parserStartOffset = pParserStartOffset ; this.parserEndOffset = pParserEndOffset ; } /** * Checks the disjunction of the {@link Identifier} sets. */ private void checkDisjunction ( ) { /* * Check the disjunction of the attribute identifiers. */ ArrayList < Identifier > allIdentifiers = this.expressions [ 1 ] .getIdentifiersAll ( ) ; ArrayList < Identifier > negativeIdentifiers = new ArrayList < Identifier > ( ) ; for ( Identifier idAttribute : this.identifiers ) { negativeIdentifiers.clear ( ) ; for ( Identifier allId : allIdentifiers ) { if ( ( idAttribute.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 ( idAttribute.equals ( allId ) ) { negativeIdentifiers.add ( allId ) ; } } negativeIdentifiers.add ( idAttribute ) ; LanguageParserMultiException .throwExceptionDisjunction ( negativeIdentifiers ) ; } } } /** * {@inheritDoc} */ @ Override public Inherit clone ( ) { Identifier [ ] newIdentifiers = new Identifier [ this.identifiers.length ] ; for ( int i = 0 ; i < newIdentifiers.length ; i ++ ) { newIdentifiers [ i ] = this.identifiers [ i ].clone ( ) ; } return new Inherit ( newIdentifiers , this.expressions [ 0 ].clone ( ) , this.expressions [ 1 ].clone ( ) ) ; } /** * {@inheritDoc} */ @ Override public boolean equals ( Object pObject ) { if ( pObject instanceof Inherit ) { Inherit other = ( Inherit ) pObject ; return ( ( Arrays.equals ( this.identifiers , other.identifiers ) ) && ( this.expressions [ 0 ].equals ( other.expressions [ 0 ] ) ) && ( this.expressions [ 1 ] .equals ( other.expressions [ 1 ] ) ) ) ; } return false ; } /** * Returns the sub body. * * @return the sub body. */ public Expression getBody ( ) { return this.expressions [ 1 ] ; } /** * {@inheritDoc} */ @ Override public String getCaption ( ) { return CAPTION ; } /** * Returns a list of all {@link Attribute} {@link Identifier}s in the domain * of this {@link Expression}. * * @return A list of all {@link Attribute} {@link Identifier}s in the domain * of this {@link Expression}. */ @ Override public ArrayList < Identifier > getDomA ( ) { if ( this.domA == null ) { this.domA = new ArrayList < Identifier > ( ) ; for ( Identifier a : this.identifiers ) { this.domA.add ( a ) ; } this.domA.addAll ( this.expressions [ 0 ].getDomA ( ) ) ; this.domA.addAll ( this.expressions [ 1 ].getDomA ( ) ) ; } return this.domA ; } /** * Returns the sub {@link Expression}. * * @return the sub {@link Expression}. */ public Expression getE ( ) { return this.expressions [ 0 ] ; } /** * 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 INDICES_E ; } /** * Returns the {@link Identifier}s of this {@link Expression}. * * @return The {@link Identifier}s of this {@link Expression}. */ public Identifier [ ] getIdentifiers ( ) { return this.identifiers ; } /** * Returns a list of in this {@link Expression} bound {@link Identifier}s. * * @return A list of in this {@link Expression} bound {@link Identifier}s. */ public ArrayList < ArrayList < Identifier >> getIdentifiersBound ( ) { if ( this.boundIdentifiers == null ) { this.boundIdentifiers = new ArrayList < ArrayList < Identifier >> ( this.identifiers.length + 1 ) ; ArrayList < Identifier > boundExpressionBody = new ArrayList < Identifier > ( ) ; boundExpressionBody .addAll ( this.expressions [ 1 ].getIdentifiersFree ( ) ) ; for ( int i = 0 ; i < this.identifiers.length ; i ++ ) { ArrayList < Identifier > boundIdList = new ArrayList < Identifier > ( ) ; for ( Identifier freeId : boundExpressionBody ) { if ( this.identifiers [ i ].equals ( freeId ) ) { freeId.setBoundTo ( this , this.identifiers [ i ] ) ; freeId.setSet ( Identifier.Set.ATTRIBUTE ) ; boundIdList.add ( freeId ) ; } } this.boundIdentifiers.add ( boundIdList ) ; } } return this.boundIdentifiers ; } /** * {@inheritDoc} */ @ Override public ArrayList < Identifier > getIdentifiersFree ( ) { if ( this.identifiersFree == null ) { this.identifiersFree = new ArrayList < Identifier > ( ) ; this.identifiersFree.add ( new Identifier ( SELF , Identifier.Set.SELF ) ) ; this.identifiersFree.addAll ( this.expressions [ 0 ] .getIdentifiersFree ( ) ) ; ArrayList < Identifier > freeB = new ArrayList < Identifier > ( ) ; freeB.addAll ( this.expressions [ 1 ].getIdentifiersFree ( ) ) ; for ( Identifier a : this.identifiers ) { while ( freeB.remove ( a ) ) { // Remove all Identifiers with the same name } } this.identifiersFree.addAll ( freeB ) ; } 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 prefix of this {@link Expression}. * * @return The prefix of this {@link Expression}. * @see #prefix */ @ Override public String getPrefix ( ) { if ( this.prefix == null ) { this.prefix = PREFIX_BODY ; } return this.prefix ; } /** * {@inheritDoc} */ @ Override public int hashCode ( ) { return this.identifiers.hashCode ( ) + this.expressions.hashCode ( ) ; } /** * {@inheritDoc} */ @ Override public Inherit substitute ( Identifier pId , Expression pExpression ) { if ( pExpression.getIdentifierFreeNotOnlyVariable ( ) ) { throw new NotOnlyFreeVariableException ( ) ; } /* * Do not substitute, if the Identifiers are equal. */ boolean substituteBody = true ; for ( Identifier a : this.identifiers ) { if ( pId.equals ( a ) ) { substituteBody = false ; break ; } } /* * Perform the substitution. */ Expression newE = this.expressions [ 0 ].substitute ( pId , pExpression ) ; Expression newBody ; if ( substituteBody ) { newBody = this.expressions [ 1 ].substitute ( pId , pExpression ) ; } else { newBody = this.expressions [ 1 ] ; } return new Inherit ( this.identifiers , newE , newBody ) ; } /** * {@inheritDoc} * * @see Expression#substitute(TypeSubstitution) */ @ Override public Inherit substitute ( TypeSubstitution pTypeSubstitution ) { Expression newE = this.expressions [ 0 ].substitute ( pTypeSubstitution ) ; Expression newBody = this.expressions [ 1 ].substitute ( pTypeSubstitution ) ; return new Inherit ( this.identifiers , newE , newBody ) ; } /** * {@inheritDoc} * * @see Expression#toLatexStringBuilder(LatexStringBuilderFactory,int) */ @ Override public LatexStringBuilder toLatexStringBuilder ( LatexStringBuilderFactory pLatexStringBuilderFactory , int pIndent ) { StringBuilder identifier = new StringBuilder ( ) ; for ( int i = 0 ; i < this.identifiers.length ; i ++ ) { identifier .append ( this.identifiers [ i ].toPrettyString ( ).toString ( ) ) ; if ( i != this.identifiers.length - 1 ) { identifier.append ( PRETTY_COMMA ) ; identifier.append ( PRETTY_SPACE ) ; } } String descriptions[] = new String [ 2 + this.identifiers.length + this.expressions.length ] ; descriptions [ 0 ] = this.toPrettyString ( ).toString ( ) ; descriptions [ 1 ] = identifier.toString ( ) ; for ( int i = 0 ; i < this.identifiers.length ; i ++ ) { descriptions [ 2 + i ] = this.identifiers [ i ].toPrettyString ( ) .toString ( ) ; } descriptions [ descriptions.length - 2 ] = this.expressions [ 0 ] .toPrettyString ( ).toString ( ) ; descriptions [ descriptions.length - 1 ] = this.expressions [ 1 ] .toPrettyString ( ).toString ( ) ; LatexStringBuilder builder = pLatexStringBuilderFactory.newBuilder ( PRIO_INHERIT , LATEX_INHERIT , pIndent , descriptions ) ; builder.addBuilderBegin ( ) ; for ( int i = 0 ; i < this.identifiers.length ; i ++ ) { builder.addBuilder ( this.identifiers [ i ].toLatexStringBuilder ( pLatexStringBuilderFactory , pIndent + LATEX_INDENT * 2 ) , PRIO_ID ) ; if ( i != this.identifiers.length - 1 ) { builder.addText ( LATEX_LINE_BREAK_SOURCE_CODE ) ; builder.addText ( DefaultLatexStringBuilder.getIndent ( pIndent + LATEX_INDENT ) + LATEX_COMMA ) ; builder.addText ( LATEX_SPACE ) ; } } builder.addBreak ( ) ; builder.addBuilderEnd ( ) ; builder.addBuilder ( this.expressions [ 0 ].toLatexStringBuilder ( pLatexStringBuilderFactory , pIndent + LATEX_INDENT ) , PRIO_INHERIT_E ) ; builder.addBreak ( ) ; builder.addBuilder ( this.expressions [ 1 ].toLatexStringBuilder ( pLatexStringBuilderFactory , pIndent + LATEX_INDENT ) , PRIO_INHERIT_B ) ; return builder ; } /** * {@inheritDoc} * * @see Expression#toPrettyStringBuilder(PrettyStringBuilderFactory) */ @ Override public PrettyStringBuilder toPrettyStringBuilder ( PrettyStringBuilderFactory pPrettyStringBuilderFactory ) { if ( this.prettyStringBuilder == null ) { this.prettyStringBuilder = pPrettyStringBuilderFactory.newBuilder ( this , PRIO_INHERIT ) ; this.prettyStringBuilder.addKeyword ( PRETTY_INHERIT ) ; this.prettyStringBuilder.addText ( PRETTY_SPACE ) ; for ( int i = 0 ; i < this.identifiers.length ; i ++ ) { this.prettyStringBuilder.addBuilder ( this.identifiers [ i ] .toPrettyStringBuilder ( pPrettyStringBuilderFactory ) , PRIO_ID ) ; if ( i != this.identifiers.length - 1 ) { this.prettyStringBuilder.addText ( PRETTY_COMMA ) ; this.prettyStringBuilder.addText ( PRETTY_SPACE ) ; } } this.prettyStringBuilder.addText ( PRETTY_SPACE ) ; this.prettyStringBuilder.addBreak ( ) ; this.prettyStringBuilder.addKeyword ( PRETTY_FROM ) ; this.prettyStringBuilder.addText ( PRETTY_SPACE ) ; this.prettyStringBuilder.addBuilder ( this.expressions [ 0 ] .toPrettyStringBuilder ( pPrettyStringBuilderFactory ) , PRIO_INHERIT_E ) ; this.prettyStringBuilder.addText ( PRETTY_SPACE ) ; this.prettyStringBuilder.addText ( PRETTY_SEMI ) ; this.prettyStringBuilder.addText ( PRETTY_SPACE ) ; this.prettyStringBuilder.addBreak ( ) ; this.prettyStringBuilder.addBuilder ( this.expressions [ 1 ] .toPrettyStringBuilder ( pPrettyStringBuilderFactory ) , PRIO_INHERIT_B ) ; } return this.prettyStringBuilder ; } }