text:
package de.opitzconsulting.orcas.diff;

import static de.opitzconsulting.origOrcasDsl.OrigOrcasDslPackage.Literals.*;

import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EStructuralFeature;

import de.opitzconsulting.orcas.diff.DiffAction.DiffReasonType;
import de.opitzconsulting.orcas.diff.DiffReasonKey.DiffReasonKeyRegistry;
import de.opitzconsulting.orcas.diff.RecreateNeededBuilder.Difference;
import de.opitzconsulting.orcas.diff.RecreateNeededBuilder.DifferenceImpl;
import de.opitzconsulting.orcas.diff.RecreateNeededBuilder.DifferenceImplAttributeOnly;
import de.opitzconsulting.orcas.orig.diff.AbstractDiff;
import de.opitzconsulting.orcas.orig.diff.ColumnDiff;
import de.opitzconsulting.orcas.orig.diff.ColumnRefDiff;
import de.opitzconsulting.orcas.orig.diff.ConstraintDiff;
import de.opitzconsulting.orcas.orig.diff.ForeignKeyDiff;
import de.opitzconsulting.orcas.orig.diff.IndexDiff;
import de.opitzconsulting.orcas.orig.diff.IndexOrUniqueKeyDiff;
import de.opitzconsulting.orcas.orig.diff.InlineCommentDiff;
import de.opitzconsulting.orcas.orig.diff.ModelDiff;
import de.opitzconsulting.orcas.orig.diff.MviewDiff;
import de.opitzconsulting.orcas.orig.diff.SequenceDiff;
import de.opitzconsulting.orcas.orig.diff.TableDiff;
import de.opitzconsulting.orcas.orig.diff.UniqueKeyDiff;
import de.opitzconsulting.orcas.sql.CallableStatementProvider;
import de.opitzconsulting.orcas.sql.WrapperReturnFirstValue;
import de.opitzconsulting.orcas.sql.WrapperReturnValueFromResultSet;
import de.opitzconsulting.origOrcasDsl.Model;

public class OrcasDiff
{
  private static Log _log = LogFactory.getLog( OrcasDiff.class );

  private Parameters _parameters;
  private RecreateNeededRegistry recreateNeededRegistry;
  private List<AbstractDiff> implicitDropList;
  private DiffReasonKeyRegistry diffReasonKeyRegistry;
  private DdlBuilder ddlBuilder;
  private List<DiffAction> diffActions = new ArrayList<DiffAction>();
  private DiffAction activeDiffAction;
  private DataHandler dataHandler;
  private AlterTableCombiner currentAlterTableCombiner;
  private Map<String, List<String>> oldContraintNames = new HashMap<>();
  private List<String> oldObjectNames = new ArrayList<>();
  private Map<String, List<String>> newContraintNames = new HashMap<>();
  private List<String> newObjectNames = new ArrayList<>();
  private DatabaseHandler databaseHandler;

  public OrcasDiff( CallableStatementProvider pCallableStatementProvider, Parameters pParameters, DatabaseHandler pDatabaseHandler )
  {
    _parameters = pParameters;

    dataHandler = new DataHandler( pCallableStatementProvider, pParameters );

    ddlBuilder = pDatabaseHandler.createDdlBuilder( pParameters );
    databaseHandler = pDatabaseHandler;
  }

  private List<DiffActionReason> getIndexRecreate( TableDiff pTableDiff, String pIndexname )
  {
    for( IndexOrUniqueKeyDiff lIndexOrUniqueKeyDiff : pTableDiff.ind_uksIndexDiff )
    {
      if( lIndexOrUniqueKeyDiff.consNameNew.equals( pIndexname ) )
      {
        if( isRecreateNeeded( lIndexOrUniqueKeyDiff ) )
        {
          return recreateNeededRegistry.getRecreateNeededReasons( lIndexOrUniqueKeyDiff );
        }
        else
        {
          return Collections.emptyList();
        }
      }
    }

    throw new RuntimeException( "index not found: " + pIndexname + " " + pTableDiff.nameNew );
  }

  private List<Difference> isRecreateColumn( ColumnDiff pColumnDiff )
  {
    List<Difference> lReturn = new ArrayList<>();

    if( pColumnDiff.data_typeNew != null && pColumnDiff.data_typeOld != null )
    {
      if( !pColumnDiff.data_typeIsEqual )
      {
        lReturn.add(new DifferenceImpl(COLUMN__DATA_TYPE, pColumnDiff));
      }

      if( isLessTahnOrNull( pColumnDiff.precisionNew, pColumnDiff.precisionOld ) )
      {
        lReturn.add(new DifferenceImpl(COLUMN__PRECISION, pColumnDiff));
      }

      if( isLessTahnOrNull( pColumnDiff.scaleNew, pColumnDiff.scaleOld ) )
      {
        lReturn.add(new DifferenceImpl(COLUMN__SCALE, pColumnDiff));
      }
    }

    if( !pColumnDiff.object_typeIsEqual )
    {
      lReturn.add(new DifferenceImpl(COLUMN__OBJECT_TYPE, pColumnDiff));
    }

    if( !pColumnDiff.unsignedIsEqual )
    {
      lReturn.add(new DifferenceImpl(COLUMN__UNSIGNED, pColumnDiff));
    }

    if( !pColumnDiff.virtualIsEqual )
    {
      lReturn.add(new DifferenceImpl(COLUMN__VIRTUAL, pColumnDiff));
    }

    return lReturn;
  }

  private boolean isLessTahnOrNull( Integer pValue1, Integer pValue2 )
  {
    if( pValue1 == null && pValue2 == null )
    {
      return false;
    }

    if( pValue1 == null || pValue2 == null )
    {
      return true;
    }

    return pValue1 < pValue2;
  }

  private void loadNames( ModelDiff pModelDiff, boolean pIsNew )
  {
    Map<String, List<String>> lContraintNames;
    List<String> lObjectNames;
    Predicate<AbstractDiff> lPredicate;

    if( pIsNew )
    {
      lPredicate = p -> p.isNew;
      lContraintNames = newContraintNames;
      lObjectNames = newObjectNames;
    }
    else
    {
      lPredicate = p -> p.isOld;
      lContraintNames = oldContraintNames;
      lObjectNames = oldObjectNames;
    }

    pModelDiff.model_elementsTableDiff//
    .stream()//
    .filter( lPredicate )//
    .forEach( lTableDiff ->
    {
      String lTableName = (String) lTableDiff.getValue( TABLE__NAME, pIsNew );
      lObjectNames.add( lTableName );

      Consumer<String> lContraintNameHandler = p ->
      {
        List<String> lContraintNameList = lContraintNames.get( lTableName );

        if( lContraintNameList == null )
        {
          lContraintNameList = new ArrayList<>();
          lContraintNames.put( lTableName, lContraintNameList );
        }

        lContraintNameList.add( p );
      };

      Optional.of( lTableDiff.primary_keyDiff )//
      .filter( lPredicate )//
      .map( getNameFunction( PRIMARY_KEY__CONS_NAME, pIsNew ) )//
      .ifPresent( lContraintNameHandler );

      lTableDiff.ind_uksIndexDiff//
      .stream()//
      .filter( lPredicate )//
      .map( getNameFunction( INDEX_OR_UNIQUE_KEY__CONS_NAME, pIsNew ) )//
      .forEach( lObjectNames::add );

      lTableDiff.ind_uksUniqueKeyDiff//
      .stream()//
      .filter( lPredicate )//
      .map( getNameFunction( INDEX_OR_UNIQUE_KEY__CONS_NAME, pIsNew ) )//
      .forEach( lContraintNameHandler );

      lTableDiff.constraintsDiff//
      .stream()//
      .filter( lPredicate )//
      .map( getNameFunction( CONSTRAINT__CONS_NAME, pIsNew ) )//
      .forEach( lContraintNameHandler );

      lTableDiff.foreign_keysDiff//
      .stream()//
      .filter( lPredicate )//
      .map( getNameFunction( FOREIGN_KEY__CONS_NAME, pIsNew ) )//
      .forEach( lContraintNameHandler );
    } );

    pModelDiff.model_elementsMviewDiff//
    .stream()//
    .filter( lPredicate )//
    .map( getNameFunction( MVIEW__MVIEW_NAME, pIsNew ) )//
    .forEach( lObjectNames::add );

    pModelDiff.model_elementsSequenceDiff//
    .stream()//
    .filter( lPredicate )//
    .map( getNameFunction( SEQUENCE__SEQUENCE_NAME, pIsNew ) )//
    .forEach( lObjectNames::add );
  }

  private Function<? super AbstractDiff, ? extends String> getNameFunction( EAttribute pAttribute, boolean pIsNew )
  {
    Function<? super AbstractDiff, ? extends String> lFunction = p -> (String) p.getValue( pAttribute, pIsNew );
    return lFunction;
  }

  private void updateIsRecreateNeeded( ModelDiff pModelDiff )
  {
    Map<String,List<DiffActionReason>> recreateMviewMap = new HashMap<>();

    for( TableDiff lTableDiff : pModelDiff.model_elementsTableDiff )
    {
      if( lTableDiff.isMatched )
      {
        setRecreateNeededFor( lTableDiff )//
        .ifDifferent( TABLE__PERMANENTNESS )//
        .ifDifferent( TABLE__TRANSACTION_CONTROL )//
        .calculate();

        if( recreateNeededRegistry.isRecreateNeeded(lTableDiff) ){
          recreateMviewMap.computeIfAbsent(lTableDiff.nameNew, p->new ArrayList<>()).addAll(recreateNeededRegistry.getRecreateNeededReasons(lTableDiff));
        }

        Map<String, List<DiffActionReason>> lRecreateColumnNames = new HashMap<>();

        for( ColumnDiff lColumnDiff : lTableDiff.columnsDiff )
        {
          setRecreateNeededFor( lColumnDiff )//
          .ifX( p ->
          {
            List<Difference> lRecreateColumn = isRecreateColumn( p.getDiff() );
            if( !lRecreateColumn.isEmpty() )
            {
              p.setRecreateNeededDifferent( lRecreateColumn );
              lRecreateColumnNames.put( lColumnDiff.nameOld, recreateNeededRegistry.getRecreateNeededReasons( lColumnDiff ) );
            }
            else {
              List<Difference>
                  lChangeVirtualColumn =
                  RecreateNeededBuilder.getDifferentEAttributes(lColumnDiff, Stream.of(COLUMN__IDENTITY, COLUMN__DEFAULT_VALUE).collect(
                      Collectors.toList()));
              if (lColumnDiff.isOld && (lColumnDiff.virtualOld != null || !lColumnDiff.virtualIsEqual) && !lChangeVirtualColumn.isEmpty()) {
                DiffActionReasonDifferent
                    lDiffActionReasonDifferent =
                    new DiffActionReasonDifferent(diffReasonKeyRegistry.getDiffReasonKey(lColumnDiff));
                lChangeVirtualColumn.forEach(p1 -> lDiffActionReasonDifferent.addDiffReasonDetail(p1));
                lRecreateColumnNames.put(
                    lColumnDiff.nameOld,
                    Collections.singletonList(lDiffActionReasonDifferent));
              }
            }
          } )//
          .calculate();

          if( recreateNeededRegistry.isRecreateNeeded(lColumnDiff) ){
            recreateMviewMap.computeIfAbsent(lTableDiff.nameNew, p->new ArrayList<>()).addAll(recreateNeededRegistry.getRecreateNeededReasons(lColumnDiff));
          }

          if( !lColumnDiff.isMatched) {
            if (lColumnDiff.isNew) {
              recreateMviewMap
                  .computeIfAbsent(lTableDiff.nameNew, p -> new ArrayList<>())
                  .addAll(Collections.singletonList(new DiffActionReasonMissing(diffReasonKeyRegistry.getDiffReasonKey(lColumnDiff))));
            } else {
              recreateMviewMap
                  .computeIfAbsent(lTableDiff.nameNew, p -> new ArrayList<>())
                  .addAll(Collections.singletonList(new DiffActionReasonSurplus(diffReasonKeyRegistry.getDiffReasonKey(lColumnDiff))));
            }
          }
        }

        setRecreateNeededFor( lTableDiff.primary_keyDiff )//
        .ifDifferentName( PRIMARY_KEY__CONS_NAME, oldContraintNames, lTableDiff.primary_keyDiff.consNameNew, lTableDiff.primary_keyDiff.consNameOld, databaseHandler.isRenamePrimaryKey() )//
        .ifDifferent( PRIMARY_KEY__PK_COLUMNS )//
        .ifDifferent( PRIMARY_KEY__REVERSE )//
        .ifDifferent( PRIMARY_KEY__INDEXNAME )//
        .ifDifferent( PRIMARY_KEY__TABLESPACE, _parameters.isIndexmovetablespace() )//
        .ifColumnDependentRecreate( lRecreateColumnNames, lTableDiff.primary_keyDiff.pk_columnsDiff )//
        .calculate();

        for( IndexDiff lIndexDiff : lTableDiff.ind_uksIndexDiff )
        {
          // domain index cant be compared
          boolean lNoDomainIndex = lIndexDiff.domain_index_expressionNew == null;

          // prevent recreate if column-list equals function based expression
          boolean lExpressionDifferent = !Objects.equals(ddlBuilder.getIndexExpression(lIndexDiff,true),ddlBuilder.getIndexExpression(lIndexDiff,false));

          setRecreateNeededFor( lIndexDiff )//
          .ifDifferentName( INDEX_OR_UNIQUE_KEY__CONS_NAME, oldObjectNames, lIndexDiff.consNameNew, lIndexDiff.consNameOld, databaseHandler.isRenameIndex() )//
          .ifDifferent( INDEX__INDEX_COLUMNS, lNoDomainIndex && lExpressionDifferent )//
          .ifDifferent( INDEX__FUNCTION_BASED_EXPRESSION, lNoDomainIndex && lExpressionDifferent )//
          .ifDifferent( INDEX__DOMAIN_INDEX_EXPRESSION, lNoDomainIndex )//
          .ifDifferent( INDEX__UNIQUE )//
          .ifDifferent( INDEX__BITMAP )//
          .ifDifferent( INDEX__GLOBAL )//
          .ifDifferent( INDEX__COMPRESSION )//
          .ifColumnDependentRecreate( lRecreateColumnNames, lIndexDiff.index_columnsDiff )//
          .calculate();
        }

        for( UniqueKeyDiff lUniqueKeyDiff : lTableDiff.ind_uksUniqueKeyDiff )
        {
          setRecreateNeededFor( lUniqueKeyDiff )//
          .ifDifferentName( INDEX_OR_UNIQUE_KEY__CONS_NAME, oldContraintNames, lUniqueKeyDiff.consNameNew, lUniqueKeyDiff.consNameOld, databaseHandler.isRenameUniqueKey() )//
          .ifDifferent( UNIQUE_KEY__UK_COLUMNS )//
          .ifDifferent( UNIQUE_KEY__INDEXNAME )//
          .ifDifferent( INDEX_OR_UNIQUE_KEY__TABLESPACE, _parameters.isIndexmovetablespace() )//
          .ifColumnDependentRecreate( lRecreateColumnNames, lUniqueKeyDiff.uk_columnsDiff )//
          .ifX( p ->
          {
            if( lUniqueKeyDiff.indexnameNew != null )
            {
              List<DiffActionReason> lIndexRecreate = getIndexRecreate( lTableDiff, lUniqueKeyDiff.indexnameNew );

              if( !lIndexRecreate.isEmpty() )
              {
                p.setRecreateNeededDependsOn( lIndexRecreate );
              }
            }
          } ).calculate();
        }

        for( ConstraintDiff lConstraintDiff : lTableDiff.constraintsDiff )
        {
          setRecreateNeededFor( lConstraintDiff )//
          .ifDifferentName( CONSTRAINT__CONS_NAME, oldContraintNames, lConstraintDiff.consNameNew, lConstraintDiff.consNameOld, databaseHandler.isRenameConstraint() )//
          .ifDifferent( CONSTRAINT__RULE, databaseHandler.isExpressionDifferent(lConstraintDiff.ruleNew,lConstraintDiff.ruleOld) )//
          .ifDifferent( CONSTRAINT__DEFERRTYPE )//
          .calculate();
        }

        for( ForeignKeyDiff lForeignKeyDiff : lTableDiff.foreign_keysDiff )
        {
          setRecreateNeededFor( lForeignKeyDiff )//
          .ifDifferentName( FOREIGN_KEY__CONS_NAME, oldContraintNames, lForeignKeyDiff.consNameNew, lForeignKeyDiff.consNameOld, databaseHandler.isRenameForeignKey() )//
          .ifDifferent( FOREIGN_KEY__DEFERRTYPE )//
          .ifDifferent( FOREIGN_KEY__DELETE_RULE )//
          .ifDifferent( FOREIGN_KEY__DEST_COLUMNS )//
          .ifDifferent( FOREIGN_KEY__DEST_TABLE )//
          .ifDifferent( FOREIGN_KEY__SRC_COLUMNS )//
          //.ifDifferent( FOREIGN_KEY__STATUS )//
          .ifColumnDependentRecreate( lRecreateColumnNames, lForeignKeyDiff.srcColumnsDiff )//
          .calculate();
        }

        setRecreateNeededFor( lTableDiff.mviewLogDiff )//
        .ifDifferent( MVIEW_LOG__COLUMNS )//
        .ifDifferent( MVIEW_LOG__PRIMARY_KEY, "rowid".equalsIgnoreCase( lTableDiff.mviewLogDiff.rowidNew ) )//
        .ifX( p ->
        {
          if( lTableDiff.mviewLogDiff.rowidNew == null && !"primary".equalsIgnoreCase( lTableDiff.mviewLogDiff.primaryKeyOld ) )
          {
            p.setRecreateNeededDifferentAttributes( Collections.singletonList( MVIEW_LOG__PRIMARY_KEY ) );
          }
        } )//
        .ifDifferent( MVIEW_LOG__ROWID )//
        .ifDifferent( MVIEW_LOG__WITH_SEQUENCE )//
        .ifDifferent( MVIEW_LOG__COMMIT_SCN )//
        .ifDifferent( MVIEW_LOG__TABLESPACE, _parameters.isMviewlogmovetablespace() )//
        // these changes should by applied with alter statements, but it results
        // in ORA-27476
        .ifDifferent( MVIEW_LOG__START_WITH )//
        .ifDifferent( MVIEW_LOG__NEXT )//
        .ifDifferent( MVIEW_LOG__REPEAT_INTERVAL )//
        .calculate();

        for( InlineCommentDiff lCommentDiff : lTableDiff.commentsDiff )
        {
          if( lCommentDiff.column_nameOld != null )
          {
            setRecreateNeededFor( lCommentDiff )//
            .ifColumnDependentRecreate( lRecreateColumnNames, lCommentDiff.column_nameOld )//
            .calculate();
          }
        }
      }
    }

    for( MviewDiff lMviewDiff : pModelDiff.model_elementsMviewDiff )
    {
      setRecreateNeededFor( lMviewDiff )//
      .ifDifferentName( MVIEW__MVIEW_NAME, oldObjectNames, lMviewDiff.mview_nameNew, lMviewDiff.mview_nameOld, databaseHandler.isRenameMView() )//
      .ifDifferent( MVIEW__TABLESPACE )//
      .ifDifferent( MVIEW__BUILD_MODE )//
      .ifDifferent( MVIEW__MVIEW_COLUMNS, ddlBuilder.getColumnList( lMviewDiff.mview_columnsDiff ) != null )//
      .ifX( p ->
      {
        if( !replaceLinefeedBySpace( lMviewDiff.viewSelectCLOBNew ).equals( replaceLinefeedBySpace( lMviewDiff.viewSelectCLOBOld ) ) )
        {
          p.setRecreateNeededDifferent( Collections.singletonList( new DifferenceImpl( MVIEW__VIEW_SELECT_CLOB, lMviewDiff ) ) );
        }
      } )//
      .ifX( p ->
      {
        if( recreateMviewMap.containsKey( lMviewDiff.mview_nameNew ) )
        {
          p.setRecreateNeededDependsOn( recreateMviewMap.get( lMviewDiff.mview_nameNew ) );
        }
      } )//
      .calculate();
    }

    for( TableDiff lTableDiff : pModelDiff.model_elementsTableDiff )
    {
      for( ForeignKeyDiff lForeignKeyDiff : lTableDiff.foreign_keysDiff )
      {
        setRecreateNeededFor( lForeignKeyDiff )//
        .ifX( p ->
        {
          List<DiffActionReason> lRefConstraintRecreate = getRefConstraintRecreate( pModelDiff, lForeignKeyDiff.destTableOld, lForeignKeyDiff.destColumnsDiff );

          if( !lRefConstraintRecreate.isEmpty() )
          {
            p.setRecreateNeededDependsOn( lRefConstraintRecreate );
          }
        } )//
        .calculate();
      }
    }
  }

  private <T extends AbstractDiff> RecreateNeededBuilder<T> setRecreateNeededFor( T pDiff )
  {
    return recreateNeededRegistry.createRecreateNeededBuilder( pDiff );
  }

  private Optional<TableDiff> findTableDiffByOldName( ModelDiff pModelDiff, String pTableName )
  {
    return pModelDiff.model_elementsTableDiff.stream()//
    .filter( p -> p.isOld )//
    .filter( p -> p.nameOld.equals( pTableName ) )//
    .findAny();
  }

  private Optional<TableDiff> findTableDiffByNewName( ModelDiff pModelDiff, String pTableName )
  {
    return pModelDiff.model_elementsTableDiff.stream()//
                                             .filter( p -> p.isNew )//
                                             .filter( p -> p.nameNew.equals( pTableName ) )//
                                             .findAny();
  }

  private List<DiffActionReason> getRefConstraintRecreate( ModelDiff pModelDiff, String pDestTableName, List<ColumnRefDiff> pDestColumnsDiff )
  {
    List<DiffActionReason> lReturn = new ArrayList<>();

    for( TableDiff lTableDiff : pModelDiff.model_elementsTableDiff )
    {
      if( lTableDiff.isMatched && lTableDiff.nameOld.equals( pDestTableName ) )
      {
        if( isRecreateNeeded( lTableDiff ) )
        {
          lReturn.addAll( recreateNeededRegistry.getRecreateNeededReasons( lTableDiff ) );
        }

        if( isRecreateNeeded( lTableDiff.primary_keyDiff ) )
        {
          if( isOldColumnNamesEqual( pDestColumnsDiff, lTableDiff.primary_keyDiff.pk_columnsDiff ) )
          {
            lReturn.addAll( recreateNeededRegistry.getRecreateNeededReasons( lTableDiff.primary_keyDiff ) );
          }
        }

        for( UniqueKeyDiff lUniqueKeyDiff : lTableDiff.ind_uksUniqueKeyDiff )
        {
          if( isRecreateNeeded( lUniqueKeyDiff ) )
          {
            if( isOldColumnNamesEqual( pDestColumnsDiff, lUniqueKeyDiff.uk_columnsDiff ) )
            {
              lReturn.addAll( recreateNeededRegistry.getRecreateNeededReasons( lUniqueKeyDiff ) );
            }
          }
        }
      }
    }

    return lReturn;
  }

  private boolean isRecreateNeeded( AbstractDiff pDiff )
  {
    return recreateNeededRegistry.isRecreateNeeded( pDiff );
  }

  private boolean isOldColumnNamesEqual( List<ColumnRefDiff> pColumnRefDiffList1, List<ColumnRefDiff> pColumnRefDiffList2 )
  {
    return getOldColumnNames( pColumnRefDiffList1 ).equals( getOldColumnNames( pColumnRefDiffList2 ) );
  }

  private String getOldColumnNames( List<ColumnRefDiff> pColumnRefDiffList )
  {
    String lReturn = "";

    for( ColumnRefDiff lColumnRefDiff : pColumnRefDiffList )
    {
      if( lColumnRefDiff.isOld )
      {
        lReturn += lColumnRefDiff.column_nameOld + ",";
      }
    }

    return lReturn;
  }

  private String replaceLinefeedBySpace( String pValue )
  {
    return pValue.replace( Character.toString( (char) 13 ) + Character.toString( (char) 10 ), "" ).replace( Character.toString( (char) 10 ), "" ).replace( Character.toString( (char) 13 ), "" );
  }

  public DiffResult compare( Model pModelSoll, Model pModelIst )
  {
    _log.debug( "build generic diff" );
    ModelDiff lModelDiff = new ModelDiff( pModelSoll );
    lModelDiff.mergeWithOldValue( pModelIst );

    _log.debug( "sort for ref part" );
    sortTablesForRefPart( lModelDiff );

    diffReasonKeyRegistry = new DiffReasonKey.DiffReasonKeyRegistry( lModelDiff );
    recreateNeededRegistry = new RecreateNeededRegistry( diffReasonKeyRegistry );

    _log.debug( "update recreate" );
    loadNames( lModelDiff, true );
    loadNames( lModelDiff, false );
    updateIsRecreateNeeded( lModelDiff );

    implicitDropList = new ArrayList<>();
    if( _parameters.isMinimizeStatementCount() )
    {
      updateImplicitDropList( lModelDiff );
    }

    _log.debug( "handle all mviews drop" );
    handleAllMviews( lModelDiff, true );

    _log.debug( "handle all tables" );
    handleAllTables( lModelDiff );

    _log.debug( "handle all sequences" );
    handleAllSequences( lModelDiff );

    _log.debug( "handle all mviews" );
    handleAllMviews( lModelDiff, false );

    if( _parameters.isAdditionsOnly() )
    {
      // diffActions = diffActions.stream().filter( p -> p.getDiffReasonType()
      // == DiffReasonType.CREATE ).collect( Collectors.toList() );
    }

    return new DiffResult( diffActions );
  }

  private void updateImplicitDropList( ModelDiff pModelDiff )
  {
    if( _parameters.isAdditionsOnly() )
    {
      return;
    }

    pModelDiff.model_elementsTableDiff.stream()//
    .filter( p -> p.isOld )//
    .forEach( pTableDiff ->
    {
      pTableDiff.constraintsDiff.stream()//
      .filter( p -> p.isOld )//
      .filter( pDiff -> ddlBuilder.isContainsOnlyDropColumns( pTableDiff, pDiff.ruleOld ) )//
      .forEach( implicitDropList::add );

      Optional.ofNullable( pTableDiff.primary_keyDiff )//
      .filter( p -> p.isOld )//
      .filter( pDiff -> ddlBuilder.isAllColumnsOnlyOld( pTableDiff, pDiff.pk_columnsDiff ) )//
      .ifPresent( implicitDropList::add );

      pTableDiff.ind_uksUniqueKeyDiff.stream()//
      .filter( p -> p.isOld )//
      .filter( pDiff -> ddlBuilder.isAllColumnsOnlyOld( pTableDiff, pDiff.uk_columnsDiff ) )//
      .forEach( implicitDropList::add );

      pTableDiff.ind_uksIndexDiff.stream()//
      .filter( p -> p.isOld )//
      .filter( p -> p.function_based_expressionOld == null )//
      .filter( pDiff -> ddlBuilder.isAllColumnsOnlyOld( pTableDiff, pDiff.index_columnsDiff ) )//
      .forEach( implicitDropList::add );

      pTableDiff.foreign_keysDiff.stream()//
      .filter( p -> p.isOld )//
      .filter( pDiff ->
      {
        if( !ddlBuilder.isAllColumnsOnlyOld( pTableDiff, pDiff.srcColumnsDiff ) )
        {
          return false;
        }

        Optional<TableDiff> lDestTableDiff = findTableDiffByOldName( pModelDiff, pDiff.destTableOld );
        if( lDestTableDiff.isPresent() )
        {
          return ddlBuilder.isAllColumnsNew( pDiff.destColumnsDiff, lDestTableDiff.get() );
        }

        return true;
      } )//
      .forEach( implicitDropList::add );
    } );
  }

  public static class DiffResult
  {
    private List<DiffAction> diffActions = new ArrayList<DiffAction>();

    public List<DiffAction> getDiffActions()
    {
      return diffActions;
    }

    public DiffResult( List<DiffAction> pDiffActions )
    {
      diffActions.addAll( pDiffActions );
    }
  }

  private void handleAllSequences( ModelDiff pModelDiff )
  {
    for( SequenceDiff lSequenceDiff : pModelDiff.model_elementsSequenceDiff )
    {
      dropIfNeeded( lSequenceDiff, p -> ddlBuilder.dropSequence( p, lSequenceDiff ) );

      createIfNeededOrAlter( lSequenceDiff, //
      p1 -> ddlBuilder.createSequnece( p1, lSequenceDiff, dataHandler ), //
      p2 -> ddlBuilder.alterSequenceIfNeeded( p2, lSequenceDiff, dataHandler ) );
    }
  }

  private <T> void doInDiffAction( DiffAction pDiffAction, AbstractDiffActionRunnable<T> pRunnable, T pAbstractStatementBuilder )
  {
    activeDiffAction = pDiffAction;
    pRunnable.accept( pAbstractStatementBuilder );
    addDiffAction( activeDiffAction );
    activeDiffAction = null;
  }

  private void addDiffAction( DiffAction pDiffAction )
  {
    if( !diffActions.contains( pDiffAction ) && !pDiffAction.hasNoStatements() )
    {
      diffActions.add( pDiffAction );
    }
  }

  private <T> void doInDiffAction( DiffAction pDiffAction, List<DiffActionReason> pDiffActionReasonList, AbstractDiffActionRunnable<T> pRunnable, T pAbstractStatementBuilder )
  {
    for( DiffActionReason lDiffActionReason : pDiffActionReasonList )
    {
      pDiffAction.addDiffActionReason( lDiffActionReason );
    }
    doInDiffAction( pDiffAction, pRunnable, pAbstractStatementBuilder );
  }

  private <T extends AbstractStatementBuilder> void doInDiffAction( AbstractDiff pDiff, List<DiffActionReason> pDiffActionReasonList, DiffReasonType pDiffReasonType, AbstractDiffActionRunnable<T> pRunnable, T pAbstractStatementBuilder )
  {
    doInDiffAction( new DiffAction( diffReasonKeyRegistry.getDiffReasonKey( pDiff ), pDiffReasonType ), pDiffActionReasonList, pRunnable, pAbstractStatementBuilder );
  }

  private void doInDiffActionAlter( AbstractDiff pDiff, DiffActionRunnableAlter pRunnable )
  {
    DiffAction lDiffAction = new DiffAction( diffReasonKeyRegistry.getDiffReasonKey( pDiff ), DiffReasonType.ALTER );
    DiffActionReasonDifferent lDiffActionReasonDifferent = new DiffActionReasonDifferent( diffReasonKeyRegistry.getDiffReasonKey( pDiff ) );
    doInDiffAction( lDiffAction, Collections.singletonList( lDiffActionReasonDifferent ), pRunnable, new StatementBuilderAlter( lDiffActionReasonDifferent, pDiff, _parameters.isAdditionsOnly(), () -> activeDiffAction, currentAlterTableCombiner ) );
  }

  private void doInDiffActionDrop( AbstractDiff pDiff, DiffActionRunnable pRunnable )
  {
    boolean lRecreateNeeded = isRecreateNeeded( pDiff );
    DiffAction lDiffAction = new DiffAction( diffReasonKeyRegistry.getDiffReasonKey( pDiff ), lRecreateNeeded ? DiffReasonType.RECREATE_DROP : DiffReasonType.DROP );

    doInDiffAction( lDiffAction, lRecreateNeeded ? recreateNeededRegistry.getRecreateNeededReasons( pDiff ) : Collections.singletonList( new DiffActionReasonSurplus( diffReasonKeyRegistry.getDiffReasonKey( pDiff ) ) ), pRunnable, createStatementBuilder() );
  }

  private void doInDiffActionCreate( AbstractDiff pDiff, DiffActionRunnable pRunnable )
  {
    boolean lRecreateNeeded = isRecreateNeeded( pDiff );
    DiffAction lDiffAction = new DiffAction( diffReasonKeyRegistry.getDiffReasonKey( pDiff ), lRecreateNeeded ? DiffReasonType.RECREATE_CREATE : DiffReasonType.CREATE );

    doInDiffAction( lDiffAction, lRecreateNeeded ? recreateNeededRegistry.getRecreateNeededReasons( pDiff ) : Collections.singletonList( new DiffActionReasonMissing( diffReasonKeyRegistry.getDiffReasonKey( pDiff ) ) ), pRunnable, createStatementBuilder() );
  }

  private void dropIfNeeded( AbstractDiff pDiff, DiffActionRunnable pRunnable )
  {
    dropIfNeeded( pDiff, pRunnable, null );
  }

  private void dropIfNeeded( AbstractDiff pDiff, DiffActionRunnable pRunnable, Runnable pNoDropHandler )
  {
    if( pDiff.isOld && (pDiff.isMatched == false || isRecreateNeeded( pDiff )) )
    {
      if( !implicitDropList.contains( pDiff ) )
      {
        doInDiffActionDrop( pDiff, pRunnable );
      }
    }
    else
    {
      if( pNoDropHandler != null )
      {
        pNoDropHandler.run();
      }
    }
  }

  private <T extends AbstractDiff> void dropIfNeeded( List<T> pDiffList, DiffActionRunnableWithDiff<T> pRunnable )
  {
    for( T lDiff : pDiffList )
    {
      dropIfNeeded( lDiff, p ->
      {
        pRunnable.accept( new StatementBuilderWithDiff<>( () -> activeDiffAction, _parameters.isAdditionsOnly(), lDiff, currentAlterTableCombiner ) );
      } );
    }
  }

  private void runInAlterTableCombiner( Runnable pRunnable )
  {
    if( _parameters.isMinimizeStatementCount() )
    {
      currentAlterTableCombiner = new AlterTableCombiner( diffReasonKeyRegistry, this::addDiffAction );
    }

    pRunnable.run();

    if( currentAlterTableCombiner != null )
    {
      currentAlterTableCombiner.finishIfNeeded();
      currentAlterTableCombiner = null;
    }
  }

  private void handleAllTables( ModelDiff pModelDiff )
  {
    for( TableDiff lTableDiff : pModelDiff.model_elementsTableDiff )
    {
      runInAlterTableCombiner( () -> dropIfNeeded( lTableDiff.foreign_keysDiff, p -> ddlBuilder.dropForeignKey( p, lTableDiff, p.diff ) ) );
    }

    for( TableDiff lTableDiff : pModelDiff.model_elementsTableDiff )
    {
      dropIfNeeded( lTableDiff, p -> ddlBuilder.dropTable( p, lTableDiff, dataHandler ), null );
    }

    pModelDiff.model_elementsTableDiff//
    .stream()//
    .filter( p -> p.isNew )//
    .filter( p -> p.isOld )//
    .filter( p -> !isRecreateNeeded( p ) )//
    .forEach( pTableDiff ->
    {
      runInAlterTableCombiner( () ->
      {
        handleTableCleanupTableDetails( pTableDiff, true );
      } );
    } );

    pModelDiff.model_elementsTableDiff//
    .stream()//
    .filter( p -> p.isNew )//
    .forEach( pTableDiff ->
    {
      if( !pTableDiff.isOld || isRecreateNeeded( pTableDiff ) )
      {
        runInAlterTableCombiner( () ->
        {
          doInDiffActionCreate( pTableDiff, p -> ddlBuilder.createTable( p, pTableDiff ) );

          handleTableDetailsNoColumns( pTableDiff );
        } );
      }
      else
      {
        runInAlterTableCombiner( () ->
        {
          doInDiffActionAlter( pTableDiff, p -> ddlBuilder.alterTableIfNeeded( p, pTableDiff, _parameters.isTablemovetablespace(), getDefaultTablespace(null,pTableDiff.nameNew,null) ) );

          handleTableCleanupTableDetails( pTableDiff, false );
        } );

        runInAlterTableCombiner( () ->
        {
          handleTableRecreateOrAlterColumns( pTableDiff );
        } );

        runInAlterTableCombiner( () ->
        {
          handleTableCreateColumns( pTableDiff );

          handleTableDetailsNoColumns( pTableDiff );
        } );
      }
    } );

    for( TableDiff lTableDiff : pModelDiff.model_elementsTableDiff )
    {
      runInAlterTableCombiner( () ->
      {
        for( ForeignKeyDiff lForeignKeyDiff : lTableDiff.foreign_keysDiff )
        {
          if( lTableDiff.isOld == false && lTableDiff.tablePartitioningRefPartitionsDiff.isNew && lTableDiff.tablePartitioningRefPartitionsDiff.fkNameNew.equalsIgnoreCase( lForeignKeyDiff.consNameNew ) )
          {
            // fk for ref-partition created in create-table
          }
          else
          {
            if( _parameters.getMultiSchema() ) {
              if( lForeignKeyDiff.isNew )
              {
                if( lForeignKeyDiff.isOld == false  )
                {
                  Optional<TableDiff> lDestTableDiff = findTableDiffByNewName(pModelDiff, lForeignKeyDiff.destTableNew);

                  if (lDestTableDiff.isPresent()) {
                    DiffAction lDiffAction = new DiffAction(new DiffReasonKey(lDestTableDiff.get(), "references"), DiffReasonType.CREATE);

                    doInDiffAction(lDiffAction, Collections.singletonList(new DiffActionReasonMissing(diffReasonKeyRegistry.getDiffReasonKey(
                        lForeignKeyDiff))),
                        (DiffActionRunnable) p -> ddlBuilder.createForeignKeyGrantIfNeeded(p, lTableDiff, lForeignKeyDiff), createStatementBuilder());
                  }
                }
              }
            }

            createIfNeededOrAlter( lForeignKeyDiff, p -> ddlBuilder.createForeignKey( p, lTableDiff, lForeignKeyDiff, dataHandler ), p -> ddlBuilder.alterForeignKeyIfNeeded( p, lTableDiff, lForeignKeyDiff, dataHandler ) );
          }
        }
      } );
    }
  }

  private void handleTableCreateColumns( TableDiff pTableDiff )
  {
    List<ColumnDiff> lCreateColumnDiffList = pTableDiff.columnsDiff.stream()//
    .filter( p -> p.isNew && !p.isOld )//
    .collect( Collectors.toList() );

    if( lCreateColumnDiffList.size() > 1 && _parameters.isMinimizeStatementCount() && ddlBuilder.isMultiCreatePossible( pTableDiff, lCreateColumnDiffList ) )
    {
      DiffAction lDiffAction = new DiffAction( diffReasonKeyRegistry.getDiffReasonKey( pTableDiff ), DiffReasonType.ALTER );
      List<DiffActionReason> lDiffActionReasonList = lCreateColumnDiffList//
      .stream()//
      .map( pColumnDiff -> new DiffActionReasonMissing( diffReasonKeyRegistry.getDiffReasonKey( pColumnDiff ) ) )//
      .collect( Collectors.toList() );

      doInDiffAction( lDiffAction, lDiffActionReasonList, p -> ddlBuilder.createColumns( p, pTableDiff, lCreateColumnDiffList ), createStatementBuilder() );
    }
    else
    {
      lCreateColumnDiffList.forEach( pColumnDiff -> doInDiffActionCreate( pColumnDiff, p -> ddlBuilder.createColumn( p, pTableDiff, pColumnDiff ) ) );
    }
  }

  private void handleTableRecreateOrAlterColumns( TableDiff pTableDiff )
  {
    for( ColumnDiff lColumnDiff : pTableDiff.columnsDiff )
    {
      if( lColumnDiff.isNew && lColumnDiff.isOld )
      {
        if( isRecreateNeeded( lColumnDiff ) )
        {
          doInDiffAction( lColumnDiff, recreateNeededRegistry.getRecreateNeededReasons( lColumnDiff ), DiffReasonType.RECREATE, //
          p -> ddlBuilder.recreateColumn( p, pTableDiff, lColumnDiff ) //
          , createStatementBuilder() );
        }
        else
        {
          doInDiffActionAlter( lColumnDiff, p -> ddlBuilder.alterColumnIfNeeded( p, pTableDiff, lColumnDiff ) );
        }
      }
    }
  }

  private void handleTableDetailsNoColumns( TableDiff pTableDiff )
  {
    if ( pTableDiff.primary_keyDiff.indexnameNew != null )
    {
      pTableDiff.ind_uksIndexDiff.stream()
                                 .filter( p -> p.isNew )
                                 .filter( p -> p.consNameNew.toLowerCase().equals( pTableDiff.primary_keyDiff.indexnameNew.toLowerCase() ) )
                                 .forEach( p -> handleIndex( pTableDiff, p ) );
    }

    handlePrimarykey( pTableDiff );

    pTableDiff.constraintsDiff.forEach( p -> handleConstraint( pTableDiff, p ) );

    pTableDiff.ind_uksIndexDiff.stream().filter( p -> pTableDiff.primary_keyDiff.indexnameNew == null || !p.isNew || !p.consNameNew.toLowerCase().equals( pTableDiff.primary_keyDiff.indexnameNew.toLowerCase() ) ).forEach( p -> handleIndex( pTableDiff, p ) );

    pTableDiff.ind_uksUniqueKeyDiff.forEach( p -> handleUniquekey( pTableDiff, p ) );

    pTableDiff.commentsDiff.forEach( p -> handleComment( pTableDiff, p ) );

    handleMviewlog( pTableDiff );
  }

  private boolean isCleanupTableDetailsGlobalNeeded( TableDiff pTableDiff )
  {
    String lTableName = pTableDiff.nameNew;

    List<String> lOldConstraintNamesOtherTables = oldContraintNames.entrySet()//
    .stream()//
    .filter( p -> !p.getKey().equals( lTableName ) )//
    .map( Map.Entry::getValue )//
    .flatMap( List::stream ).collect( Collectors.toList() );

    if( newContraintNames.containsKey( lTableName ) && newContraintNames.get( lTableName )//
    .stream()//
    .filter( p -> lOldConstraintNamesOtherTables.contains( p ) )//
    .findAny()//
    .isPresent() )
    {
      return true;
    }

    return false;
  }

  private void handleTableCleanupTableDetails( TableDiff pTableDiff, boolean pGlobalMode )
  {
    if( isCleanupTableDetailsGlobalNeeded( pTableDiff ) == pGlobalMode )
    {
      if( databaseHandler.isUpdateIdentity() )
      {
        for( ColumnDiff lColumnDiff : pTableDiff.columnsDiff )
        {
          dropIfNeeded( lColumnDiff.identityDiff, p -> ddlBuilder.dropColumnIdentity( p, pTableDiff, lColumnDiff, lColumnDiff.identityDiff ) );
        }
      }

      dropIfNeeded( pTableDiff.primary_keyDiff, p -> ddlBuilder.dropPrimaryKey( p, pTableDiff, pTableDiff.primary_keyDiff ) );

      dropIfNeeded( pTableDiff.constraintsDiff, p -> ddlBuilder.dropConstraint( p, pTableDiff, p.diff ) );

      dropIfNeeded( pTableDiff.mviewLogDiff, p -> ddlBuilder.dropMaterializedViewLog( p, pTableDiff ) );

      dropIfNeeded( pTableDiff.ind_uksUniqueKeyDiff, p -> ddlBuilder.dropUniqueKey( p, pTableDiff, p.diff ) );

      dropIfNeeded( pTableDiff.ind_uksIndexDiff, p -> ddlBuilder.dropIndex( p, pTableDiff, p.diff ) );

      for( InlineCommentDiff lCommentDiff : pTableDiff.commentsDiff )
      {
        if( lCommentDiff.isOld && lCommentDiff.isMatched == false )
        {
          boolean lIsColumnComment = lCommentDiff.column_nameOld != null;
          if( !lIsColumnComment || columnIsNew( pTableDiff, lCommentDiff.column_nameOld ) )
          {
            doInDiffActionDrop( lCommentDiff, p -> ddlBuilder.dropComment( p, pTableDiff, lCommentDiff ) );
          }
        }
      }

      List<ColumnDiff> lDropColumnDiffList = pTableDiff.columnsDiff.stream()//
      .filter( p -> !p.isNew )//
      .collect( Collectors.toList() );

      if( lDropColumnDiffList.size() > 1 && _parameters.isMinimizeStatementCount() && ddlBuilder.isMultiDropPossible( pTableDiff, lDropColumnDiffList, dataHandler ) )
      {
        DiffActionRunnable lRunnable = p -> ddlBuilder.getColumnDropHandler( p, pTableDiff, lDropColumnDiffList ).run();

        DiffAction lDiffAction = new DiffAction( diffReasonKeyRegistry.getDiffReasonKey( pTableDiff ), DiffReasonType.ALTER );
        List<DiffActionReason> lDiffActionReasonList = lDropColumnDiffList//
        .stream()//
        .map( pColumnDiff -> new DiffActionReasonSurplus( diffReasonKeyRegistry.getDiffReasonKey( pColumnDiff ) ) )//
        .collect( Collectors.toList() );

        doInDiffAction( lDiffAction, lDiffActionReasonList, lRunnable, createStatementBuilder() );
      }
      else
      {
        lDropColumnDiffList.forEach( pColumnDiff -> doInDiffActionDrop( pColumnDiff, p -> ddlBuilder.dropColumn( p, pTableDiff, pColumnDiff, dataHandler ) ) );
      }
    }
  }

  private void handleComment( TableDiff pTableDiff, InlineCommentDiff pInlineCommentDiff )
  {
    if( pInlineCommentDiff.isEqual == false || isRecreateNeeded( pInlineCommentDiff ) )
    {
      if( pInlineCommentDiff.isNew )
      {
        DiffReasonType lDiffReasonType;
        List<DiffActionReason> lDiffActionReasonList;
        if( isRecreateNeeded( pInlineCommentDiff ) )
        {
          lDiffReasonType = DiffReasonType.RECREATE_CREATE;
          lDiffActionReasonList = recreateNeededRegistry.getRecreateNeededReasons( pInlineCommentDiff );
        }
        else
        {
          if( pInlineCommentDiff.isMatched )
          {
            lDiffReasonType = DiffReasonType.ALTER;
            DiffActionReasonDifferent lDiffActionReasonDifferent = new DiffActionReasonDifferent( diffReasonKeyRegistry.getDiffReasonKey( pInlineCommentDiff ) );
            lDiffActionReasonDifferent.addDiffReasonDetail( new DifferenceImplAttributeOnly(INLINE_COMMENT__COMMENT) );
            lDiffActionReasonList = Collections.singletonList( lDiffActionReasonDifferent );
          }
          else
          {
            lDiffReasonType = DiffReasonType.CREATE;
            lDiffActionReasonList = Collections.singletonList( new DiffActionReasonMissing( diffReasonKeyRegistry.getDiffReasonKey( pInlineCommentDiff ) ) );
          }
        }

        doInDiffAction( pInlineCommentDiff, lDiffActionReasonList, lDiffReasonType, p -> ddlBuilder.setComment( p, pTableDiff, pInlineCommentDiff ), createStatementBuilder() );
      }
    }
  }

  private boolean columnIsNew( TableDiff pTableDiff, String pColumnName )
  {
    for( ColumnDiff lColumnDiff : pTableDiff.columnsDiff )
    {
      if( lColumnDiff.isNew )
      {
        if( lColumnDiff.nameNew.equals( pColumnName ) )
        {
          return true;
        }
      }
    }

    return false;
  }

  private void handleUniquekey( TableDiff pTableDiff, UniqueKeyDiff pUniqueKeyDiff )
  {
    createIfNeededOrAlter( pUniqueKeyDiff, //
    p -> ddlBuilder.createUniqueKey( p, pTableDiff, pUniqueKeyDiff ), //
    p -> ddlBuilder.alterUniqueKeyIfNeeded( p, pTableDiff, pUniqueKeyDiff ) );
  }

  private void handleIndex( TableDiff pTableDiff, IndexDiff pIndexDiff )
  {
    if( _parameters.getMultiSchema() ) {
      if( pIndexDiff.isNew )
      {
        if( pIndexDiff.isOld == false  )
        {
          DiffAction lDiffAction = new DiffAction( new DiffReasonKey(pTableDiff,"index"), DiffReasonType.CREATE );

          doInDiffAction( lDiffAction, Collections.singletonList( new DiffActionReasonMissing( diffReasonKeyRegistry.getDiffReasonKey(
              pIndexDiff) ) ),
              (DiffActionRunnable) p -> ddlBuilder.createIndexGrantIfNeeded(p, pTableDiff, pIndexDiff), createStatementBuilder() );
        }
      }
    }

    createIfNeededOrAlter( pIndexDiff, //
    p -> ddlBuilder.createIndex( p, pTableDiff, pIndexDiff, _parameters.isIndexparallelcreate() ), //
    p -> ddlBuilder.alterIndexIfNeeded( p, pIndexDiff, _parameters.isIndexmovetablespace(), getDefaultTablespace(null,pTableDiff.nameNew,pIndexDiff.consNameNew) ) );
  }

  private String getDefaultTablespace(String pMviewName, String pTableName, String pIndexName)
  {
    return InitDiffRepository.getDefaultTablespace(pMviewName, pTableName, pIndexName);
  }

  private void handleConstraint( TableDiff pTableDiff, ConstraintDiff pConstraintDiff )
  {
    createIfNeededOrAlter( pConstraintDiff, p -> ddlBuilder.createConstraint( p, pTableDiff, pConstraintDiff ), p -> ddlBuilder.updateConstraintIfNeeded( p, pTableDiff, pConstraintDiff ) );
  }

  private void handlePrimarykey( TableDiff pTableDiff )
  {
    if( pTableDiff.isOld || !pTableDiff.indexOrganizedNew ) {
      createIfNeededOrAlter(
          pTableDiff.primary_keyDiff,
          p -> ddlBuilder.createPrimarykey(p, pTableDiff),
          p -> ddlBuilder.alterPrimarykeyIfNeeded(p, pTableDiff));
    }
  }

  static ForeignKeyDiff getFkForRefPartitioning( TableDiff pTableDiff )
  {
    for( ForeignKeyDiff lForeignKeyDiff : pTableDiff.foreign_keysDiff )
    {
      if( lForeignKeyDiff.consNameNew.equalsIgnoreCase( pTableDiff.tablePartitioningRefPartitionsDiff.fkNameNew ) )
      {
        return lForeignKeyDiff;
      }
    }

    throw new RuntimeException( "fk for refpartitioning not found" );
  }

  private boolean addTableList( List<TableDiff> pRemainingTableDiffList, List<String> pAddedTableNames, List<TableDiff> pOrderedList )
  {
    boolean lAddedAtLeastOne = false;

    for( TableDiff lTableDiff : new ArrayList<TableDiff>( pRemainingTableDiffList ) )
    {
      String lRequiredTableName = null;

      if( !lTableDiff.isOld && lTableDiff.tablePartitioningRefPartitionsDiff.isNew )
      {
        lRequiredTableName = getFkForRefPartitioning( lTableDiff ).destTableNew;
      }

      if( lRequiredTableName == null || pAddedTableNames.contains( lRequiredTableName ) )
      {
        pOrderedList.add( lTableDiff );
        pAddedTableNames.add( lTableDiff.nameNew );
        pRemainingTableDiffList.remove( lTableDiff );

        lAddedAtLeastOne = true;
      }
    }

    return lAddedAtLeastOne;
  }

  private void sortTablesForRefPart( ModelDiff pModelDiff )
  {
    List<TableDiff> lTableDiffList = pModelDiff.model_elementsTableDiff;

    List<TableDiff> lRemainingTableDiffList = new ArrayList<TableDiff>( lTableDiffList );
    List<String> lAddedTableNames = new ArrayList<String>();
    List<TableDiff> lOrderedList = new ArrayList<TableDiff>();

    while( addTableList( lRemainingTableDiffList, lAddedTableNames, lOrderedList ) )
    {
    }

    if( !lRemainingTableDiffList.isEmpty() )
    {
      throw new RuntimeException( "possible table order not found " + lRemainingTableDiffList.get( 0 ).nameNew );
    }

    pModelDiff.model_elementsTableDiff = lOrderedList;
  }

  private void createIfNeededOrAlter( AbstractDiff pDiff, DiffActionRunnable pRunnableCreate, DiffActionRunnableAlter pRunnableAlter )
  {
    if( pDiff.isNew )
    {
      if( pDiff.isOld == false || isRecreateNeeded( pDiff ) )
      {
        doInDiffActionCreate( pDiff, pRunnableCreate );
      }
      else
      {
        if( pRunnableAlter != null )
        {
          doInDiffActionAlter( pDiff, pRunnableAlter );
        }
      }
    }
  }

  private void handleAllMviews( ModelDiff pModelDiff, boolean pDrop )
  {
    for (MviewDiff lMviewDiff : pModelDiff.model_elementsMviewDiff) {
      if (pDrop) {
        dropIfNeeded(lMviewDiff, p -> ddlBuilder.dropMview(p, lMviewDiff));
      } else {
        createIfNeededOrAlter(
            lMviewDiff, //
            p2 -> ddlBuilder.createMview(p2, lMviewDiff), //
            p1 -> ddlBuilder.alterMviewIfNeeded(p1, lMviewDiff));
      }
    }
  }

  private void handleMviewlog( TableDiff pTableDiff )
  {
    createIfNeededOrAlter( pTableDiff.mviewLogDiff, //
    p -> ddlBuilder.createMviewlog( p, pTableDiff, _parameters.getDateformat() ), //
    p -> ddlBuilder.alterMviewlogIfNeeded( p, pTableDiff, _parameters.getDateformat() ) );
  }

  private interface AbstractDiffActionRunnable<T> extends Consumer<T>
  {
  }

  private interface DiffActionRunnable extends AbstractDiffActionRunnable<StatementBuilder>
  {
  }

  private interface DiffActionRunnableWithDiff<T extends AbstractDiff> extends Consumer<StatementBuilderWithDiff<T>>
  {
  }

  private interface DiffActionRunnableAlter extends AbstractDiffActionRunnable<StatementBuilderAlter>
  {
  }

  private StatementBuilder createStatementBuilder()
  {
    return new StatementBuilder( () -> activeDiffAction, _parameters.isAdditionsOnly(), currentAlterTableCombiner );
  }

  public static class DataHandler
  {
    private CallableStatementProvider _callableStatementProvider;
    private Parameters _parameters;

    public DataHandler( CallableStatementProvider pCallableStatementProvider, Parameters pParameters )
    {
      _callableStatementProvider = pCallableStatementProvider;
      _parameters = pParameters;
    }

    public boolean hasRowsIgnoreExceptions( String pTestStatement )
    {
      try
      {
        return hasRows( pTestStatement );
      }
      catch( Exception e )
      {
        return false;
      }
    }

    public void dropWithDropmodeCheck( StatementBuilder pStatementBuilder, String pTestStatement, Runnable pDropHandler )
    {
      if( !isDropOk( pTestStatement ) )
      {
        pStatementBuilder.fail( "dropmode not active, the following statement contains data. Execution would result in data-loss: " + pTestStatement );
      }

      pDropHandler.run();
    }

    public boolean isDropOk( String pTestStatement )
    {
      if( isCheckDropMode() )
      {
        if( hasRows( pTestStatement ) )
        {
          return false;
        }
      }

      return true;
    }

    public boolean isCheckDropMode()
    {
      return !_parameters.isAdditionsOnly() && !isDropmode();
    }

    private boolean hasRows( String pTestStatement )
    {
      return (Boolean) new WrapperReturnValueFromResultSet( pTestStatement, _callableStatementProvider )
      {
        @Override
        protected Object getValueFromResultSet( ResultSet pResultSet ) throws SQLException
        {
          return pResultSet.next();
        }
      }.executeForValue();
    }

    public boolean isDropmode()
    {
      return _parameters.isDropmode();
    }

    public BigDecimal getSequenceMaxValueSelectValue( SequenceDiff pSequenceDiff )
    {
      BigDecimal lSollStartValue = null;

      try
      {
        if( pSequenceDiff.max_value_selectNew != null )
        {
          lSollStartValue = (BigDecimal) new WrapperReturnFirstValue( pSequenceDiff.max_value_selectNew, _callableStatementProvider ).executeForValue();

          lSollStartValue = lSollStartValue.add( BigDecimal.valueOf( 1 ) );
        }
      }
      catch( Exception e )
      {
        // kann vorkommen, wenn fuer das select benoetigte Tabellen nicht
        // exisitieren. kann erst richtig korrigiert werden, wenn auch der
        // Tabellenabgleich auf dieses Package umgestellt wurde
      }
      return lSollStartValue;
    }
  }
}


text:
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Amazon.SQS;
using Amazon.SQS.Model;
using Microsoft.AspNet.Mvc;

namespace DbSpeed.Controllers
{
	public class SqsController : Controller
	{
		private const string QueueName = "Customers";
		private readonly IAmazonSQS _client;

		public SqsController(IAmazonSQS client)
		{
            _client = client;
        }

        [HttpGet]
		public async Task<IEnumerable<string>> Get()
		{
			var result = new List<string>();
			var watch = new Stopwatch();
			var response = await _client.GetQueueUrlAsync(new GetQueueUrlRequest(QueueName));

			for (int i = 1; i < 11; i++)
			{
				watch.Restart();

				await _client.ReceiveMessageAsync(new ReceiveMessageRequest(response.QueueUrl));

				watch.Stop();
				result.Add($"SQS {i} try: {watch.ElapsedMilliseconds}");
			}

			return result;
		}

        [HttpPost]
		public async Task<IEnumerable<string>> Post()
		{
			var result = new List<string>();
			var watch = new Stopwatch();

			var response = await _client.CreateQueueAsync(new CreateQueueRequest { QueueName = QueueName });

			for (int i = 1; i < 11; i++)
			{
				watch.Restart();

				await _client.SendMessageAsync(new SendMessageRequest { MessageBody = "Hallo" + i, QueueUrl = response.QueueUrl });

				watch.Stop();
				result.Add($"SQS write {i} try: {watch.ElapsedMilliseconds}");
			}

			return result;
		}

        [HttpDelete]
		public async Task Delete()
		{
			var response = await _client.GetQueueUrlAsync(new GetQueueUrlRequest(QueueName));
			await _client.DeleteQueueAsync(new DeleteQueueRequest(response.QueueUrl));
		}
	}
}

redpajama_set_name: RedPajamaGithub


text:
package org.hillview.table;

import org.hillview.table.api.ContentsKind;
import org.hillview.table.api.IDateColumn;

import javax.annotation.Nullable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A column of Dates that can grow in size.
 */
public class DateListColumn
        extends BaseListColumn
        implements IDateColumn {
    private final ArrayList<LocalDateTime[]> segments;
    private static final Logger logger = Logger.getLogger(DateListColumn.class.getName());

    /**
     * Used when parsing; this is set the first time when parsing a date
     * and used subsequently.
     */
    @Nullable
    private DateTimeFormatter parserFormatter;
    /**
     * Used in conjunction with the parseFormatter.  If true
     * parse the strings as LocalDate. Java is really stupid in this respect,
     * and the spec is unclear about this.
     * @see Parsing LocalDateTime
     */
    private boolean parseAsDate;

    public DateListColumn(final ColumnDescription desc) {
        super(desc);
        this.checkKind(ContentsKind.Date);
        this.segments = new ArrayList<LocalDateTime[]>();
        this.parserFormatter = null;
    }

    @Nullable
    @Override
    public LocalDateTime getDate(final int rowIndex) {
        final int segmentId = rowIndex >> this.LogSegmentSize;
        final int localIndex = rowIndex & this.SegmentMask;
        return this.segments.get(segmentId)[localIndex];
    }

    @Override
    void grow() {
        this.segments.add(new LocalDateTime[this.SegmentSize]);
        this.growMissing();
    }

    @SuppressWarnings("Duplicates")
    private void append(@Nullable final LocalDateTime value) {
        final int segmentId = this.size >> this.LogSegmentSize;
        final int localIndex = this.size & this.SegmentMask;
        if (this.segments.size() <= segmentId)
            this.grow();
        this.segments.get(segmentId)[localIndex] = value;
        this.size++;
    }

    @Override
    public boolean isMissing(final int rowIndex) {
        return this.getDate(rowIndex) == null;
    }

    @Override
    public void appendMissing() {
        this.append(null);
    }

    private static final LinkedHashMap<String, String> DATE_FORMAT_REGEXPS =
            new LinkedHashMap<String, String>() {{
                put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy-MM-dd HH:mm:ss");
                put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMMM yyyy HH:mm:ss");
                put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMM yyyy HH:mm:ss");
                put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy/MM/dd HH:mm:ss");
                put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "MM/dd/yyyy HH:mm:ss");
                put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd-MM-yyyy HH:mm:ss");
                put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}$", "dd-MM-yyyy HH:mm");
                put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy-MM-dd HH:mm");
                put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}$", "MM/dd/yyyy HH:mm");
                put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy/MM/dd HH:mm");
                put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMM yyyy HH:mm");
                put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMMM yyyy HH:mm");
                put("^\\d{1,2}-\\d{1,2}-\\d{4}$", "dd-MM-yyyy");
                put("^\\d{4}-\\d{1,2}-\\d{1,2}$", "yyyy-MM-dd");
                put("^\\d{1,2}/\\d{1,2}/\\d{4}$", "MM/dd/yyyy");
                put("^\\d{4}/\\d{1,2}/\\d{1,2}$", "yyyy/MM/dd");
                put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}$", "dd MMM yyyy");
                put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}$", "dd MMMM yyyy");
                put("^\\d{12}$", "yyyyMMddHHmm");
                put("^\\d{8}\\s\\d{4}$", "yyyyMMdd HHmm");
                put("^\\d{8}$", "yyyyMMdd");
                put("^\\d{14}$", "yyyyMMddHHmmss");
                put("^\\d{8}\\s\\d{6}$", "yyyyMMdd HHmmss");
    }};

    private static final DateTimeFormatter[] toTry = {
            DateTimeFormatter.BASIC_ISO_DATE,
            DateTimeFormatter.ISO_LOCAL_DATE,
            DateTimeFormatter.ISO_OFFSET_DATE,
            DateTimeFormatter.ISO_DATE,
            DateTimeFormatter.ISO_LOCAL_TIME,
            DateTimeFormatter.ISO_OFFSET_TIME,
            DateTimeFormatter.ISO_TIME,
            DateTimeFormatter.ISO_LOCAL_DATE_TIME,
            DateTimeFormatter.ISO_OFFSET_DATE_TIME,
            DateTimeFormatter.ISO_ZONED_DATE_TIME,
            DateTimeFormatter.ISO_DATE_TIME,
            DateTimeFormatter.ISO_ORDINAL_DATE,
            DateTimeFormatter.ISO_INSTANT,
            DateTimeFormatter.RFC_1123_DATE_TIME
    };

    @SuppressWarnings("UnnecessaryContinue")
    private void guessParseFormat(String s) {
        boolean[] asDate = {false, true};

        for (boolean b : asDate) {
            this.parseAsDate = b;
            for (DateTimeFormatter d : toTry) {
                try {
                    if (b)
                        LocalDate.parse(s, d);
                    else
                        LocalDateTime.parse(s, d);

                    logger.log(Level.INFO, "Guessed date format " + d.toString());
                    this.parserFormatter = d;
                    return;
                } catch (DateTimeParseException ex) {
                    continue;
                }
            }
        }

        this.parseAsDate = false;
        // none of the standard formats worked, let's try some custom ones
        for (String regexp : DATE_FORMAT_REGEXPS.keySet()) {
            if (s.toLowerCase().matches(regexp)) {
                String format = DATE_FORMAT_REGEXPS.get(regexp);
                this.parserFormatter = DateTimeFormatter.ofPattern(format);
                logger.log(Level.INFO, "Guessed date format " + regexp);
                return;
            }
        }

        throw new RuntimeException("Could not guess parsing format for date " + s);
    }

    @Override
    public void parseAndAppendString(@Nullable String s) {
        if ((s == null) || s.isEmpty())
            this.parseEmptyOrNull();
        else {
            if (this.parserFormatter == null)
                this.guessParseFormat(s);
            LocalDateTime dt;
            if (this.parseAsDate) {
                LocalDate date = LocalDate.parse(s, this.parserFormatter);
                dt = date.atStartOfDay();
            } else {
                dt = LocalDateTime.parse(s, this.parserFormatter);
            }
            this.append(dt);
        }
    }
}


text: A simple starter project demonstrating the basic concepts of Angular 2.


text:
/**
 *
 */
package com.tacocat.lambda.core.system.action;

import com.tacocat.lambda.core.component.Component;

/**
 * Function that takes a component and can modify its state
 */
@FunctionalInterface
public interface Modification {
	public void apply(Component arg);
}


text:
body{
	-webkit-backface-visibility:hidden;
}
.animated{
	-webkit-animation-duration:1s;
	-moz-animation-duration:1s;
	animation-duration:1s;
	-webkit-animation-fill-mode:both;
	-moz-animation-fill-mode:both;
	animation-fill-mode:both;
}
.radius{
	-moz-border-radius:3px;
	border-radius:3px;
}
.radius-corner{
	-moz-border-top-left-radius:3px;
	border-top-left-radius:3px;
}
.radius-side{
	-moz-border-top-left-radius:3px;
	border-top-left-radius:3px;
	-moz-border-top-right-radius:3px;
	border-top-right-radius:3px;
}


text:
import React from 'react';
import PropTypes from 'prop-types';

import './ReadingRelatedPassage.css';

class ReadingRelatedPassage extends React.Component {

	getDefaultProps() {
		return {
			relatedPassage: {
				workId: 'foobar',
				authorName: 'Theocritus',
				englishTitle: 'Idylls',
				edition: '1920, A. S. F. Gow',
				location: '17.2',
				textNodes: [
					{
						text: 'ἄλλοκα δ' αὖ ποτὶ τὸν ῥιπτεῖ νόον· οἳ δ' ὑπ' ἔρωτος',
						n: 2,
					},
				],
			},
		};
	}

	render() {
		const relatedPassage = this.props.relatedPassage;

		return (

				{relatedPassage.edition}

						{relatedPassage.authorName},
						{relatedPassage.englishTitle}
						{relatedPassage.location}

					{relatedPassage.textNodes.map((textNode, i) =>

					)}

		);
	}
}

ReadingRelatedPassage.propTypes = {
	relatedPassage: PropTypes.object,
};

export default ReadingRelatedPassage;


text:
{-# LANGUAGE CPP #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# OPTIONS_GHC -Wall #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Text.PrettyPrint.ANSI.Leijen
-- Copyright   :  Daan Leijen (c) 2000, http://www.cs.uu.nl/~daan
--                Max Bolingbroke (c) 2008, http://blog.omega-prime.co.uk
-- License     :  BSD-style (see the file LICENSE)
--
-- Maintainer  :  batterseapower@hotmail.com
-- Stability   :  provisional
-- Portability :  portable
--
-- Pretty print module based on Philip Wadler's \"prettier printer\"
--
-- @
--      \"A prettier printer\"
--      Draft paper, April 1997, revised March 1998.
--      <http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf>
-- @
--
-- PPrint is an implementation of the pretty printing combinators
-- described by Philip Wadler (1997). In their bare essence, the
-- combinators of Wadler are not expressive enough to describe some
-- commonly occurring layouts. The PPrint library adds new primitives
-- to describe these layouts and works well in practice.
--
-- The library is based on a single way to concatenate documents,
-- which is associative and has both a left and right unit.  This
-- simple design leads to an efficient and short implementation. The
-- simplicity is reflected in the predictable behaviour of the
-- combinators which make them easy to use in practice.
--
-- A thorough description of the primitive combinators and their
-- implementation can be found in Philip Wadler's paper
-- (1997). Additions and the main differences with his original paper
-- are:
--
-- * The nil document is called empty.
--
-- * The above combinator is called '<$>'. The operator '</>' is used
-- for soft line breaks.
--
-- * There are three new primitives: 'align', 'fill' and
-- 'fillBreak'. These are very useful in practice.
--
-- * Lots of other useful combinators, like 'fillSep' and 'list'.
--
-- * There are two renderers, 'renderPretty' for pretty printing and
-- 'renderCompact' for compact output. The pretty printing algorithm
-- also uses a ribbon-width now for even prettier output.
--
-- * There are two displayers, 'displayS' for strings and 'displayIO' for
-- file based output.
--
-- * There is a 'Pretty' class.
--
-- * The implementation uses optimised representations and strictness
-- annotations.
--
-- Full documentation for the original wl-pprint library available at
-- <http://www.cs.uu.nl/~daan/download/pprint/pprint.html>.
--
-- The library has been extended to allow formatting text for output
-- to ANSI style consoles. New combinators allow:
--
-- * Control of foreground and background color of text
--
-- * The abliity to make parts of the text bold or underlined
--
-- This functionality is, as far as possible, portable across platforms
-- with their varying terminals.  However, one thing to be particularly
-- wary of is that console colors will not be displayed on Windows unless
-- the 'Doc' value is output using the 'putDoc' function or one of it's
-- friends.  Rendering the 'Doc' to a 'String' and then outputing /that/
-- will only work on Unix-style operating systems.
-----------------------------------------------------------
module Text.PrettyPrint.ANSI.Leijen (
   -- * Documents
   Doc, putDoc, hPutDoc,

   -- * Basic combinators
   empty, char, text, (<>), nest, line, linebreak, group, softline,
   softbreak, hardline, flatAlt, renderSmart,

   -- * Alignment
   --
   -- The combinators in this section can not be described by Wadler's
   -- original combinators. They align their output relative to the
   -- current output position - in contrast to @nest@ which always
   -- aligns to the current nesting level. This deprives these
   -- combinators from being \`optimal\'. In practice however they
   -- prove to be very useful. The combinators in this section should
   -- be used with care, since they are more expensive than the other
   -- combinators. For example, @align@ shouldn't be used to pretty
   -- print all top-level declarations of a language, but using @hang@
   -- for let expressions is fine.
   align, hang, indent, encloseSep, list, tupled, semiBraces,

   -- * Operators
   (<+>), (Text.PrettyPrint.ANSI.Leijen.<$>), (</>), (<$$>), (<//>),

   -- * List combinators
   hsep, vsep, fillSep, sep, hcat, vcat, fillCat, cat, punctuate,

   -- * Fillers
   fill, fillBreak,

   -- * Bracketing combinators
   enclose, squotes, dquotes, parens, angles, braces, brackets,

   -- * Character documents
   lparen, rparen, langle, rangle, lbrace, rbrace, lbracket, rbracket,
   squote, dquote, semi, colon, comma, space, dot, backslash, equals,

   -- * Colorisation combinators
   black, red, green, yellow, blue, magenta, cyan, white,
   dullblack, dullred, dullgreen, dullyellow, dullblue, dullmagenta, dullcyan, dullwhite,
   onblack, onred, ongreen, onyellow, onblue, onmagenta, oncyan, onwhite,
   ondullblack, ondullred, ondullgreen, ondullyellow, ondullblue, ondullmagenta, ondullcyan, ondullwhite,

   -- * Emboldening combinators
   bold, debold,

   -- * Underlining combinators
   underline, deunderline,

   -- * Removing formatting
   plain,

   -- * Primitive type documents
   string, int, integer, float, double, rational,

   -- * Pretty class
   Pretty(..),

   -- * Rendering
   SimpleDoc(..), renderPretty, renderCompact, displayS, displayIO

   -- * Undocumented
        , bool

        , column, columns, nesting, width

        ) where

import System.IO (Handle,hPutStr,stdout)
import qualified Text.PrettyPrint.Free.Internal as P
import           Text.PrettyPrint.Free.Internal
  hiding (Doc, renderPretty, renderSmart, displayS, displayIO, putDoc, hPutDoc)

import System.Console.ANSI (Color(..), ColorIntensity(..), ConsoleLayer(..),
                            Underlining(..), ConsoleIntensity(..),
                            SGR(..), setSGRCode)

import Data.Maybe (catMaybes, maybeToList)

-- | The document @(x \<$\> y)@ concatenates document @x@ and @y@ with a
-- 'line' in between. (infixr 5)
(<$>) :: Doc -> Doc -> Doc
x <$> y         = x <> line <> y

-- | The document @(x \<$$\> y)@ concatenates document @x@ and @y@ with
-- a @linebreak@ in between. (infixr 5)
(<$$>) :: Doc -> Doc -> Doc
x <$$> y        = x <> linebreak <> y

-----------------------------------------------------------
-- Combinators for prelude types
-----------------------------------------------------------

-- string is like "text" but replaces '\n' by "line"

-- | The document @(string s)@ concatenates all characters in @s@
-- using @line@ for newline characters and @char@ for all other
-- characters. It is used instead of 'text' whenever the text contains
-- newline characters.
string :: String -> Doc
string ""       = empty
string ('\n':s) = line <> string s
string s        = case (span (/='\n') s) of
                    (xs,ys) -> text xs <> string ys

bool :: Bool -> Doc
bool b          = text (show b)

-- | The document @(int i)@ shows the literal integer @i@ using
-- 'text'.
int :: Int -> Doc
int i           = text (show i)

-- | The document @(integer i)@ shows the literal integer @i@ using
-- 'text'.
integer :: Integer -> Doc
integer i       = text (show i)

-- | The document @(float f)@ shows the literal float @f@ using
-- 'text'.
float :: Float -> Doc
float f         = text (show f)

-- | The document @(double d)@ shows the literal double @d@ using
-- 'text'.
double :: Double -> Doc
double d        = text (show d)

-- | The document @(rational r)@ shows the literal rational @r@ using
-- 'text'.
rational :: Rational -> Doc
rational r      = text (show r)

-----------------------------------------------------------
-- Primitives
-----------------------------------------------------------

data AnsiEffect = AEColor ConsoleLayer ColorIntensity Color
                | AEIntensify ConsoleIntensity
                | AEItalicize Bool
                | AEUnderline Underlining

type Doc = P.Doc AnsiEffect ()

type SimpleDoc' = SimpleDoc () (Either [SGR] ())

-----------------------------------------------------------
-- Colors
-----------------------------------------------------------

-- | Displays a document with the black forecolor
black :: Doc -> Doc
-- | Displays a document with the red forecolor
red :: Doc -> Doc
-- | Displays a document with the green forecolor
green :: Doc -> Doc
-- | Displays a document with the yellow forecolor
yellow :: Doc -> Doc
-- | Displays a document with the blue forecolor
blue :: Doc -> Doc
-- | Displays a document with the magenta forecolor
magenta :: Doc -> Doc
-- | Displays a document with the cyan forecolor
cyan :: Doc -> Doc
-- | Displays a document with the white forecolor
white :: Doc -> Doc
-- | Displays a document with the dull black forecolor
dullblack :: Doc -> Doc
-- | Displays a document with the dull red forecolor
dullred :: Doc -> Doc
-- | Displays a document with the dull green forecolor
dullgreen :: Doc -> Doc
-- | Displays a document with the dull yellow forecolor
dullyellow :: Doc -> Doc
-- | Displays a document with the dull blue forecolor
dullblue :: Doc -> Doc
-- | Displays a document with the dull magenta forecolor
dullmagenta :: Doc -> Doc
-- | Displays a document with the dull cyan forecolor
dullcyan :: Doc -> Doc
-- | Displays a document with the dull white forecolor
dullwhite :: Doc -> Doc
(black, dullblack)     = colorFunctions Black
(red, dullred)         = colorFunctions Red
(green, dullgreen)     = colorFunctions Green
(yellow, dullyellow)   = colorFunctions Yellow
(blue, dullblue)       = colorFunctions Blue
(magenta, dullmagenta) = colorFunctions Magenta
(cyan, dullcyan)       = colorFunctions Cyan
(white, dullwhite)     = colorFunctions White

-- | Displays a document with a forecolor given in the first parameter
color :: Color -> Doc -> Doc
-- | Displays a document with a dull forecolor given in the first parameter
dullcolor :: Color -> Doc -> Doc
color     = Annotate . AEColor Foreground Vivid
dullcolor = Annotate . AEColor Foreground Dull

colorFunctions :: Color -> (Doc -> Doc, Doc -> Doc)
colorFunctions what = (color what, dullcolor what)

-- | Displays a document with the black backcolor
onblack :: Doc -> Doc
-- | Displays a document with the red backcolor
onred :: Doc -> Doc
-- | Displays a document with the green backcolor
ongreen :: Doc -> Doc
-- | Displays a document with the yellow backcolor
onyellow :: Doc -> Doc
-- | Displays a document with the blue backcolor
onblue :: Doc -> Doc
-- | Displays a document with the magenta backcolor
onmagenta :: Doc -> Doc
-- | Displays a document with the cyan backcolor
oncyan :: Doc -> Doc
-- | Displays a document with the white backcolor
onwhite :: Doc -> Doc
-- | Displays a document with the dull block backcolor
ondullblack :: Doc -> Doc
-- | Displays a document with the dull red backcolor
ondullred :: Doc -> Doc
-- | Displays a document with the dull green backcolor
ondullgreen :: Doc -> Doc
-- | Displays a document with the dull yellow backcolor
ondullyellow :: Doc -> Doc
-- | Displays a document with the dull blue backcolor
ondullblue :: Doc -> Doc
-- | Displays a document with the dull magenta backcolor
ondullmagenta :: Doc -> Doc
-- | Displays a document with the dull cyan backcolor
ondullcyan :: Doc -> Doc
-- | Displays a document with the dull white backcolor
ondullwhite :: Doc -> Doc
(onblack, ondullblack)     = oncolorFunctions Black
(onred, ondullred)         = oncolorFunctions Red
(ongreen, ondullgreen)     = oncolorFunctions Green
(onyellow, ondullyellow)   = oncolorFunctions Yellow
(onblue, ondullblue)       = oncolorFunctions Blue
(onmagenta, ondullmagenta) = oncolorFunctions Magenta
(oncyan, ondullcyan)       = oncolorFunctions Cyan
(onwhite, ondullwhite)     = oncolorFunctions White

-- | Displays a document with a backcolor given in the first parameter
oncolor :: Color -> Doc -> Doc
-- | Displays a document with a dull backcolor given in the first parameter
ondullcolor :: Color -> Doc -> Doc
oncolor     = Annotate . AEColor Background Vivid
ondullcolor = Annotate . AEColor Background Dull

oncolorFunctions :: Color -> (Doc -> Doc, Doc -> Doc)
oncolorFunctions what = (oncolor what, ondullcolor what)

-----------------------------------------------------------
-- Console Intensity
-----------------------------------------------------------

-- | Displays a document in a heavier font weight
bold :: Doc -> Doc
bold = Annotate (AEIntensify BoldIntensity)

-- | Displays a document in the normal font weight
debold :: Doc -> Doc
debold = Annotate (AEIntensify NormalIntensity)

-- NB: I don't support FaintIntensity here because it is not widely supported by terminals.

-----------------------------------------------------------
-- Italicization
-----------------------------------------------------------

{-

I'm in two minds about providing these functions, since italicization is so rarely implemented.
It is especially bad because "italicization" may cause the meaning of colors to flip, which will
look a bit weird, to say the least...

-- | Displays a document in italics. This is not widely supported, and it's use is not recommended
italicize :: Doc -> Doc
italicize = Italicize True

-- | Displays a document with no italics
deitalicize :: Doc -> Doc
deitalicize = Italicize False

-}

-----------------------------------------------------------
-- Underlining
-----------------------------------------------------------

-- | Displays a document with underlining
underline :: Doc -> Doc
underline = Annotate (AEUnderline SingleUnderline)

-- | Displays a document with no underlining
deunderline :: Doc -> Doc
deunderline = Annotate (AEUnderline NoUnderline)

-- NB: I don't support DoubleUnderline here because it is not widely supported by terminals.

-----------------------------------------------------------
-- Removing formatting
-----------------------------------------------------------

-- | Removes all colorisation, emboldening and underlining from a document
plain :: Doc -> Doc
plain Fail            = Fail
plain e@Empty         = e
plain c@(Char _)      = c
plain t@(Text _ _)    = t
plain l@Line          = l
plain (FlatAlt x y)   = FlatAlt (plain x) (plain y)
plain (Cat x y)       = Cat (plain x) (plain y)
plain (Nest i x)      = Nest i (plain x)
plain (Union x y)     = Union (plain x) (plain y)
plain (Column f)      = Column (plain . f)
plain (Columns f)     = Columns (plain . f)
plain (Ribbon f)      = Ribbon (plain . f)
plain (Nesting f)     = Nesting (plain . f)
plain (Effect x)      = Effect x
plain (Annotate _ x)  = x

-----------------------------------------------------------
-- Renderers
-----------------------------------------------------------

-----------------------------------------------------------
-- renderPretty: the default pretty printing algorithm
-----------------------------------------------------------

data AnsiState = AS
  { as_fc :: Maybe (ColorIntensity, Color)
  , as_bc :: Maybe (ColorIntensity, Color)
  , as_in :: Maybe ConsoleIntensity
  , as_it :: Maybe Bool
  , as_un :: Maybe Underlining

  , -- | A delta to be applied in the "push" direction
    as_delta :: Maybe SGR
{-
  , -- | A delta to be applied in the "pop" direction
    as_undelta :: Maybe SGR
-}
  }

-- Push all annotations down to the effect layer for easier processing!
scanANSI :: P.SimpleDoc AnsiEffect e -> P.SimpleDoc () (Either [SGR] e)
scanANSI = sdocAE (\_ e -> SEffect (Right e))
                  merge
                  (\as -> SEffect (Left $ maybeToList (as_delta as)))
                  (\as -> SEffect (Left $ reset as))
                  (AS Nothing Nothing Nothing Nothing Nothing Nothing)
 where
  merge :: AnsiState -> AnsiEffect -> AnsiState
  merge as0 ae = (\x -> x { as_delta = Just $ de ae }) $ go ae
   where
    go (AEColor Foreground i c) = as0 { as_fc = Just (i,c) }
    go (AEColor Background i c) = as0 { as_bc = Just (i,c) }
    go (AEIntensify i)          = as0 { as_in = Just i }
    go (AEItalicize i)          = as0 { as_it = Just i }
    go (AEUnderline u)          = as0 { as_un = Just u }

    de (AEColor l i c) = SetColor l i c
    de (AEIntensify i) = SetConsoleIntensity i
    de (AEItalicize i) = SetItalicized i
    de (AEUnderline u) = SetUnderlining u

  reset :: AnsiState -> [SGR]
  reset as = Reset : catMaybes
    [ (uncurry (SetColor Foreground)) `fmap` as_fc as
    , (uncurry (SetColor Background)) `fmap` as_bc as
    , SetConsoleIntensity             `fmap` as_in as
    , SetItalicized                   `fmap` as_it as
    , SetUnderlining                  `fmap` as_un as
    ]

-- | This is the default pretty printer which is used by 'show',
-- 'putDoc' and 'hPutDoc'. @(renderPretty ribbonfrac width x)@ renders
-- document @x@ with a page width of @width@ and a ribbon width of
-- @(ribbonfrac * width)@ characters. The ribbon width is the maximal
-- amount of non-indentation characters on a line. The parameter
-- @ribbonfrac@ should be between @0.0@ and @1.0@. If it is lower or
-- higher, the ribbon width will be 0 or @width@ respectively.
renderPretty :: Float -> Int -> Doc -> SimpleDoc'
renderPretty f i = scanANSI . P.renderPretty f i

-- | A slightly smarter rendering algorithm with more lookahead. It provides
-- provide earlier breaking on deeply nested structures.
-- For example, consider this python-ish pseudocode:
-- @fun(fun(fun(fun(fun([abcdefg, abcdefg])))))@
-- If we put a softbreak (+ nesting 2) after each open parenthesis, and align
-- the elements of the list to match the opening brackets, this will render with
-- @renderPretty@ and a page width of 20c as:
-- @
-- fun(fun(fun(fun(fun([
--                     | abcdef,
--                     | abcdef,
--                     ]
--   )))))             |
-- @
-- Where the 20c. boundary has been marked with |. Because @renderPretty@ only
-- uses one-line lookahead, it sees that the first line fits, and is stuck
-- putting the second and third lines after the 20c mark. In contrast,
-- @renderSmart@ will continue to check the potential document up to the end of
-- the indentation level. Thus, it will format the document as:
--
-- @
-- fun(                |
--   fun(              |
--     fun(            |
--       fun(          |
--         fun([       |
--               abcdef,
--               abcdef,
--             ]       |
--   )))))             |
-- @
-- Which fits within the 20c. mark.
-- In addition, @renderSmart@ uses this lookahead to minimize the number of
-- lines printed, leading to more compact and visually appealing output.
-- Consider this example using the same syntax as above:
-- @aaaaaaaaaaa([abc, def, ghi])@
-- When rendered with @renderPretty@ and a page width of 20c, we get:
-- @
-- aaaaaaaaaaa([ abc
--             , def
--             , ghi ])
-- @
-- Whereas when rendered with @renderSmart@ and a page width of 20c, we get:
-- @
-- aaaaaaaaaaa(
--   [abc, def, ghi])
-- @

renderSmart :: Int -> Doc -> SimpleDoc'
renderSmart i = scanANSI . P.renderSmart i

{- From renderFits:
    -- I used to do a @SSGR [Reset]@ here, but if you do that it will result
    -- in any rendered @Doc@ containing at least some ANSI control codes. This
    -- may be undesirable if you want to render to non-ANSI devices by simply
    -- not making use of the ANSI color combinators I provide.
    --
    -- What I "really" want to do here is do an initial Reset iff there is some
    -- ANSI color within the Doc, but that's a bit fiddly. I'll fix it if someone
    -- complains!
-}

-----------------------------------------------------------
-- Displayers:  displayS and displayIO
-----------------------------------------------------------

-- | @(displayS simpleDoc)@ takes the output @simpleDoc@ from a
-- rendering function and transforms it to a 'ShowS' type (for use in
-- the 'Show' class).
--
-- > showWidth :: Int -> Doc -> String
-- > showWidth w x   = displayS (renderPretty 0.4 w x) ""
displayS :: SimpleDoc' -> ShowS
displayS = displayDecorated ci ci ef showString
 where
  ci = const id

  ef (Right _) = id
  ef (Left x)  = showString (setSGRCode x)

-- | @(displayIO handle simpleDoc)@ writes @simpleDoc@ to the file
-- handle @handle@, discarding all effects and annotations. This function
-- is used for example by 'hPutDoc':
--
-- > hPutDoc handle doc  = displayIO handle (renderPretty 0.4 80 doc)
--
-- Any ANSI colorisation in @simpleDoc@ will be output.
displayIO :: Handle -> SimpleDoc' -> IO ()
displayIO handle = displayDecorated cpu cpu cpu (hPutStr handle)
 where cpu = const (pure ())

-----------------------------------------------------------
-- default pretty printers: show, putDoc and hPutDoc
-----------------------------------------------------------

instance Show Doc where
  showsPrec _ doc       = displayS $ renderPretty 0.4 80 doc

-- | The action @(putDoc doc)@ pretty prints document @doc@ to the
-- standard output, with a page width of 100 characters and a ribbon
-- width of 40 characters.
--
-- > main :: IO ()
-- > main = do{ putDoc (text "hello" <+> text "world") }
--
-- Which would output
--
-- @
-- hello world
-- @
--
-- Any ANSI colorisation in @doc@ will be output.
putDoc :: Doc -> IO ()
putDoc doc              = hPutDoc stdout doc

-- | @(hPutDoc handle doc)@ pretty prints document @doc@ to the file
-- handle @handle@ with a page width of 80 characters and a ribbon
-- width of 32 characters.
--
-- > main = do{ handle <- openFile "MyFile" WriteMode
-- >          ; hPutDoc handle (vcat (map text
-- >                            ["vertical","text"]))
-- >          ; hClose handle
-- >          }
--
-- Any ANSI colorisation in @doc@ will be output.
hPutDoc :: Handle -> Doc -> IO ()
hPutDoc handle doc = displayIO handle $ renderPretty 0.4 80 doc

--  LocalWords:  PPrint combinators Wadler Wadler's encloseSep


text:
<?php

/**
 * Adminhtml sales order create newsletter block
 *
 * @category   Mage
 * @package    Mage_Adminhtml
 * @author      Magento Core Team <core@magentocommerce.com>
 */

class Mage_Adminhtml_Block_Sales_Order_Create_Newsletter extends Mage_Adminhtml_Block_Sales_Order_Create_Abstract
{

    public function __construct()
    {
        parent::__construct();
        $this->setId('sales_order_create_newsletter');
    }

    public function getHeaderText()
    {
        return Mage::helper('sales')->__('Newsletter Subscription');
    }

    public function getHeaderCssClass()
    {
        return 'head-newsletter-list';
    }

    protected function _toHtml()
    {
        if (! Mage::getSingleton('adminhtml/quote')->getIsOldCustomer()) {
            return parent::_toHtml();
        }
        return '';
    }

}