text:
/*
 * This driver supports reading and writing GTAs (Generic Tagged Arrays). See
 * http://www.nongnu.org/gta/ for details on this format.
 *
 * Supported Features:
 * - CreateCopy().
 * - GTA compression can be set.
 * - Raster data is updatable for uncompressed GTAs.
 * - All input/output is routed through the VSIF*L functions
 *   (GDAL_DCAP_VIRTUALIO is set to "YES").
 * - All kinds of metadata are supported (see tag list below).
 *
 * Limitations:
 * - Only uncompressed GTAs can be updated.
 * - Only raster data updates are possible; metadata cannot be changed.
 * - Color palettes are not supported.
 * - CInt16 is stored as gta::cfloat32, and CInt32 as gta::cfloat64.
 * - GDAL metadata is assumed to be in UTF-8 encoding, so that no conversion is
 *   necessary to store it in GTA tags. I'm not sure that this is correct, but
 *   since some metadata might not be representable in the local encoding (e.g.
 *   a chinese description in latin1), using UTF-8 seems reasonable.
 *
 * The following could be implemented, but currently is not:
 * - Allow metadata updates by using a special GDAL/METADATA_BUFFER tag that
 *   contains a number of spaces as a placeholder for additional metadata, so
 *   that the header size on disk can be kept constant.
 * - Implement Create().
 * - Implement AddBand() for uncompressed GTAs. But this would be inefficient:
 *   the old data would need to be copied to a temporary file, and then copied
 *   back while extending it with the new band.
 * - When strict conversion is requested, store CInt16 in 2 x gta::int16 and
 *   CInt32 in 2 x gta::int32, and mark these components with special flags so
 *   that this is reverted when opening the GTA.
 * - Support color palettes by storing the palette in special tags.
 *
 * This driver supports the following standard GTA tags:
 * DESCRIPTION
 * INTERPRETATION
 * NO_DATA_VALUE
 * MIN_VALUE
 * MAX_VALUE
 * UNIT
 *
 * Additionally, the following tags are used for GDAL-specific metadata:
 * GDAL/PROJECTION      (WKT)
 * GDAL/GEO_TRANSFORM   (6 doubles)
 * GDAL/OFFSET          (1 double)
 * GDAL/SCALE           (1 double)
 * GDAL/GCP_PROJECTION  (WKT)
 * GDAL/GCP_COUNT       (1 int > 0)
 * GDAL/GCP%d           (5 doubles)
 * GDAL/GCP%d_INFO      (String)
 * GDAL/CATEGORY_COUNT  (1 int > 0)
 * GDAL/CATEGORY%d      (String)
 * GDAL/META/DEFAULT/%s (String)
 * GDAL/META/RCP/%s     (String)
 */

#include <limits.h>
#include "cpl_port.h" // for snprintf for MSVC
#include <gta/gta.hpp>
#include "gdal_pam.h"

CPL_CVSID("$Id: gtadataset.cpp 27044 2014-03-16 23:41:27Z rouault $");

CPL_C_START
void    GDALRegister_GTA(void);
CPL_C_END

/************************************************************************/
/* Helper functions                                                     */
/************************************************************************/

static void ScanDoubles( const char *pszString, double *padfDoubles, int nCount )

{
    char *pszRemainingString = (char *)pszString;
    for( int i = 0; i < nCount; i++ )
    {
        padfDoubles[i] = 0.0;   // fallback value
        padfDoubles[i] = CPLStrtod( pszRemainingString, &pszRemainingString );
    }
}

static CPLString PrintDoubles( const double *padfDoubles, int nCount )

{
    CPLString oString;
    for( int i = 0; i < nCount; i++ )
    {
        oString.FormatC( padfDoubles[i], "%.16g" );
        if( i < nCount - 1)
        {
            oString += ' ';
        }
    }
    return oString;
}

/************************************************************************/
/* ==================================================================== */
/* GTA custom IO class using GDAL's IO abstraction layer                */
/* ==================================================================== */
/************************************************************************/

class GTAIO : public gta::custom_io
{
  private:
    VSILFILE *fp;

  public:
    GTAIO( ) throw ()
        : fp( NULL )
    {
    }
    ~GTAIO( )
    {
        close( );
    }

    int open( const char *pszFilename, const char *pszMode )
    {
        fp = VSIFOpenL( pszFilename, pszMode );
        return ( fp == NULL ? -1 : 0 );
    }

    void close( )
    {
        if( fp != NULL )
        {
            VSIFCloseL( fp );
            fp = NULL;
        }
    }

    vsi_l_offset tell( )
    {
        return VSIFTellL( fp );
    }

    virtual size_t read(void *buffer, size_t size, bool *error) throw ()
    {
        size_t s;
        s = VSIFReadL( buffer, 1, size, fp );
        if( s != size )
        {
            errno = EIO;
            *error = true;
        }
        return size;
    }

    virtual size_t write(const void *buffer, size_t size, bool *error) throw ()
    {
        size_t s;
        s = VSIFWriteL( buffer, 1, size, fp );
        if( s != size )
        {
            errno = EIO;
            *error = true;
        }
        return size;
    }

    virtual bool seekable() throw ()
    {
        return true;
    }

    virtual void seek(intmax_t offset, int whence, bool *error) throw ()
    {
        int r;
        r = VSIFSeekL( fp, offset, whence );
        if( r != 0 )
        {
            errno = EIO;
            *error = true;
        }
    }
};

/************************************************************************/
/* ==================================================================== */
/*                              GTADataset                              */
/* ==================================================================== */
/************************************************************************/

class GTARasterBand;

class GTADataset : public GDALPamDataset
{
    friend class GTARasterBand;

  private:
    // GTA input/output via VSIF*L functions
    GTAIO       oGTAIO;
    // GTA information
    gta::header oHeader;
    vsi_l_offset DataOffset;
    // Metadata
    bool        bHaveGeoTransform;
    double      adfGeoTransform[6];
    int         nGCPs;
    char        *pszGCPProjection;
    GDAL_GCP    *pasGCPs;
    // Cached data block for block-based input/output
    int         nLastBlockXOff, nLastBlockYOff;
    void        *pBlock;

    // Block-based input/output of all bands at once. This is used
    // by the GTARasterBand input/output functions.
    CPLErr      ReadBlock( int, int );
    CPLErr      WriteBlock( );

  public:
                GTADataset();
                ~GTADataset();

    static GDALDataset *Open( GDALOpenInfo * );

    CPLErr      GetGeoTransform( double * padfTransform );
    CPLErr      SetGeoTransform( double * padfTransform );

    const char *GetProjectionRef( );
    CPLErr      SetProjection( const char *pszProjection );

    int         GetGCPCount( );
    const char *GetGCPProjection( );
    const GDAL_GCP *GetGCPs( );
    CPLErr      SetGCPs( int, const GDAL_GCP *, const char * );
};

/************************************************************************/
/* ==================================================================== */
/*                            GTARasterBand                             */
/* ==================================================================== */
/************************************************************************/

class GTARasterBand : public GDALPamRasterBand
{
    friend class GTADataset;
  private:
    // Size of the component represented by this band
    size_t      sComponentSize;
    // Offset of the component represented by this band inside a GTA element
    size_t      sComponentOffset;
    // StringList for category names
    char      **papszCategoryNames;
    // StringList for metadata
    char      **papszMetaData;

  public:
                GTARasterBand( GTADataset *, int );
                ~GTARasterBand( );

    CPLErr      IReadBlock( int, int, void * );
    CPLErr      IWriteBlock( int, int, void * );

    char      **GetCategoryNames( );
    CPLErr      SetCategoryNames( char ** );

    double      GetMinimum( int * );
    double      GetMaximum( int * );

    double      GetNoDataValue( int * );
    CPLErr      SetNoDataValue( double );
    double      GetOffset( int * );
    CPLErr      SetOffset( double );
    double      GetScale( int * );
    CPLErr      SetScale( double );
    const char *GetUnitType( );
    CPLErr      SetUnitType( const char * );
    GDALColorInterp GetColorInterpretation( );
    CPLErr      SetColorInterpretation( GDALColorInterp );
};

/************************************************************************/
/*                           GTARasterBand()                            */
/************************************************************************/

GTARasterBand::GTARasterBand( GTADataset *poDS, int nBand )

{
    this->poDS = poDS;
    this->nBand = nBand;

    // Data type
    switch( poDS->oHeader.component_type( nBand-1 ) )
    {
    case gta::int8:
        eDataType = GDT_Byte;
        SetMetadataItem("PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE");
        break;
    case gta::uint8:
        eDataType = GDT_Byte;
        break;
    case gta::int16:
        eDataType = GDT_Int16;
        break;
    case gta::uint16:
        eDataType = GDT_UInt16;
        break;
    case gta::int32:
        eDataType = GDT_Int32;
        break;
    case gta::uint32:
        eDataType = GDT_UInt32;
        break;
    case gta::float32:
        eDataType = GDT_Float32;
        break;
    case gta::float64:
        eDataType = GDT_Float64;
        break;
    case gta::cfloat32:
        eDataType = GDT_CFloat32;
        break;
    case gta::cfloat64:
        eDataType = GDT_CFloat64;
        break;
    default:
        // cannot happen because we checked this in GTADataset::Open()
        break;
    }

    // Block size
    nBlockXSize = poDS->GetRasterXSize();
    nBlockYSize = 1;

    // Component information
    sComponentSize = poDS->oHeader.component_size( nBand-1 );
    sComponentOffset = 0;
    for( int i = 0; i < nBand-1; i++ )
    {
        sComponentOffset += poDS->oHeader.component_size( i );
    }

    // Metadata
    papszCategoryNames = NULL;
    papszMetaData = NULL;
    if( poDS->oHeader.component_taglist( nBand-1 ).get( "DESCRIPTION" ) )
    {
        SetDescription( poDS->oHeader.component_taglist( nBand-1 ).get( "DESCRIPTION" ) );
    }
    for( uintmax_t i = 0; i < poDS->oHeader.component_taglist( nBand-1 ).tags(); i++)
    {
        const char *pszTagName = poDS->oHeader.component_taglist( nBand-1 ).name( i );
        if( strncmp( pszTagName, "GDAL/META/", 10 ) == 0 )
        {
            const char *pDomainEnd = strchr( pszTagName + 10, '/' );
            if( pDomainEnd && pDomainEnd - (pszTagName + 10) > 0 )
            {
                char *pszDomain = (char *)VSIMalloc( pDomainEnd - (pszTagName + 10) + 1 );
                if( !pszDomain )
                {
                    continue;
                }
                int j;
                for( j = 0; j < pDomainEnd - (pszTagName + 10); j++ )
                {
                    pszDomain[j] = pszTagName[10 + j];
                }
                pszDomain[j] = '\0';
                const char *pszName = pszTagName + 10 + j + 1;
                const char *pszValue = poDS->oHeader.component_taglist( nBand-1 ).value( i );
                SetMetadataItem( pszName, pszValue,
                        strcmp( pszDomain, "DEFAULT" ) == 0 ? NULL : pszDomain );
                VSIFree( pszDomain );
            }
        }
    }
}

/************************************************************************/
/*                           ~GTARasterBand()                           */
/************************************************************************/

GTARasterBand::~GTARasterBand( )

{
    CSLDestroy( papszCategoryNames );
    CSLDestroy( papszMetaData );
}

/************************************************************************/
/*                             GetCategoryNames()                       */
/************************************************************************/

char **GTARasterBand::GetCategoryNames( )

{
    if( !papszCategoryNames )
    {
        GTADataset *poGDS = (GTADataset *) poDS;
        const char *pszCatCount = poGDS->oHeader.component_taglist( nBand-1 ).get( "GDAL/CATEGORY_COUNT" );
        int nCatCount = 0;
        if( pszCatCount )
        {
            nCatCount = atoi( pszCatCount );
        }
        if( nCatCount > 0 )
        {
            for( int i = 0; i < nCatCount; i++ )
            {
                const char *pszCatName = poGDS->oHeader.component_taglist( nBand-1 ).get(
                        CPLSPrintf( "GDAL/CATEGORY%d", i ) );
                papszCategoryNames = CSLAddString( papszCategoryNames, pszCatName ? pszCatName : "" );
            }
        }
    }
    return papszCategoryNames;
}

/************************************************************************/
/*                             SetCategoryName()                        */
/************************************************************************/

CPLErr GTARasterBand::SetCategoryNames( char ** )

{
    CPLError( CE_Warning, CPLE_NotSupported,
            "The GTA driver does not support metadata updates.\n" );
    return CE_Failure;
}

/************************************************************************/
/*                             GetMinimum()                             */
/************************************************************************/

double GTARasterBand::GetMinimum( int *pbSuccess )

{
    GTADataset *poGDS = (GTADataset *) poDS;
    const char *pszValue = poGDS->oHeader.component_taglist( nBand-1 ).get( "MIN_VALUE" );
    if( pszValue )
    {
        if( pbSuccess )
            *pbSuccess = true;
        return CPLAtof( pszValue );
    }
    else
    {
        return GDALRasterBand::GetMinimum( pbSuccess );
    }
}

/************************************************************************/
/*                             GetMaximum()                             */
/************************************************************************/

double GTARasterBand::GetMaximum( int *pbSuccess  )

{
    GTADataset *poGDS = (GTADataset *) poDS;
    const char *pszValue = poGDS->oHeader.component_taglist( nBand-1 ).get( "MAX_VALUE" );
    if( pszValue )
    {
        if( pbSuccess )
            *pbSuccess = true;
        return CPLAtof( pszValue );
    }
    else
    {
        return GDALRasterBand::GetMaximum( pbSuccess );
    }
}

/************************************************************************/
/*                             GetNoDataValue()                         */
/************************************************************************/

double GTARasterBand::GetNoDataValue( int *pbSuccess )

{
    GTADataset *poGDS = (GTADataset *) poDS;
    const char *pszValue = poGDS->oHeader.component_taglist( nBand-1 ).get( "NO_DATA_VALUE" );
    if( pszValue )
    {
        if( pbSuccess )
            *pbSuccess = true;
        return CPLAtof( pszValue );
    }
    else
    {
        return GDALRasterBand::GetNoDataValue( pbSuccess );
    }
}

/************************************************************************/
/*                             SetNoDataValue()                         */
/************************************************************************/

CPLErr GTARasterBand::SetNoDataValue( double )

{
    CPLError( CE_Warning, CPLE_NotSupported,
            "The GTA driver does not support metadata updates.\n" );
    return CE_Failure;
}

/************************************************************************/
/*                             GetOffset()                              */
/************************************************************************/

double GTARasterBand::GetOffset( int *pbSuccess )

{
    GTADataset *poGDS = (GTADataset *) poDS;
    const char *pszValue = poGDS->oHeader.component_taglist( nBand-1 ).get( "GDAL/OFFSET" );
    if( pszValue )
    {
        if( pbSuccess )
            *pbSuccess = true;
        return CPLAtof( pszValue );
    }
    else
    {
        return GDALRasterBand::GetOffset( pbSuccess );
    }
}

/************************************************************************/
/*                             SetOffset()                              */
/************************************************************************/

CPLErr GTARasterBand::SetOffset( double )

{
    CPLError( CE_Warning, CPLE_NotSupported,
            "The GTA driver does not support metadata updates.\n" );
    return CE_Failure;
}

/************************************************************************/
/*                             GetScale()                               */
/************************************************************************/

double GTARasterBand::GetScale( int *pbSuccess )

{
    GTADataset *poGDS = (GTADataset *) poDS;
    const char *pszValue = poGDS->oHeader.component_taglist( nBand-1 ).get( "GDAL/SCALE" );
    if( pszValue )
    {
        if( pbSuccess )
            *pbSuccess = true;
        return CPLAtof( pszValue );
    }
    else
    {
        return GDALRasterBand::GetScale( pbSuccess );
    }
}

/************************************************************************/
/*                             SetScale()                               */
/************************************************************************/

CPLErr GTARasterBand::SetScale( double )

{
    CPLError( CE_Warning, CPLE_NotSupported,
            "The GTA driver does not support metadata updates.\n" );
    return CE_Failure;
}

/************************************************************************/
/*                             GetUnitType()                            */
/************************************************************************/

const char *GTARasterBand::GetUnitType( )

{
    GTADataset *poGDS = (GTADataset *) poDS;
    const char *pszValue = poGDS->oHeader.component_taglist( nBand-1 ).get( "UNIT" );
    return pszValue ? pszValue : "";
}

/************************************************************************/
/*                             SetUnitType()                            */
/************************************************************************/

CPLErr GTARasterBand::SetUnitType( const char * )

{
    CPLError( CE_Warning, CPLE_NotSupported,
            "The GTA driver does not support metadata updates.\n" );
    return CE_Failure;
}

/************************************************************************/
/*                             GetColorInterpretation()                 */
/************************************************************************/

GDALColorInterp GTARasterBand::GetColorInterpretation( )

{
    GTADataset *poGDS = (GTADataset *) poDS;
    const char *pszColorInterpretation =
        poGDS->oHeader.component_taglist( nBand-1 ).get(
                "INTERPRETATION" );
    if( pszColorInterpretation )
    {
        if( EQUAL( pszColorInterpretation, "GRAY" ) )
            return GCI_GrayIndex ;
        else if ( EQUAL( pszColorInterpretation, "RED" ) )
            return GCI_RedBand ;
        else if ( EQUAL( pszColorInterpretation, "GREEN" ) )
            return GCI_GreenBand ;
        else if ( EQUAL( pszColorInterpretation, "BLUE" ) )
            return GCI_BlueBand ;
        else if ( EQUAL( pszColorInterpretation, "ALPHA" ) )
            return GCI_AlphaBand ;
        else if ( EQUAL( pszColorInterpretation, "HSL/H" ) )
            return GCI_HueBand ;
        else if ( EQUAL( pszColorInterpretation, "HSL/S" ) )
            return GCI_SaturationBand ;
        else if ( EQUAL( pszColorInterpretation, "HSL/L" ) )
            return GCI_LightnessBand ;
        else if ( EQUAL( pszColorInterpretation, "CMYK/C" ) )
            return GCI_CyanBand ;
        else if ( EQUAL( pszColorInterpretation, "CMYK/M" ) )
            return GCI_MagentaBand ;
        else if ( EQUAL( pszColorInterpretation, "CMYK/Y" ) )
            return GCI_YellowBand ;
        else if ( EQUAL( pszColorInterpretation, "CMYK/K" ) )
            return GCI_BlackBand ;
        else if ( EQUAL( pszColorInterpretation, "YCBCR/Y" ) )
            return GCI_YCbCr_YBand;
        else if ( EQUAL( pszColorInterpretation, "YCBCR/CB" ) )
            return GCI_YCbCr_CbBand;
        else if ( EQUAL( pszColorInterpretation, "YCBCR/CR" ) )
            return GCI_YCbCr_CrBand;
    }
    return GCI_Undefined;
}

/************************************************************************/
/*                             SetColorInterpretation()                 */
/************************************************************************/

CPLErr GTARasterBand::SetColorInterpretation( GDALColorInterp )

{
    CPLError( CE_Warning, CPLE_NotSupported,
            "The GTA driver does not support metadata updates.\n" );
    return CE_Failure;
}

/************************************************************************/
/*                             IReadBlock()                             */
/************************************************************************/

CPLErr GTARasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
                                  void * pImage )

{
    GTADataset *poGDS = (GTADataset *) poDS;

    // Read and cache block containing all bands at once
    if( poGDS->ReadBlock( nBlockXOff, nBlockYOff ) != CE_None )
    {
        return CE_Failure;
    }

    char *pBlock = (char *)poGDS->pBlock;
    if( poGDS->oHeader.compression() != gta::none )
    {
        // pBlock contains the complete data set. Add the offset into the
        // requested block. This assumes that nBlockYSize == 1 and
        // nBlockXSize == nRasterXSize.
        pBlock += nBlockYOff * nBlockXSize * poGDS->oHeader.element_size();
    }

    // Copy the data for this band from the cached block
    for( int i = 0; i < nBlockXSize; i++ )
    {
        char *pSrc = pBlock + i * poGDS->oHeader.element_size() + sComponentOffset;
        char *pDst = (char *) pImage + i * sComponentSize;
        memcpy( (void *) pDst, (void *) pSrc, sComponentSize );
    }

    return CE_None;
}

/************************************************************************/
/*                             IWriteBlock()                            */
/************************************************************************/

CPLErr GTARasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
                                  void * pImage )

{
    GTADataset *poGDS = (GTADataset *) poDS;

    if( poGDS->oHeader.compression() != gta::none )
    {
        CPLError( CE_Warning, CPLE_NotSupported,
                "The GTA driver cannot update compressed GTAs.\n" );
        return CE_Failure;
    }

    // Read and cache block containing all bands at once
    if( poGDS->ReadBlock( nBlockXOff, nBlockYOff ) != CE_None )
    {
        return CE_Failure;
    }
    char *pBlock = (char *)poGDS->pBlock;

    // Copy the data for this band into the cached block
    for( int i = 0; i < nBlockXSize; i++ )
    {
        char *pSrc = (char *) pImage + i * sComponentSize;
        char *pDst = pBlock + i * poGDS->oHeader.element_size() + sComponentOffset;
        memcpy( (void *) pDst, (void *) pSrc, sComponentSize );
    }

    // Write the block that conatins all bands at once
    if( poGDS->WriteBlock( ) != CE_None )
    {
        return CE_Failure;
    }

    return CE_None;
}

/************************************************************************/
/* ==================================================================== */
/*                              GTADataset                              */
/* ==================================================================== */
/************************************************************************/

/************************************************************************/
/*                            GTADataset()                              */
/************************************************************************/

GTADataset::GTADataset()

{
    // Initialize Metadata
    bHaveGeoTransform = false;
    nGCPs = 0;
    pszGCPProjection = NULL;
    pasGCPs = NULL;
    // Initialize block-based input/output
    nLastBlockXOff = -1;
    nLastBlockYOff = -1;
    pBlock = NULL;
}

/************************************************************************/
/*                            ~GTADataset()                             */
/************************************************************************/

GTADataset::~GTADataset()

{
    FlushCache();
    VSIFree( pszGCPProjection );
    for( int i = 0; i < nGCPs; i++ )
    {
        VSIFree( pasGCPs[i].pszId );
        VSIFree( pasGCPs[i].pszInfo );
    }
    VSIFree( pasGCPs );
    VSIFree( pBlock );
}

/************************************************************************/
/*                             ReadBlock()                              */
/************************************************************************/

CPLErr GTADataset::ReadBlock( int nBlockXOff, int nBlockYOff )

{
    /* Compressed data sets must be read into memory completely.
     * Uncompressed data sets are read block-wise. */

    if( oHeader.compression() != gta::none )
    {
        if( pBlock == NULL )
        {
            if( oHeader.data_size() > (size_t)(-1)
                    || ( pBlock = VSIMalloc( oHeader.data_size() ) ) == NULL )
            {
                CPLError( CE_Failure, CPLE_OutOfMemory,
                        "Cannot allocate buffer for the complete data set.\n"
                        "Try to uncompress the data set to allow block-wise "
                        "reading.\n" );
                return CE_Failure;
            }

            try
            {
                oHeader.read_data( oGTAIO, pBlock );
            }
            catch( gta::exception &e )
            {
                CPLError( CE_Failure, CPLE_FileIO, "GTA error: %s\n", e.what() );
                return CE_Failure;
            }
        }
    }
    else
    {
        // This has to be the same as in the RasterBand constructor!
        int nBlockXSize = GetRasterXSize();
        int nBlockYSize = 1;

        if( nLastBlockXOff == nBlockXOff && nLastBlockYOff == nBlockYOff )
            return CE_None;

        if( pBlock == NULL )
        {
            pBlock = VSIMalloc2( oHeader.element_size(), nBlockXSize );
            if( pBlock == NULL )
            {
                CPLError( CE_Failure, CPLE_OutOfMemory,
                        "Cannot allocate scanline buffer" );
                return CE_Failure;
            }
        }

        try
        {
            uintmax_t lo[2] = { (uintmax_t)nBlockXOff * nBlockXSize, (uintmax_t)nBlockYOff * nBlockYSize};
            uintmax_t hi[2] = { lo[0] + nBlockXSize - 1, lo[1] + nBlockYSize - 1 };
            oHeader.read_block( oGTAIO, DataOffset, lo, hi, pBlock );
        }
        catch( gta::exception &e )
        {
            CPLError( CE_Failure, CPLE_FileIO, "GTA error: %s\n", e.what() );
            return CE_Failure;
        }

        nLastBlockXOff = nBlockXOff;
        nLastBlockYOff = nBlockYOff;
    }
    return CE_None;
}

/************************************************************************/
/*                             WriteBlock()                             */
/************************************************************************/

CPLErr GTADataset::WriteBlock( )

{
    // This has to be the same as in the RasterBand constructor!
    int nBlockXSize = GetRasterXSize();
    int nBlockYSize = 1;

    // Write the block (nLastBlockXOff, nLastBlockYOff) stored in pBlock.
    try
    {
        uintmax_t lo[2] = { (uintmax_t)nLastBlockXOff * nBlockXSize, (uintmax_t)nLastBlockYOff * nBlockYSize};
        uintmax_t hi[2] = { lo[0] + nBlockXSize - 1, lo[1] + nBlockYSize - 1 };
        oHeader.write_block( oGTAIO, DataOffset, lo, hi, pBlock );
    }
    catch( gta::exception &e )
    {
        CPLError( CE_Failure, CPLE_FileIO, "GTA error: %s\n", e.what() );
        return CE_Failure;
    }

    return CE_None;
}

/************************************************************************/
/*                          GetGeoTransform()                           */
/************************************************************************/

CPLErr GTADataset::GetGeoTransform( double * padfTransform )

{
    if( bHaveGeoTransform )
    {
        memcpy( padfTransform, adfGeoTransform, 6*sizeof(double) );
        return CE_None;
    }
    else
    {
        return CE_Failure;
    }
}

/************************************************************************/
/*                          SetGeoTransform()                           */
/************************************************************************/

CPLErr GTADataset::SetGeoTransform( double * )

{
    CPLError( CE_Warning, CPLE_NotSupported,
            "The GTA driver does not support metadata updates.\n" );
    return CE_Failure;
}

/************************************************************************/
/*                          GetProjectionRef()                          */
/************************************************************************/

const char *GTADataset::GetProjectionRef()

{
    const char *p = oHeader.global_taglist().get("GDAL/PROJECTION");
    return ( p ? p : "" );
}

/************************************************************************/
/*                          SetProjection()                             */
/************************************************************************/

CPLErr GTADataset::SetProjection( const char * )

{
    CPLError( CE_Warning, CPLE_NotSupported,
            "The GTA driver does not support metadata updates.\n" );
    return CE_Failure;
}

/************************************************************************/
/*                          GetGCPCount()                               */
/************************************************************************/

int GTADataset::GetGCPCount( )

{
    return nGCPs;
}

/************************************************************************/
/*                          GetGCPProjection()                          */
/************************************************************************/

const char * GTADataset::GetGCPProjection( )

{
    return pszGCPProjection ? pszGCPProjection : "";
}

/************************************************************************/
/*                          GetGCPs()                                   */
/************************************************************************/

const GDAL_GCP * GTADataset::GetGCPs( )

{
    return pasGCPs;
}

/************************************************************************/
/*                          SetGCPs()                                   */
/************************************************************************/

CPLErr GTADataset::SetGCPs( int, const GDAL_GCP *, const char * )

{
    CPLError( CE_Warning, CPLE_NotSupported,
            "The GTA driver does not support metadata updates.\n" );
    return CE_Failure;
}

/************************************************************************/
/*                                Open()                                */
/************************************************************************/

GDALDataset *GTADataset::Open( GDALOpenInfo * poOpenInfo )

{
    if( poOpenInfo->nHeaderBytes < 5 )
        return NULL;

    if( !EQUALN((char *)poOpenInfo->pabyHeader,"GTA",3) )
        return NULL;

/* -------------------------------------------------------------------- */
/*      Create a corresponding GDALDataset.                             */
/* -------------------------------------------------------------------- */
    GTADataset  *poDS;

    poDS = new GTADataset();

    if( poDS->oGTAIO.open( poOpenInfo->pszFilename,
            poOpenInfo->eAccess == GA_Update ? "r+" : "r" ) != 0 )
    {
        CPLError( CE_Failure, CPLE_OpenFailed, "Cannot open file.\n" );
        delete poDS;
        return NULL;
    }

/* -------------------------------------------------------------------- */
/*      Read the header.                                                */
/* -------------------------------------------------------------------- */

    try
    {
        poDS->oHeader.read_from( poDS->oGTAIO );
    }
    catch( gta::exception &e )
    {
        CPLError( CE_Failure, CPLE_OpenFailed, "GTA error: %s\n", e.what() );
        delete poDS;
        return NULL;
    }
    poDS->DataOffset = poDS->oGTAIO.tell();
    poDS->eAccess = poOpenInfo->eAccess;

    if( poDS->oHeader.compression() != gta::none
            && poOpenInfo->eAccess == GA_Update )
    {
        CPLError( CE_Failure, CPLE_NotSupported,
                  "The GTA driver does not support update access to compressed "
                  "data sets.\nUncompress the data set first.\n" );
        delete poDS;
        return NULL;
    }

    if( poDS->oHeader.dimensions() != 2 )
    {
        CPLError( CE_Failure, CPLE_NotSupported,
                "The GTA driver does not support GTAs with %s than 2 "
                "dimensions.\n",
                poDS->oHeader.dimensions() < 2 ? "less" : "more" );
        delete poDS;
        return NULL;
    }

    // We know the dimensions are > 0 (guaranteed by libgta), but they may be
    // unrepresentable in GDAL.
    if( poDS->oHeader.dimension_size(0) > INT_MAX
            || poDS->oHeader.dimension_size(1) > INT_MAX )
    {
        CPLError( CE_Failure, CPLE_NotSupported,
                "The GTA driver does not support the size of this data set.\n" );
        delete poDS;
        return NULL;
    }
    poDS->nRasterXSize = poDS->oHeader.dimension_size(0);
    poDS->nRasterYSize = poDS->oHeader.dimension_size(1);

    // Check the number of bands (called components in GTA)
    if( poDS->oHeader.components() > INT_MAX-1
            || poDS->oHeader.element_size() > ((size_t)-1) )
    {
        CPLError( CE_Failure, CPLE_NotSupported,
                "The GTA driver does not support the number or size of bands "
                "in this data set.\n" );
        delete poDS;
        return NULL;
    }
    poDS->nBands = poDS->oHeader.components();

    // Check the data types (called component types in GTA)
    for( int iBand = 0; iBand < poDS->nBands; iBand++ )
    {
        if( poDS->oHeader.component_type(iBand) != gta::uint8
                && poDS->oHeader.component_type(iBand) != gta::int8
                && poDS->oHeader.component_type(iBand) != gta::uint16
                && poDS->oHeader.component_type(iBand) != gta::int16
                && poDS->oHeader.component_type(iBand) != gta::uint32
                && poDS->oHeader.component_type(iBand) != gta::int32
                && poDS->oHeader.component_type(iBand) != gta::float32
                && poDS->oHeader.component_type(iBand) != gta::float64
                && poDS->oHeader.component_type(iBand) != gta::cfloat32
                && poDS->oHeader.component_type(iBand) != gta::cfloat64 )
        {
            CPLError( CE_Failure, CPLE_NotSupported,
                    "The GTA driver does not support some of the data types "
                    "used in this data set.\n" );
            delete poDS;
            return NULL;
        }
    }

/* -------------------------------------------------------------------- */
/*      Read and set meta information.                                  */
/* -------------------------------------------------------------------- */

    if( poDS->oHeader.global_taglist().get("GDAL/GEO_TRANSFORM") )
    {
        poDS->bHaveGeoTransform = true;
        ScanDoubles( poDS->oHeader.global_taglist().get( "GDAL/GEO_TRANSFORM" ),
                poDS->adfGeoTransform, 6 );
    }
    else
    {
        poDS->bHaveGeoTransform = false;
    }

    if( poDS->oHeader.global_taglist().get("GDAL/GCP_PROJECTION") )
    {
        poDS->pszGCPProjection = VSIStrdup( poDS->oHeader.global_taglist().get("GDAL/GCP_PROJECTION") );
    }
    if( poDS->oHeader.global_taglist().get("GDAL/GCP_COUNT") )
    {
        poDS->nGCPs = atoi( poDS->oHeader.global_taglist().get("GDAL/GCP_COUNT") );
        if( poDS->nGCPs < 1 )
        {
            poDS->nGCPs = 0;
        }
        else
        {
            poDS->pasGCPs = (GDAL_GCP *)VSIMalloc2( poDS->nGCPs, sizeof(GDAL_GCP) );
            if( poDS->pasGCPs == NULL )
            {
                CPLError( CE_Failure, CPLE_OutOfMemory, "Cannot allocate GCP list" );
                delete poDS;
                return NULL;
            }
            for( int i = 0; i < poDS->nGCPs; i++ )
            {
                poDS->pasGCPs[i].pszInfo = NULL;
                poDS->pasGCPs[i].dfGCPPixel = 0.0;
                poDS->pasGCPs[i].dfGCPLine = 0.0;
                poDS->pasGCPs[i].dfGCPX = 0.0;
                poDS->pasGCPs[i].dfGCPY = 0.0;
                poDS->pasGCPs[i].dfGCPZ = 0.0;
                poDS->pasGCPs[i].pszId = VSIStrdup( CPLSPrintf( "%d", i ) );
                char pszGCPTagName[64];
                char pszGCPInfoTagName[64];
                strcpy( pszGCPTagName, CPLSPrintf( "GDAL/GCP%d", i ) );
                strcpy( pszGCPInfoTagName, CPLSPrintf( "GDAL/GCP%d_INFO", i ) );
                if( poDS->oHeader.global_taglist().get(pszGCPInfoTagName) )
                {
                    poDS->pasGCPs[i].pszInfo = VSIStrdup( poDS->oHeader.global_taglist().get(pszGCPInfoTagName) );
                }
                else
                {
                    poDS->pasGCPs[i].pszInfo = VSIStrdup( "" );
                }
                if( poDS->oHeader.global_taglist().get(pszGCPTagName) )
                {
                    double adfTempDoubles[5];
                    ScanDoubles( poDS->oHeader.global_taglist().get(pszGCPTagName), adfTempDoubles, 5 );
                    poDS->pasGCPs[i].dfGCPPixel = adfTempDoubles[0];
                    poDS->pasGCPs[i].dfGCPLine = adfTempDoubles[1];
                    poDS->pasGCPs[i].dfGCPX = adfTempDoubles[2];
                    poDS->pasGCPs[i].dfGCPY = adfTempDoubles[3];
                    poDS->pasGCPs[i].dfGCPZ = adfTempDoubles[4];
                }
            }
        }
    }

    if( poDS->oHeader.global_taglist().get("DESCRIPTION") )
    {
        poDS->SetDescription( poDS->oHeader.global_taglist().get("DESCRIPTION") );
    }
    for( uintmax_t i = 0; i < poDS->oHeader.global_taglist().tags(); i++)
    {
        const char *pszTagName = poDS->oHeader.global_taglist().name( i );
        if( strncmp( pszTagName, "GDAL/META/", 10 ) == 0 )
        {
            const char *pDomainEnd = strchr( pszTagName + 10, '/' );
            if( pDomainEnd && pDomainEnd - (pszTagName + 10) > 0 )
            {
                char *pszDomain = (char *)VSIMalloc( pDomainEnd - (pszTagName + 10) + 1 );
                if( !pszDomain )
                {
                    CPLError( CE_Failure, CPLE_OutOfMemory, "Cannot allocate metadata buffer" );
                    delete poDS;
                    return NULL;
                }
                int j;
                for( j = 0; j < pDomainEnd - (pszTagName + 10); j++ )
                {
                    pszDomain[j] = pszTagName[10 + j];
                }
                pszDomain[j] = '\0';
                const char *pszName = pszTagName + 10 + j + 1;
                const char *pszValue = poDS->oHeader.global_taglist().value( i );
                poDS->SetMetadataItem( pszName, pszValue,
                        strcmp( pszDomain, "DEFAULT" ) == 0 ? NULL : pszDomain );
                VSIFree( pszDomain );
            }
        }
    }

    if( poDS->nBands > 0 )
    {
        poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" );
    }
    if( poDS->oHeader.compression() == gta::bzip2 )
        poDS->SetMetadataItem( "COMPRESSION", "BZIP2", "IMAGE_STRUCTURE" );
    else if( poDS->oHeader.compression() == gta::xz )
        poDS->SetMetadataItem( "COMPRESSION", "XZ", "IMAGE_STRUCTURE" );
    else if( poDS->oHeader.compression() == gta::zlib )
        poDS->SetMetadataItem( "COMPRESSION", "ZLIB", "IMAGE_STRUCTURE" );
    else if( poDS->oHeader.compression() == gta::zlib1 )
        poDS->SetMetadataItem( "COMPRESSION", "ZLIB1", "IMAGE_STRUCTURE" );
    else if( poDS->oHeader.compression() == gta::zlib2 )
        poDS->SetMetadataItem( "COMPRESSION", "ZLIB2", "IMAGE_STRUCTURE" );
    else if( poDS->oHeader.compression() == gta::zlib3 )
        poDS->SetMetadataItem( "COMPRESSION", "ZLIB3", "IMAGE_STRUCTURE" );
    else if( poDS->oHeader.compression() == gta::zlib4 )
        poDS->SetMetadataItem( "COMPRESSION", "ZLIB4", "IMAGE_STRUCTURE" );
    else if( poDS->oHeader.compression() == gta::zlib5 )
        poDS->SetMetadataItem( "COMPRESSION", "ZLIB5", "IMAGE_STRUCTURE" );
    else if( poDS->oHeader.compression() == gta::zlib6 )
        poDS->SetMetadataItem( "COMPRESSION", "ZLIB6", "IMAGE_STRUCTURE" );
    else if( poDS->oHeader.compression() == gta::zlib7 )
        poDS->SetMetadataItem( "COMPRESSION", "ZLIB7", "IMAGE_STRUCTURE" );
    else if( poDS->oHeader.compression() == gta::zlib8 )
        poDS->SetMetadataItem( "COMPRESSION", "ZLIB8", "IMAGE_STRUCTURE" );
    else if( poDS->oHeader.compression() == gta::zlib9 )
        poDS->SetMetadataItem( "COMPRESSION", "ZLIB9", "IMAGE_STRUCTURE" );

/* -------------------------------------------------------------------- */
/*      Create band information objects.                                */
/* -------------------------------------------------------------------- */
    for( int iBand = 0; iBand < poDS->nBands; iBand++ )
    {
        poDS->SetBand( iBand+1, new GTARasterBand( poDS, iBand+1 ) );
    }

/* -------------------------------------------------------------------- */
/*      Initialize any PAM information.                                 */
/* -------------------------------------------------------------------- */
    poDS->SetDescription( poOpenInfo->pszFilename );
    poDS->TryLoadXML();

/* -------------------------------------------------------------------- */
/*      Check for overviews.                                            */
/* -------------------------------------------------------------------- */
    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );

    return( poDS );
}

/************************************************************************/
/*                             CreateCopy()                             */
/************************************************************************/

static GDALDataset*
GTACreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
                int bStrict, char ** papszOptions,
                GDALProgressFunc pfnProgress, void * pProgressData )

{
    if( !pfnProgress( 0.0, NULL, pProgressData ) )
        return NULL;

/* -------------------------------------------------------------------- */
/*      Create a GTA header                                             */
/* -------------------------------------------------------------------- */

    gta::compression eGTACompression = gta::none;
    const char *pszCompressionValue = CSLFetchNameValue( papszOptions,
            "COMPRESS" );
    if( pszCompressionValue != NULL )
    {
        if( EQUAL( pszCompressionValue, "NONE" ) )
            eGTACompression = gta::none;
        else if( EQUAL( pszCompressionValue, "BZIP2" ) )
            eGTACompression = gta::bzip2;
        else if( EQUAL( pszCompressionValue, "XZ" ) )
            eGTACompression = gta::xz;
        else if( EQUAL( pszCompressionValue, "ZLIB" ))
            eGTACompression = gta::zlib;
        else if( EQUAL( pszCompressionValue, "ZLIB1" ))
            eGTACompression = gta::zlib1;
        else if( EQUAL( pszCompressionValue, "ZLIB2" ))
            eGTACompression = gta::zlib2;
        else if( EQUAL( pszCompressionValue, "ZLIB3" ))
            eGTACompression = gta::zlib3;
        else if( EQUAL( pszCompressionValue, "ZLIB4" ))
            eGTACompression = gta::zlib4;
        else if( EQUAL( pszCompressionValue, "ZLIB5" ))
            eGTACompression = gta::zlib5;
        else if( EQUAL( pszCompressionValue, "ZLIB6" ))
            eGTACompression = gta::zlib6;
        else if( EQUAL( pszCompressionValue, "ZLIB7" ))
            eGTACompression = gta::zlib7;
        else if( EQUAL( pszCompressionValue, "ZLIB8" ))
            eGTACompression = gta::zlib8;
        else if( EQUAL( pszCompressionValue, "ZLIB9" ))
            eGTACompression = gta::zlib9;
        else
            CPLError( CE_Warning, CPLE_IllegalArg,
                      "COMPRESS=%s value not recognised, ignoring.",
                      pszCompressionValue );
    }

    gta::type *peGTATypes = (gta::type *)VSIMalloc2( poSrcDS->GetRasterCount(), sizeof(gta::type) );
    if( peGTATypes == NULL )
    {
        CPLError( CE_Failure, CPLE_OutOfMemory, "Cannot allocate GTA type list" );
        return NULL;
    }
    for( int i = 0; i < poSrcDS->GetRasterCount(); i++ )
    {
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( i+1 );
        if( poSrcBand->GetColorInterpretation() == GCI_PaletteIndex )
        {
            CPLError( CE_Failure, CPLE_NotSupported,
                    "The GTA driver does not support color palettes.\n" );
            VSIFree( peGTATypes );
            return NULL;
        }
        GDALDataType eDT = poSrcBand->GetRasterDataType();
        switch( eDT )
        {
        case GDT_Byte:
        {
            const char *pszPixelType = poSrcBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
            if (pszPixelType && EQUAL(pszPixelType, "SIGNEDBYTE"))
                peGTATypes[i] = gta::int8;
            else
                peGTATypes[i] = gta::uint8;
            break;
        }
        case GDT_UInt16:
            peGTATypes[i] = gta::uint16;
            break;
        case GDT_Int16:
            peGTATypes[i] = gta::int16;
            break;
        case GDT_UInt32:
            peGTATypes[i] = gta::uint32;
            break;
        case GDT_Int32:
            peGTATypes[i] = gta::int32;
            break;
        case GDT_Float32:
            peGTATypes[i] = gta::float32;
            break;
        case GDT_Float64:
            peGTATypes[i] = gta::float64;
            break;
        case GDT_CInt16:
            if( bStrict )
            {
                CPLError( CE_Failure, CPLE_NotSupported,
                        "The GTA driver does not support the CInt16 data "
                        "type.\n"
                        "(If no strict copy is required, the driver can "
                        "use CFloat32 instead.)\n" );
                VSIFree( peGTATypes );
                return NULL;
            }
            peGTATypes[i] = gta::cfloat32;
            break;
        case GDT_CInt32:
            if( bStrict )
            {
                CPLError( CE_Failure, CPLE_NotSupported,
                        "The GTA driver does not support the CInt32 data "
                        "type.\n"
                        "(If no strict copy is required, the driver can "
                        "use CFloat64 instead.)\n" );
                VSIFree( peGTATypes );
                return NULL;
            }
            peGTATypes[i] = gta::cfloat64;
            break;
        case GDT_CFloat32:
            peGTATypes[i] = gta::cfloat32;
            break;
        case GDT_CFloat64:
            peGTATypes[i] = gta::cfloat64;
            break;
        default:
            CPLError( CE_Failure, CPLE_NotSupported,
                    "The GTA driver does not support source data sets using "
                    "unknown data types.\n");
            VSIFree( peGTATypes );
            return NULL;
        }
    }

    gta::header oHeader;
    try
    {
        oHeader.set_compression( eGTACompression );
        oHeader.set_dimensions( poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize() );
        oHeader.set_components( poSrcDS->GetRasterCount(), peGTATypes );
        const char *pszDescription = poSrcDS->GetDescription();
        // Metadata from GDALMajorObject
        if( pszDescription && pszDescription[0] != '\0' )
        {
            oHeader.global_taglist().set( "DESCRIPTION", pszDescription );
        }
        const char *papszMetadataDomains[] = { NULL /* default */, "RPC" };
        size_t nMetadataDomains = sizeof( papszMetadataDomains ) / sizeof( papszMetadataDomains[0] );
        for( size_t iDomain = 0; iDomain < nMetadataDomains; iDomain++ )
        {
            char **papszMetadata = poSrcDS->GetMetadata( papszMetadataDomains[iDomain] );
            if( papszMetadata )
            {
                for( int i = 0; papszMetadata[i]; i++ )
                {
                    char *pEqualSign = strchr( papszMetadata[i], '=' );
                    if( pEqualSign && pEqualSign - papszMetadata[i] > 0 )
                    {
                        *pEqualSign = '\0';
                        oHeader.global_taglist().set(
                                CPLSPrintf( "GDAL/META/%s/%s",
                                    papszMetadataDomains[iDomain] ? papszMetadataDomains[iDomain] : "DEFAULT",
                                    papszMetadata[i] ),
                                pEqualSign + 1 );
                        *pEqualSign = '=';
                    }
                }
            }
        }
        // Projection and transformation
        const char *pszWKT = poSrcDS->GetProjectionRef();
        if( pszWKT && pszWKT[0] != '\0' )
        {
            oHeader.global_taglist().set( "GDAL/PROJECTION", pszWKT );
        }
        double adfTransform[6];
        if( poSrcDS->GetGeoTransform( adfTransform ) == CE_None )
        {
            oHeader.global_taglist().set( "GDAL/GEO_TRANSFORM",
                    PrintDoubles( adfTransform, 6 ).c_str() );
        }
        // GCPs
        if( poSrcDS->GetGCPCount() > 0 )
        {
            oHeader.global_taglist().set( "GDAL/GCP_COUNT", CPLSPrintf( "%d", poSrcDS->GetGCPCount() ) );
            oHeader.global_taglist().set( "GDAL/GCP_PROJECTION", poSrcDS->GetGCPProjection() );
            const GDAL_GCP *pasGCPs = poSrcDS->GetGCPs();
            for( int i = 0; i < poSrcDS->GetGCPCount(); i++ )
            {
                char pszGCPTagName[64];
                char pszGCPInfoTagName[64];
                strcpy( pszGCPTagName, CPLSPrintf( "GDAL/GCP%d", i ) );
                strcpy( pszGCPInfoTagName, CPLSPrintf( "GDAL/GCP%d_INFO", i ) );
                if( pasGCPs[i].pszInfo && pasGCPs[i].pszInfo[0] != '\0' )
                {
                    oHeader.global_taglist().set( pszGCPInfoTagName, pasGCPs[i].pszInfo );
                }
                double adfTempDoubles[5];
                adfTempDoubles[0] = pasGCPs[i].dfGCPPixel;
                adfTempDoubles[1] = pasGCPs[i].dfGCPLine;
                adfTempDoubles[2] = pasGCPs[i].dfGCPX;
                adfTempDoubles[3] = pasGCPs[i].dfGCPY;
                adfTempDoubles[4] = pasGCPs[i].dfGCPZ;
                oHeader.global_taglist().set( pszGCPTagName, PrintDoubles( adfTempDoubles, 5 ).c_str() );
            }
        }
        // Now the bands
        for( int iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++ )
        {
            GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 );
            // Metadata from GDALMajorObject
            const char *pszBandDescription = poSrcBand->GetDescription();
            if( pszBandDescription && pszBandDescription[0] != '\0' )
            {
                oHeader.component_taglist( iBand ).set( "DESCRIPTION", pszBandDescription );
            }
            for( size_t iDomain = 0; iDomain < nMetadataDomains; iDomain++ )
            {
                char **papszBandMetadata = poSrcBand->GetMetadata( papszMetadataDomains[iDomain] );
                if( papszBandMetadata )
                {
                    for( int i = 0; papszBandMetadata[i]; i++ )
                    {
                        char *pEqualSign = strchr( papszBandMetadata[i], '=' );
                        if( pEqualSign && pEqualSign - papszBandMetadata[i] > 0 )
                        {
                            *pEqualSign = '\0';
                            oHeader.component_taglist( iBand ).set(
                                    CPLSPrintf( "GDAL/META/%s/%s",
                                        papszMetadataDomains[iDomain] ? papszMetadataDomains[iDomain] : "DEFAULT",
                                        papszBandMetadata[i] ),
                                    pEqualSign + 1 );
                            *pEqualSign = '=';
                        }
                    }
                }
            }
            // Category names
            char **papszCategoryNames = poSrcBand->GetCategoryNames( );
            if( papszCategoryNames )
            {
                int i;
                for( i = 0; papszCategoryNames[i]; i++ )
                {
                    oHeader.component_taglist( iBand ).set(
                            CPLSPrintf( "GDAL/CATEGORY%d", i ),
                            papszCategoryNames[i] );
                }
                oHeader.component_taglist( iBand ).set(
                        "GDAL/CATEGORY_COUNT", CPLSPrintf( "%d", i ) );
            }
            // No data value
            int bHaveNoDataValue;
            double dfNoDataValue;
            dfNoDataValue = poSrcBand->GetNoDataValue( &bHaveNoDataValue );
            if( bHaveNoDataValue )
                oHeader.component_taglist( iBand ).set( "NO_DATA_VALUE",
                        PrintDoubles( &dfNoDataValue, 1 ).c_str() );
            // Min/max values
            int bHaveMinValue;
            double dfMinValue;
            dfMinValue = poSrcBand->GetMinimum( &bHaveMinValue );
            if( bHaveMinValue )
                oHeader.component_taglist( iBand ).set( "MIN_VALUE",
                        PrintDoubles( &dfMinValue, 1 ).c_str() );
            int bHaveMaxValue;
            double dfMaxValue;
            dfMaxValue = poSrcBand->GetMaximum( &bHaveMaxValue );
            if( bHaveMaxValue )
                oHeader.component_taglist( iBand ).set( "MAX_VALUE",
                        PrintDoubles( &dfMaxValue, 1 ).c_str() );
            // Offset/scale values
            int bHaveOffsetValue;
            double dfOffsetValue;
            dfOffsetValue = poSrcBand->GetOffset( &bHaveOffsetValue );
            if( bHaveOffsetValue )
                oHeader.component_taglist( iBand ).set( "GDAL/OFFSET",
                        PrintDoubles( &dfOffsetValue, 1 ).c_str() );
            int bHaveScaleValue;
            double dfScaleValue;
            dfScaleValue = poSrcBand->GetScale( &bHaveScaleValue );
            if( bHaveScaleValue )
                oHeader.component_taglist( iBand ).set( "GDAL/SCALE",
                        PrintDoubles( &dfScaleValue, 1 ).c_str() );
            // Unit
            const char *pszUnit = poSrcBand->GetUnitType( );
            if( pszUnit != NULL && pszUnit[0] != '\0' )
                oHeader.component_taglist( iBand ).set( "UNIT", pszUnit );
            // Color interpretation
            GDALColorInterp eCI = poSrcBand->GetColorInterpretation();
            if( eCI == GCI_GrayIndex )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "GRAY" );
            else if( eCI == GCI_RedBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "RED" );
            else if( eCI == GCI_GreenBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "GREEN" );
            else if( eCI == GCI_BlueBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "BLUE" );
            else if( eCI == GCI_AlphaBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "ALPHA" );
            else if( eCI == GCI_HueBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "HSL/H" );
            else if( eCI == GCI_SaturationBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "HSL/S" );
            else if( eCI == GCI_LightnessBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "HSL/L" );
            else if( eCI == GCI_CyanBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "CMYK/C" );
            else if( eCI == GCI_MagentaBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "CMYK/M" );
            else if( eCI == GCI_YellowBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "CMYK/Y" );
            else if( eCI == GCI_BlackBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "CMYK/K" );
            else if( eCI == GCI_YCbCr_YBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "YCBCR/Y" );
            else if( eCI == GCI_YCbCr_CbBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "YCBCR/CB" );
            else if( eCI == GCI_YCbCr_CrBand )
                oHeader.component_taglist( iBand ).set( "INTERPRETATION", "YCBCR/CR" );
        }
    }
    catch( gta::exception &e )
    {
        CPLError( CE_Failure, CPLE_NotSupported, "GTA error: %s\n", e.what() );
        VSIFree( peGTATypes );
        return NULL;
    }
    VSIFree( peGTATypes );

/* -------------------------------------------------------------------- */
/*      Write header and data to the file                               */
/* -------------------------------------------------------------------- */

    GTAIO oGTAIO;
    if( oGTAIO.open( pszFilename, "w" ) != 0 )
    {
        CPLError( CE_Failure, CPLE_OpenFailed,
                "Cannot create GTA file %s.\n", pszFilename );
        return NULL;
    }

    void *pLine = VSIMalloc2( oHeader.element_size(), oHeader.dimension_size(0) );
    if( pLine == NULL )
    {
        CPLError( CE_Failure, CPLE_OutOfMemory, "Cannot allocate scanline buffer.\n" );
        VSIFree( pLine );
        return NULL;
    }

    try
    {
        // Write header
        oHeader.write_to( oGTAIO );
        // Write data line by line
        gta::io_state oGTAIOState;
        for( int iLine = 0; iLine < poSrcDS->GetRasterYSize(); iLine++ )
        {
            size_t nComponentOffset = 0;
            for( int iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++ )
            {
                GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 );
                GDALDataType eDT = poSrcBand->GetRasterDataType();
                if( eDT == GDT_CInt16 )
                {
                    eDT = GDT_CFloat32;
                }
                else if( eDT == GDT_CInt32 )
                {
                    eDT = GDT_CFloat64;
                }
                char *pDst = (char *)pLine + nComponentOffset;
                CPLErr eErr = poSrcBand->RasterIO( GF_Read, 0, iLine,
                        poSrcDS->GetRasterXSize(), 1,
                        pDst, poSrcDS->GetRasterXSize(), 1, eDT,
                        oHeader.element_size(), 0 );
                if( eErr != CE_None )
                {
                    CPLError( CE_Failure, CPLE_FileIO, "Cannot read source data set.\n" );
                    VSIFree( pLine );
                    return NULL;
                }
                nComponentOffset += oHeader.component_size( iBand );
            }
            oHeader.write_elements( oGTAIOState, oGTAIO, poSrcDS->GetRasterXSize(), pLine );
            if( !pfnProgress( (iLine+1) / (double) poSrcDS->GetRasterYSize(),
                        NULL, pProgressData ) )
            {
                CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated CreateCopy()" );
                VSIFree( pLine );
                return NULL;
            }
        }
    }
    catch( gta::exception &e )
    {
        CPLError( CE_Failure, CPLE_FileIO, "GTA write error: %s\n", e.what() );
        VSIFree( pLine );
        return NULL;
    }
    VSIFree( pLine );

    oGTAIO.close();

/* -------------------------------------------------------------------- */
/*      Re-open dataset, and copy any auxilary pam information.         */
/* -------------------------------------------------------------------- */

    GTADataset *poDS = (GTADataset *) GDALOpen( pszFilename,
            eGTACompression == gta::none ? GA_Update : GA_ReadOnly );

    if( poDS )
        poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );

    return poDS;
}

/************************************************************************/
/*                          GDALRegister_GTA()                          */
/************************************************************************/

void GDALRegister_GTA()

{
    GDALDriver  *poDriver;

    if( GDALGetDriverByName( "GTA" ) == NULL )
    {
        poDriver = new GDALDriver();

        poDriver->SetDescription( "GTA" );
        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
                "Generic Tagged Arrays (.gta)" );
        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
                "frmt_gta.html" );
        poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gta" );
        poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
                "Byte UInt16 Int16 UInt32 Int32 Float32 Float64 "
                "CInt16 CInt32 CFloat32 CFloat64" );
        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
                "<CreationOptionList>"
                "  <Option name='COMPRESS' type='string-select'>"
                "    <Value>NONE</Value>"
                "    <Value>BZIP2</Value>"
                "    <Value>XZ</Value>"
                "    <Value>ZLIB</Value>"
                "    <Value>ZLIB1</Value>"
                "    <Value>ZLIB2</Value>"
                "    <Value>ZLIB3</Value>"
                "    <Value>ZLIB4</Value>"
                "    <Value>ZLIB5</Value>"
                "    <Value>ZLIB6</Value>"
                "    <Value>ZLIB7</Value>"
                "    <Value>ZLIB8</Value>"
                "    <Value>ZLIB9</Value>"
                "  </Option>"
                "</CreationOptionList>" );

        poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );

        poDriver->pfnOpen = GTADataset::Open;
        poDriver->pfnCreateCopy = GTACreateCopy;

        GetGDALDriverManager()->RegisterDriver( poDriver );
    }
}


text:
<?php

namespace Admin\Controller;

use Admin\Form\LoginAdminForm;
use Admin\Form\Validator\LoginAdminValidator;
use Admin\Model\Dao\UserDao;
use Exception;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

class LoginController extends AbstractActionController {

    public function indexAction() {
        $request = $this->getRequest();
        $loginForm = new LoginAdminForm();

        if ($request->isPost()) {

            $postData = $request->getPost();

            $loginValidator = $loginForm->setInputFilter(new LoginAdminValidator());
            $loginValidator->setData($postData);

            if ($loginValidator->isValid()) {

                $loginData = $loginValidator->getData();

                $email = $loginData['email'];
                $password = $loginData['password'];

                $userTableGateway = $this->getService('UserAdminTableGateway');
                $userDao = new UserDao($userTableGateway);
                $userData = $userDao->getByEmail($email, array('user_id', 'salt'));
//                var_dump($userData);die;
                if ($userData) {

                    $passwordEncript = md5($password . $userData->salt);
//                    echo $passwordEncript;die;

                    $loginAdmin = $this->getService('Admin\Model\LoginAdmin');

                    try {

                        $loginAdmin->login($email, $passwordEncript);
                        $this->flashMessenger()->addMessage('Login Correcto!', 'success');

                        return $this->redirect()->toRoute('admin');
                    } catch (Exception $e) {
                        //$this->layout()->mensaje = $e->getMessage();
                        $this->flashMessenger()->addMessage($e->getMessage(), 'error');
                        return $this->forward()->dispatch('Admin\Controller\Index', array('action' => 'index'));
                    }
                } else {
                    $this->flashMessenger()->addMessage('Login incorrecto verifique sus datos!', 'error');
                    return $this->forward()->dispatch('Admin\Controller\Login', array(
                                'action' => 'index',
                                'loginForm' => $loginForm
                    ));
                }
            } else {
                $messages = $loginForm->getMessages();
                print_r($messages);die;
                $this->flashMessenger()->addMessage('Login incorrecto verifique sus datos!', 'error');
                return $this->forward()->dispatch('Admin\Controller\Login', array(
                            'action' => 'index',
                            'loginForm' => $loginForm
                ));
            }
        }
        $view['loginForm'] = $loginForm;
        return new ViewModel($view);
    }

    public function logoutAction() {
        $LoginAdmin = $this->getService('Admin\Model\LoginAdmin');
        $LoginAdmin->logout();
        //seteamos el mesanje de registro exitoso
        $this->flashMessenger()->addMessage("Session Finalizada", 'success');

        return $this->forward()->dispatch('Admin\Controller\Login', array('action' => 'index'));
    }

    public function getService($serviceName) {
        $sm = $this->getServiceLocator();
        $service = $sm->get($serviceName);

        return $service;
    }

}

redpajama_set_name: RedPajamaGithub


text:
namespace aura {
class Event;
class Window;
namespace client {

// A property key to store the activation delegate for a window. The type of the
// value is |aura::ActivationDelegate*|.
AURA_EXPORT extern const char kActivationDelegateKey[];

// An interface implemented by an object that configures and responds to changes
// to a window's activation state.
class AURA_EXPORT ActivationDelegate {
 public:
  // Returns true if the window should be activated. |event| is either the mouse
  // event supplied if the activation is the result of a mouse, or the touch
  // event if the activation is the result of a touch, or NULL if activation is
  // attempted for another reason.
  virtual bool ShouldActivate(Event* event) = 0;

  // Sent when the window is activated.
  virtual void OnActivated() = 0;

  // Sent when the window loses active status.
  virtual void OnLostActive() = 0;

 protected:
  virtual ~ActivationDelegate() {}
};

// Sets/Gets the ActivationDelegate on the Window. No ownership changes.
AURA_EXPORT void SetActivationDelegate(Window* window,
                                       ActivationDelegate* delegate);
AURA_EXPORT ActivationDelegate* GetActivationDelegate(Window* window);

}  // namespace client
}  // namespace aura

#endif  // UI_AURA_CLIENT_ACTIVATION_DELEGATE_H_


text:
package org.apereo.portal.io.xml;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.io.IOUtils;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.springframework.core.io.Resource;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionOperations;

import com.google.common.base.Function;

/**
 * Utility for doing import/export round trip testing
 *
 * @author Eric Dalquist
 */
public final class IdentityImportExportTestUtilities {

    public static <T> void testIdentityImportExport(TransactionOperations transactionOperations,
                                                    final IDataImporter<T> dataImporter, final IDataExporter<?> dataExporter,
                                                    Resource resource, Function<T, String> getName) throws Exception {

    	final String importData = toString(resource);

    	final XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
    	final XMLEventReader xmlEventReader = xmlInputFactory.createXMLEventReader(new StringReader(importData));

        //Unmarshall from XML
        final Unmarshaller unmarshaller = dataImporter.getUnmarshaller();
        final StAXSource source = new StAXSource(xmlEventReader);
		@SuppressWarnings("unchecked")
        final T dataImport = (T)unmarshaller.unmarshal(source);

        //Make sure the data was unmarshalled
        assertNotNull("Unmarshalled import data was null", dataImport);

        //Import the data
        dataImporter.importData(dataImport);

        //Export the data
        final String name = getName.apply(dataImport);
        final Object dataExport = transactionOperations.execute(new TransactionCallback<Object>() {
            /* (non-Javadoc)
             * @see org.springframework.transaction.support.TransactionCallback#doInTransaction(org.springframework.transaction.TransactionStatus)
             */
            @Override
            public Object doInTransaction(TransactionStatus status) {
                return dataExporter.exportData(name);
            }
        });

        //Make sure the data was exported
        assertNotNull("Exported data was null", dataExport);

        //Marshall to XML
        final Marshaller marshaller = dataExporter.getMarshaller();

        final StringWriter result = new StringWriter();
        marshaller.marshal(dataExport, new StreamResult(result));

        //Compare the exported XML data with the imported XML data, they should match
        final String resultString = result.toString();
        try {
	        XMLUnit.setIgnoreWhitespace(true);
			Diff d = new Diff(new StringReader(importData), new StringReader(resultString));
	        assertTrue("Export result differs from import" + d, d.similar());
        }
        catch (Exception e) {
            throw new XmlTestException("Failed to assert similar between import XML and export XML", resultString, e);
        }
        catch (Error e) {
        	throw new XmlTestException("Failed to assert similar between import XML and export XML", resultString, e);
        }
    }

    private static String toString(Resource resource) throws IOException {
        final InputStream inputStream = resource.getInputStream();
        try {
            return IOUtils.toString(inputStream);
        }
        finally {
            IOUtils.closeQuietly(inputStream);
        }
    }
}


text:
#include "Nav2StatusListener.h"

namespace WFAPI {

Nav2StatusListener::Nav2StatusListener()
{
}

Nav2StatusListener::~Nav2StatusListener()
{
}

} // End namespace WFAPI


text:
import chai, { expect } from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';

import { compileJSX } from '../../src/compileJSX.js';

chai.use(sinonChai);

describe('Compile JSX', () => {
    it('should represent DOM elements with JS objects', () => {
        const expected = {
            type: 'ul',
            props: {class: 'list'},
            children: [
                {type: 'li', props: {}, children: ['item 1']},
                {type: 'li', props: {}, children: ['item 2']}
            ]
        };
        const actual = compileJSX(
            'ul',
            {class: 'list'},
            compileJSX('li', {}, 'item 1'),
            compileJSX('li', {}, 'item 2')
        );
        expect(expected).to.deep.equal(actual);
    });

    it('should handle empty props data provided', () => {
        const expected = {
            type: 'ul',
            props: {},
            children: [
                {type: 'li', props: {}, children: ['item 1']},
                {type: 'li', props: {}, children: ['item 2']}
            ]
        };
        const actual = compileJSX(
            'ul',
            null,
            compileJSX('li', {}, 'item 1'),
            compileJSX('li', {}, 'item 2')
        );
        expect(expected).to.deep.equal(actual);
    });
});


text:
/* Exported API */
void bioInit(void);
void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3);
unsigned long long bioPendingJobsOfType(int type);
void bioWaitPendingJobsLE(int type, unsigned long long num);
time_t bioOlderJobOfType(int type);
void bioKillThreads(void);

/* Background job opcodes */
#define REDIS_BIO_CLOSE_FILE          0 /* Deferred close(2) syscall. */
#define REDIS_BIO_AOF_FSYNC           1 /* Deferred AOF fsync. */
#define REDIS_BIO_LEVELDB_BACKUP      2 /* Deferred LEVELDB backup. */
#define REDIS_BIO_NUM_OPS             3


text:
from django.core import management
from django.test import TestCase

from tx_salaries.factories import (OrganizationFactory, PostFactory,
                                   MembershipFactory, EmployeeFactory,
                                   CompensationTypeFactory)

from tx_salaries.models import OrganizationStats, PositionStats

class DenormalizeCreatesStats(TestCase):
    def test_no_stats_without_denormalize(self):
        parent_org = OrganizationFactory(name="Test Parent")
        child_org = OrganizationFactory(name="Test Child", parent=parent_org)

        post = PostFactory(organization=child_org)

        # POST MUST HAVE UNICODE VALUE
        membership = MembershipFactory(post=post, organization=child_org)
        full_time = CompensationTypeFactory(name='FT')

        employee = EmployeeFactory(position=membership,
                                   compensation_type=full_time)

        with self.assertRaises(OrganizationStats.DoesNotExist):
            parent_org.stats
            child_org.stats

        with self.assertRaises(PositionStats.DoesNotExist):
            post.stats

    def test_denormalize_creates_stats(self):
        parent_org = OrganizationFactory(name="Test Parent")
        child_org = OrganizationFactory(name="Test Child", parent=parent_org)

        post = PostFactory(organization=child_org)

        # POST MUST HAVE UNICODE VALUE
        membership = MembershipFactory(post=post, organization=child_org)
        full_time = CompensationTypeFactory(name='FT')

        employee = EmployeeFactory(position=membership,
                                   compensation_type=full_time)

        management.call_command('denormalize_salary_data')

        self.assertEqual(parent_org.stats.total_number, 1)
        self.assertEqual(child_org.stats.total_number, 1)
        self.assertEqual(post.stats.total_number, 1)


text:
<?php namespace Devtools;

class PDOAdapter
{
    public static function getConnectionString($params)
    {
        switch($params['type']) {
        case 'mysql':
            return "{$params['type']}:host={$params['host']};dbname={$params['db']}";
        case 'firebird':
            return "{$params['type']}:dbname={$params['host']}:{$params['db']}";
        case 'dblib':
            return "{$params['type']}:host={$params['host']};{$params['db']}";
        };
    }

    public static function connect($parameters)
    {
        self::validateParameters($parameters);

        return new \PDO(
            self::getConnectionString($parameters),
            $parameters['username'],
            $parameters['password']
        );
    }

    private static function validateParameters($parameters)
    {
        $keys = array_keys($parameters);
        if (!in_array('type',  $keys)
            || !in_array('host', $keys)
            || !in_array('db', $keys)
            || !in_array('username', $keys)
            || !in_array('password', $keys)
        ) {
            throw new \Exception('Invalid connection options.');
        }
    }

    private static function getConnectionStringByType($type)
    {
        switch($type) {
        case 'mysql':
            return '%s:host=%s;dbname=%s';
            break;
        case 'firebird':
            return '%s:dbname=%s:%s';
            break;
        case 'dblib':
            return '%s:host=%s;%s';
            break;
        }
    }
}


text:
using GitVersion.Extensions;
using GitVersion.Helpers;
using LibGit2Sharp;

namespace GitVersion
{
    internal sealed class Reference : IReference
    {
        private static readonly LambdaEqualityHelper<IReference> equalityHelper = new(x => x.Name.Canonical);
        private static readonly LambdaKeyComparer<IReference, string> comparerHelper = new(x => x.Name.Canonical);
        internal readonly LibGit2Sharp.Reference innerReference;

        internal Reference(LibGit2Sharp.Reference reference)
        {
            this.innerReference = reference.NotNull();
            Name = new ReferenceName(reference.CanonicalName);

            if (reference is DirectReference)
                ReferenceTargetId = new ObjectId(reference.TargetIdentifier);
        }

        public ReferenceName Name { get; }
        public IObjectId? ReferenceTargetId { get; }
        public int CompareTo(IReference other) => comparerHelper.Compare(this, other);
        public override bool Equals(object obj) => Equals(obj as IReference);
        public bool Equals(IReference? other) => equalityHelper.Equals(this, other);
        public override int GetHashCode() => equalityHelper.GetHashCode(this);
        public override string ToString() => Name.ToString();
        public string TargetIdentifier => this.innerReference.TargetIdentifier;
        public static implicit operator LibGit2Sharp.Reference(Reference d)
            => d.NotNull().innerReference;
    }
}


text:
<manifest package="uncmn.commons">

    <application />

</manifest>


text:
//
//  DSAlertsQueue.h
//  DSLib
//
//  Created by Alex on 23/10/2013.
//  Copyright (c) 2013 DS ltd. All rights reserved.
//

@import Foundation;

@class DSAlertsHandler;
@class DSMessage;
@class DSAlert;

@interface DSAlertsQueue : NSObject

/** Interval between which queue will commit all objects added. Default is 2s */
@property (nonatomic, assign) NSTimeInterval despatchInterval;

- (void)addMessage:(nullable DSMessage *)message;
- (void)addAlert:(nullable DSAlert *)alert modal:(BOOL)isModal;
- (void)addError:(nullable NSError *)error;
- (void)addParseError:(nullable NSError *)error;

- (void)commit;

@end


text:
+++
date = "2017-01-05T10:08:31+11:00"
title = "Graph Data Structures in Golang"

+++

*... understand just how astonishingly commonplace (and important) graph problems are*
*they should be part of every working programmer's toolkit.*
    - [Stevey](http://steve-yegge.blogspot.com.au/2008/03/get-that-job-at-google.html)

The graph data structure is of high utility across the field of computer
science. Graph problems come in many shapes and sizes but once modeled can
typically be represented by a limited number of graph data type variants.

<!--more-->

Writing a general purpose graph library is no mean feat. This post is not about creating a
general purpose graph library, if you are after such a library there is a nice
general purpose graph library written in Go by the
[gonum](https://github.com/gonum/graph) team.

This post will lay the foundation for further exploration of graph algorithms in
Go. As such we will discuss the basic structure of various graphs. These details
will allow us to further build on these structures when faced with a particular
problem or when attempting to implement a particular well known graph algorithm.

## Unweighted Graph

One of the most simple graphs is an unweighted graph with `N` nodes labelled
`0..N-1`.

```
type Graph struct {
	NumNodes int
	Edges    [][]int
}

// NewGraph: Create graph with n nodes.
func NewGraph(n int) *Graph {
	return &Graph{
		NumNodes: n,
		Edges:    make([][]int, n),
	}
}
```

Since the nodes are sequentially numbered we can store all edges in a slice
indexed by the source node. For an edge from `u` to `v` (`u -> v`) we append `v`
to the slice at `Edges[u]`. We then have a slice of slices representing all
edges in the graph, with each slice `Edges[n]` representing all nodes adjacent
to node labelled `n`.

The graph thus far is *undirected*, an edge `u -> v` does not imply an edge `v
-> u`. If the problem we are modelling requires an undirected graph it is simply a
matter of adding an edge `v -> u` any time we add edge `u -> v`.

```
// AddEdge: Add an edge from u to v.
func (g *Graph) AddEdge(u, v int) {
	g.Edges[u] = append(g.Edges[u], v)

	// For undirected graph add edge from v to u.
	// g.Edges[v] = append(g.Edges[v], u)
}
```

We have the graph data structure implemented and are able to build a graph to
model the problem at hand. Depending on the problem we will need to access the
graph data in various ways. One common operation used, among other things, in
graph traversal is to iterate all nodes adjacent to node `n`. This is easily
achieved with the current implementation by iterating the slice at
`Edges[n]`. To process all edges in the graph one simply uses a nested loop to
iterate over `Edges`.

```
func (g *Graph) adjacentEdgesExample() {
	u := 0 // Example node label.

	fmt.Printf("Printing all edges adjacent to Node: %d\n", u)
	for _, v := range g.Edges[u] {
		// Edge exists from u to v.
		fmt.Printf("Edge: %d -> %d\n", u, v)
	}

	fmt.Println("Printing all edges in graph.")
	for u, adjacent := range g.Edges { // Nodes are labelled 0 to N-1.
		for _, v := range adjacent {
			// Edge exists from u to v.
			fmt.Printf("Edge: %d -> %d\n", u, v)
		}
	}
}
```

## Weighted Graph

The next level of complexity that we may wish to add to a graph is edge
weight. This can be achieved by defining an `Edge` type then storing all `Edge`
instances in a slice in a similar fashion to the unweighted graph above. We
choose to store the `From` node label in the `Edge` type even though it is
redundant (since `Edges` is inedexed by source node label). This often leads to
code that is easier to write and read. There is obviously a space cost to this
design choice.

```
type Graph struct {
	NumNodes int
	Edges    [][]Edge
}

type Edge struct {
	From   int
	To     int
	Weight int
}

// NewGraph: Create graph with n nodes.
func NewGraph(n int) *Graph {
	return &Graph{
		NumNodes: n,
		Edges:    make([][]Edge, n),
	}
}

```

Edges are added to the graph thus

```
// AddEdge: Add an edge from u to v.
func (g *Graph) AddEdge(u, v, w int) {
	g.Edges[u] = append(g.Edges[u], Edge{From: u, To: v, Weight: w})

	// For undirected graph add edge from v to u.
	// g.Edges[v] = append(g.Edges[v], Edge{From: v, To: u, Weight: w})
}
```

And edges may be processed simply using slice iteration.
```
func (g *Graph) adjacentEdgesExample() {
	u := 0 // Example node label.

	fmt.Printf("Printing all edges adjacent to %d\n", u)
	for _, e := range g.Edges[u] {
		fmt.Printf("Edge: %d -> %d (%d)\n", e.From, e.To, e.Weight)
	}

	fmt.Println("Printing all edges in graph.")
	for _, adjacent := range g.Edges {
		for _, e := range adjacent {
			fmt.Printf("Edge: %d -> %d (%d)\n", e.From, e.To, e.Weight)
		}
	}
}
```

## Arbitrary Node Labels

Graph nodes will not always be labelled sequentially. If we require a graph with
arbitrary graph labels we will likely use a `map` instead of a slice to store
edges. This provides flexibility at the cost of efficiency. Although map access
is said to be have constant time cost, I have found that on large data sets use
of maps is noticeably slower than use of slices. If efficiency is critical
you may like to consider remodelling your problem to use sequential node labels
if at all possible. The code using maps is similar to the code above, here we
will show an unweighted graph. Edge weights may be added as they were for
sequential node labels above.

**Herein we use a type definition to abstract away the node label type.** Also we
now refer interchangeably to node label or node id.

```
type ID int

type Graph struct {
	Nodes map[ID]struct{}
	Edges map[ID]map[ID]struct{}
}

// NewGraph: Create graph.
func NewGraph() *Graph {
	return &Graph{
		Nodes: make(map[ID]struct{}),
		Edges: make(map[ID]map[ID]struct{}),
	}
}

// AddNode: Add node id to graph, return true if added (ID's are unique).
func (g *Graph) AddNode(id ID) bool {
	if _, ok := g.Nodes[id]; ok {
		return false
	}
	g.Nodes[id] = struct{}{}
	return true
}
```

Here we add the constraint that node labels are unique. We use the Go idiom of
mapping an integer (node label) to an empty object `struct{}{}`. While making the
code longer this makes explicit our intention that only the map key is of utility.

Edges may be added thus.

```
// AddEdge: Add an edge from u to v.
func (g *Graph) AddEdge(u, v ID) {
	if _, ok := g.Nodes[u]; !ok {
		g.AddNode(u)
	}
	if _, ok := g.Nodes[v]; !ok {
		g.AddNode(v)
	}

	if _, ok := g.Edges[u]; !ok {
		g.Edges[u] = make(map[ID]struct{})
	}
	g.Edges[u][v] = struct{}{}

	// For undirected graph add edge from v to u.
	// if _, ok := g.Edges[v]; !ok {
	// 	g.Edges[v] = make(map[ID]struct{})
	// }
	// g.Edges[v][u] = struct{}{}
}
```

Prior to adding an edge we access the inner map to verify that it is available
and create it if not. Once again we use an empty struct to make explicit that
the key value is the only value of utility. An undirected graph is created by
adding an edge in the reverse direction as previously discussed.

Edge processing proceeds accordingly.

```
func (g *Graph) adjacentEdgesExample() {
	u := ID(0) // example node ID.

	fmt.Printf("Printing all edges adjacent to %d\n", u)
	for v := range g.Edges[u] {
		// Edge exists from u to v.
		fmt.Printf("Edge: %d -> %d\n", u, v)
	}

	fmt.Println("Printing all edges.")
	for u, m := range g.Edges {
		for v := range m {
			// Edge exists from u to v.
			fmt.Printf("Edge: %d -> %d\n", u, v)
		}
	}
}
```

Experienced Go programmers may pause at the use of a single variable `v` above when
iterating a `map`. Note that this refers to edge `v` not the usual idiom `k, v :=
range m`.

## Conclusion

Graphs are an essential data structure. Implementing them in Go is both
enjoyable and educational. We have presented a simple method of implementing
graphs here but with just this much work we have a data structure which allows us to
do traversal, path finding, minimum spanning tree and much more.

---
#### Bibliography:
[Ski08] - **The Algorithm Design Manual**, Steven S. Skiena
[CLRS09] - **Introduction to Algorithms**, Thomas H.Cormen, Charles E. Leiserson,
Ronald L. Rivest, Clifford Stein


text:
@implementation Message

- (void)loadFromDictionary:(NSDictionary *)dictionary
{
    self.objId = dictionary[[Message identifierString]];
    self.url = dictionary[@"url"];
    self.receiptsUrl = dictionary[@"receipts_url"];
    self.messageText = [dictionary[@"parts"] firstObject][@"body"];

    NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
    [dateFormat setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
    NSDate *date = [dateFormat dateFromString:dictionary[@"sent_at"]];
    self.sentAt = date;
//    CHANGE
//    self.conversation = [Conversation findOrCreateEntity:dictionary[@"conversation"][@"id"] inContext:nil];
}

//{
//    "parts": [
//              {
//                  "body": "Hello, World!",
//                  "mime_type": "text/plain"
//              },
//              {
//                  "body": "YW55IGNhcm5hbCBwbGVhc3VyZQ==",
//                  "mime_type": "image/jpeg",
//                  "encoding": "base64"
//              }
//              ],
//    "notification": {
//        "text": "This is the alert text to include with the Push Notification.",
//        "sound": "chime.aiff"
//    }
//}
- (NSDictionary *)serializedCreateRequestDictionary {
    NSDictionary *data = @{@"parts":@[@{@"body":self.messageText,
                                        @"mime_type":@"text/plain"}]
                           };
    NSDictionary *body = @{ @"method": @"Message.create",
                            @"request_id": [NSString stringWithFormat:@"message.create.%@", [NSDate date]],
                            @"object_id": self.conversation.objId,
                            @"data": data};
    NSDictionary *parameters = @{ @"type": @"request", @"body": body};
    return parameters;
    return nil;
}

- (void)deserializedCreateRequestDictionary:(NSDictionary *)dictionary {

}

+ (id<MTJSyncedEntity>)findOrCreateEntity:(NSString *)objID inContext:(NSManagedObjectContext *)context
{
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[self entityName]];
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"objId = %@", objID];
    NSError *error = nil;
    NSArray *result = [context executeFetchRequest:fetchRequest error:&error];
    if (error) {
        NSLog(@"error: %@", error.localizedDescription);
    }
    if (result.lastObject) {
        return result.lastObject;
    } else {
        Message *msg = [self insertNewObjectIntoContext:context];
        msg.objId = objID;
        return msg;
    }
}

+ (id)entityName
{
    return NSStringFromClass(self);
}

+ (NSString *)sortKey {
    return @"sentAt";
}

- (void)addCollection:(NSSet<NSManagedObject *> *)values {
//    nothing here yet
    abort();
}

+ (instancetype)insertNewObjectIntoContext:(NSManagedObjectContext*)context
{
    return [NSEntityDescription insertNewObjectForEntityForName:[self entityName] inManagedObjectContext:context];
}

//name of the field to identify the object in core data
+ (NSString *)identifierString {
    return @"id";
}

- (NSString *)endpointURL {
    return @"messages";
}

+ (NSString *)collectionEndpointURL {
//    messages are associated with a conversation
    abort();
    return @"";
}

- (NSString *)collectionWithRelationshipEndpointURL {
    //    nothing yet
    abort();
    return @"";

}

- (NSString *)description {
    return [NSString stringWithFormat:@" id: %@\n url: %@\n messageText:%@\n",
            self.objId, self.url, self.messageText];
}
@end


text:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectCodeStyleSettingsManager">
    <option name="PER_PROJECT_SETTINGS">
      <value>
        <Objective-C-extensions>
          <option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK" />
          <option name="RELEASE_STYLE" value="IVAR" />
          <file>
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="Import" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="Macro" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="Typedef" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="Struct" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="Enum" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="ClassPredef" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="Constant" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="Global" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="FunctionPredecl" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="Function" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="Class" />
          </file>
          <class>
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="Property" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="Synthesize" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="InitMethod" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="StaticMethod" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="InstanceMethod" />
            <option name="com.jetbrains.objc.util.OCDeclarationKind" value="DeallocMethod" />
          </class>
        </Objective-C-extensions>
        <XML>
          <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
        </XML>
      </value>
    </option>
    <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
  </component>
</project>


text:
package com.palantir.atlasdb.metrics;

import static org.assertj.core.api.Assertions.assertThat;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.palantir.common.streams.KeyedStream;
import com.palantir.tritium.metrics.registry.MetricName;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.junit.Test;

public class MetricPublicationArbiterTest {
    private static final MetricName METRIC_NAME_1 = MetricName.builder()
            .safeName("com.palantir.atlasdb.metrics.metrics.p99")
            .build();
    private static final MetricName METRIC_NAME_2 = MetricName.builder()
            .safeName("com.palantir.atlasdb.metrics.executor.count")
            .safeTags(ImmutableMap.of("tag", "value"))
            .build();

    @Test
    public void metricsWithNoFilterAreAccepted() {
        MetricPublicationArbiter arbiter = new MetricPublicationArbiter(ImmutableMap.of());
        assertThat(arbiter.test(METRIC_NAME_1)).isTrue();
        assertThat(arbiter.test(METRIC_NAME_2)).isTrue();
    }

    @Test
    public void metricsWithOneFilterAreFiltered() {
        MetricPublicationArbiter arbiter = createArbiter(ImmutableMap.of(
                METRIC_NAME_1, ImmutableSet.of(falseFilter()),
                METRIC_NAME_2, ImmutableSet.of(trueFilter())));
        assertThat(arbiter.test(METRIC_NAME_1)).isFalse();
        assertThat(arbiter.test(METRIC_NAME_2)).isTrue();
    }

    @Test
    @SuppressWarnings("DistinctVarargsChecker")
    public void metricsWithMultipleFiltersAreAcceptedOnlyIfAllFiltersPermit() {
        MetricPublicationArbiter arbiter = createArbiter(ImmutableMap.of(
                METRIC_NAME_1, ImmutableSet.of(trueFilter(), falseFilter(), trueFilter()),
                METRIC_NAME_2, ImmutableSet.of(trueFilter(), trueFilter(), trueFilter())));
        assertThat(arbiter.test(METRIC_NAME_1)).isFalse();
        assertThat(arbiter.test(METRIC_NAME_2)).isTrue();
    }

    @Test
    public void canAddFilters() {
        MetricPublicationArbiter arbiter = new MetricPublicationArbiter(new ConcurrentHashMap<>());
        assertThat(arbiter.test(METRIC_NAME_1)).isTrue();

        arbiter.registerMetricsFilter(METRIC_NAME_1, falseFilter());
        assertThat(arbiter.test(METRIC_NAME_1)).isFalse();
    }

    @Test
    public void exceptionTreatedAsNotFiltered() {
        MetricPublicationArbiter arbiter =
                createArbiter(ImmutableMap.of(METRIC_NAME_1, ImmutableSet.of(throwingFilter())));
        assertThat(arbiter.test(METRIC_NAME_1)).isTrue();
    }

    @Test
    public void rejectsMetricWithDefinitivelyFalseFilterEvenWithExceptions() {
        MetricPublicationArbiter arbiter = createArbiter(
                ImmutableMap.of(METRIC_NAME_1, ImmutableSet.of(throwingFilter(), trueFilter(), falseFilter())));
        assertThat(arbiter.test(METRIC_NAME_1)).isFalse();
    }

    @Test
    public void deduplicatesFilters() {
        Map<MetricName, Set<MetricPublicationArbiter.DeduplicatingFilterHolder>> filters = new HashMap<>();
        MetricPublicationArbiter arbiter = new MetricPublicationArbiter(filters);
        for (int i = 0; i < 100; i++) {
            int index = i;
            // not using pre-defined filters because otherwise Object.equals would work without effort
            arbiter.registerMetricsFilter(METRIC_NAME_1, () -> index == 1);
            arbiter.registerMetricsFilter(METRIC_NAME_1, () -> index == 2);
        }
        assertThat(filters.get(METRIC_NAME_1)).hasSize(2);
    }

    private static MetricPublicationFilter trueFilter() {
        // Depending on the JDK, identical lambdas may share the same hashcode
        // As a result, we cannot use this method to create a set of filters
        return new MetricPublicationFilter() {
            @Override
            public boolean shouldPublish() {
                return true;
            }
        };
    }

    private static MetricPublicationFilter falseFilter() {
        return () -> false;
    }

    private static MetricPublicationFilter throwingFilter() {
        return () -> {
            throw new RuntimeException("boo");
        };
    }

    private static MetricPublicationArbiter createArbiter(Map<MetricName, Set<MetricPublicationFilter>> filters) {
        return new MetricPublicationArbiter(KeyedStream.stream(filters)
                .map(filtersForMetric -> filtersForMetric.stream()
                        .<MetricPublicationArbiter.DeduplicatingFilterHolder>map(ImmutableDeduplicatingFilterHolder::of)
                        .collect(Collectors.toSet()))
                .collectToMap());
    }
}